From 0519aa338fc1899bfb1035210f20ac229840c0bd Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:19:49 +0100 Subject: [PATCH 1/2] complete new core applied --- .github/workflows/build-macos.yml | 279 +- .github/workflows/build-ubuntu-20.yml | 103 - .../workflows/build-ubuntu-22-capnproto.yml | 81 - .github/workflows/build-ubuntu-22.yml | 70 - .github/workflows/build-ubuntu.yml | 289 ++ .github/workflows/build-windows.yml | 352 ++- .github/workflows/run-clang-tidy.yml | 72 - .gitignore | 64 +- .gitmodules | 39 +- CMakeLists.txt | 247 +- CONTRIBUTING.md | 11 +- DEPENDENCIES.txt | 29 + NOTICE.txt | 83 + README.md | 108 +- build/linux/01_build_default.sh | 12 + build/linux/02_build_full.sh | 32 + build/linux/03_build_pubsub.sh | 32 + build/linux/04_build_pubsub_proto.sh | 32 + build/linux/05_build_pubsub_udp_only.sh | 32 + build/linux/06_build_pubsub_tcp_only.sh | 32 + build/linux/07_build_pubsub_shm_only.sh | 32 + build/linux/08_build_clientserver.sh | 32 + build/linux/09_build_clientserver_proto.sh | 32 + build/linux/10_build_monitoring_only.sh | 31 + build/windows/01_build_default.bat | 12 + build/windows/02_build_full.bat | 31 + build/windows/03_build_pubsub.bat | 31 + build/windows/04_build_pubsub_proto.bat | 31 + build/windows/05_build_pubsub_udp_only.bat | 31 + build/windows/06_build_pubsub_tcp_only.bat | 31 + build/windows/07_build_pubsub_shm_only.bat | 31 + build/windows/08_build_clientserver.bat | 31 + build/windows/09_build_clientserver_proto.bat | 31 + build/windows/10_build_monitoring_only.bat | 31 + build/windows/Readme.md | 36 - build/windows/download_npcap.ps1 | 9 - build/windows/win_make_all.bat | 10 - build/windows/win_make_build.bat | 10 - build/windows/win_make_cmake.bat | 22 - build/windows/win_make_setup.bat | 13 - build/windows/win_run_tests.bat | 11 - build/windows/win_set_vars.bat | 14 - cmake/Modules/Findasio.cmake | 5 - cmake/Modules/Findsimpleini.cmake | 5 - cmake/Modules/Findtclap.cmake | 5 - cmake/Modules/eCALConfig.cmake | 1 + cmake/helper_functions/app.desktop.in | 7 + .../helper_functions/ecal_add_functions.cmake | 57 + .../ecal_install_functions.cmake | 11 + cpack/cpack_variables.cmake | 26 +- cpack/ecal_path.xml | 7 + cpack/innosetup.cmake | 2 - cpack/innosetup/ecal_setup.iss.in | 88 +- cpack/innosetup/gfx/logo.svg | 189 ++ cpack/innosetup/gfx/logo_110_110.bmp | Bin 0 -> 36574 bytes cpack/innosetup/gfx/logo_55_55.bmp | Bin 0 -> 9294 bytes cpack/innosetup/gfx/logo_icon.bmp | Bin 40490 -> 0 bytes cpack/innosetup/gfx/logo_icon.ico | Bin 55742 -> 0 bytes cpack/innosetup/gfx/logo_icon.xcf | Bin 36077 -> 0 bytes ecal/core/CMakeLists.txt | 586 ---- ecal/core/cfg/ecal.ini | 194 -- .../include/ecal/cimpl/ecal_event_cimpl.h | 84 - .../ecal_proto_dyn_json_subscriber_cimpl.h | 92 - .../include/ecal/cimpl/ecal_publisher_cimpl.h | 297 -- ecal/core/include/ecal/cimpl/ecal_qos_cimpl.h | 66 - ecal/core/include/ecal/ecal_clang.h | 685 ----- ecal/core/include/ecal/ecal_publisher.h | 456 --- ecal/core/include/ecal/ecal_qos.h | 83 - ecal/core/include/ecal/ecal_service.h | 35 - ecal/core/include/ecal/ecal_timed_cb.h | 134 - ecal/core/include/ecal/ecalc_types.h | 21 - .../core/include/ecal/msg/capnproto/dynamic.h | 202 -- ecal/core/include/ecal/msg/capnproto/helper.h | 120 - .../include/ecal/msg/capnproto/publisher.h | 263 -- .../include/ecal/msg/capnproto/subscriber.h | 290 -- .../include/ecal/msg/flatbuffers/publisher.h | 144 - .../include/ecal/msg/flatbuffers/subscriber.h | 131 - .../include/ecal/msg/messagepack/publisher.h | 154 - .../include/ecal/msg/messagepack/subscriber.h | 138 - .../msg/protobuf/dynamic_json_subscriber.h | 111 - .../include/ecal/msg/protobuf/publisher.h | 169 -- ecal/core/src/_code.cppcheck | 14 - ecal/core/src/ecal_clang.cpp | 935 ------ ecal/core/src/ecal_def_ini.h | 131 - ecal/core/src/ecal_defs.h.in | 17 - ecal/core/src/ecal_descgate.cpp | 265 -- ecal/core/src/ecal_registration_provider.cpp | 450 --- ecal/core/src/ecal_registration_provider.h | 118 - ecal/core/src/ecal_registration_receiver.cpp | 361 --- ecal/core/src/ecal_registration_receiver.h | 129 - ecal/core/src/ecal_thread.cpp | 119 - ecal/core/src/ecal_thread.h | 68 - ecal/core/src/ecal_time.cpp | 104 - ecal/core/src/ecal_util.cpp | 390 --- ecal/core/src/ecalc.cpp | 1386 --------- ecal/core/src/io/ecal_receiver.h | 97 - ecal/core/src/io/ecal_sender.h | 85 - .../src/io/linux/ecal_socket_option_linux.h | 85 - ecal/core/src/io/rcv_sample.cpp | 460 --- ecal/core/src/io/rcv_sample.h | 116 - ecal/core/src/io/snd_raw_buffer.cpp | 187 -- ecal/core/src/io/snd_sample.cpp | 63 - ecal/core/src/io/udp_configurations.cpp | 36 - ecal/core/src/io/udp_init.cpp | 90 - ecal/core/src/io/udp_receiver.cpp | 97 - ecal/core/src/io/udp_receiver_asio.cpp | 229 -- ecal/core/src/io/udp_receiver_base.h | 46 - ecal/core/src/io/udp_receiver_npcap.cpp | 115 - ecal/core/src/io/udp_sender.cpp | 191 -- ecal/core/src/mon/ecal_monitoring_impl.cpp | 819 ------ ecal/core/src/mon/ecal_monitoring_impl.h | 303 -- ecal/core/src/mon/ecal_monitoring_threads.cpp | 161 - ecal/core/src/mon/ecal_monitoring_threads.h | 100 - .../src/pubsub/ecal_proto_dyn_json_sub.cpp | 241 -- ecal/core/src/pubsub/ecal_pubgate.cpp | 210 -- ecal/core/src/pubsub/ecal_publisher.cpp | 462 --- ecal/core/src/pubsub/ecal_subgate.cpp | 347 --- .../src/readwrite/ecal_reader_iceoryx.cpp | 132 - ecal/core/src/readwrite/ecal_reader_iceoryx.h | 77 - ecal/core/src/readwrite/ecal_reader_shm.cpp | 110 - .../core/src/readwrite/ecal_reader_udp_mc.cpp | 105 - ecal/core/src/readwrite/ecal_writer.cpp | 1171 -------- ecal/core/src/readwrite/ecal_writer.h | 185 -- .../src/readwrite/ecal_writer_iceoryx.cpp | 125 - .../core/src/readwrite/ecal_writer_inproc.cpp | 96 - ecal/core/src/readwrite/ecal_writer_shm.cpp | 223 -- .../core/src/readwrite/ecal_writer_udp_mc.cpp | 145 - ecal/core/src/readwrite/ecal_writer_udp_mc.h | 64 - ecal/core/src/service/asio_server.h | 276 -- .../src/service/ecal_service_client_impl.cpp | 628 ---- .../src/service/ecal_service_server_impl.cpp | 398 --- ecal/core/src/service/ecal_tcpclient.cpp | 367 --- ecal/core/src/service/ecal_tcpclient.h | 98 - ecal/core/src/service/ecal_tcpheader.h | 33 - ecal/core/src/service/ecal_tcpserver.cpp | 91 - ecal/core/src/service/ecal_tcpserver.h | 70 - ecal/core/src/sys_usage.cpp | 198 -- ecal/core_pb/CMakeLists.txt | 77 - ecal/core_pb/src/core_pb.cpp | 1 - ecal/core_pb/src/ecal/core/pb/topic.proto | 72 - .../include/ecal/protobuf/ecal_proto_dyn.h | 177 -- .../ecal/protobuf/ecal_proto_message_filter.h | 96 - .../ecal/protobuf/ecal_proto_visitor.h | 156 - lib/ecal_protobuf/src/ecal_proto_decoder.cpp | 316 -- lib/ecal_protobuf/src/ecal_proto_dyn.cpp | 284 -- .../ecal_proto_maximum_array_dimensions.cpp | 52 - .../src/ecal_proto_message_filter.cpp | 185 -- lib/ecal_protobuf/src/ecal_proto_visitor.cpp | 0 licenses/capnproto/LICENSE | 24 - licenses/google-flatbuffers/LICENSE.txt | 202 -- logos/ecal-cop-image.png | Bin 0 -> 314250 bytes samples/CMakeLists.txt | 154 +- .../string}/minimal_rec/CMakeLists.txt | 2 +- .../string}/minimal_rec/src/minimal_rec.c | 2 +- .../string}/minimal_rec_cb/CMakeLists.txt | 2 +- .../minimal_rec_cb/src/minimal_rec_cb.c | 2 +- .../string}/minimal_snd/CMakeLists.txt | 2 +- .../string}/minimal_snd/src/minimal_snd.c | 2 +- .../services/minimal_client_c/CMakeLists.txt | 42 - .../minimal_client_c/src/minimal_client_c.c | 77 - .../services/minimal_server_c/CMakeLists.txt | 42 - .../minimal_server_c/src/minimal_server_c.c | 77 - .../counter_rec}/CMakeLists.txt | 10 +- .../counter_rec/src/counter_rec.cpp} | 53 +- .../counter_snd/CMakeLists.txt | 2 +- .../counter_snd/src/counter_snd.cpp | 29 +- .../datarate_rec/CMakeLists.txt | 2 +- .../datarate_rec/src/datarate_rec.cpp | 20 +- .../datarate_snd/CMakeLists.txt | 2 +- .../datarate_snd/src/datarate_snd.cpp | 34 +- .../latency_rec/CMakeLists.txt | 2 +- .../latency_rec/src/latency_log.cpp | 36 +- .../latency_rec/src/latency_log.h | 0 .../latency_rec/src/latency_rec.cpp | 9 +- .../latency_snd/CMakeLists.txt | 2 +- .../latency_snd/src/binary_payload_writer.h} | 62 +- .../latency_snd/src/latency_snd.cpp | 33 +- .../many_connections/CMakeLists.txt | 45 - .../many_connections_rec}/CMakeLists.txt | 10 +- .../src}/many_connections_rec.cpp | 14 +- .../many_connections_snd/CMakeLists.txt | 39 + .../src}/many_connections_snd.cpp | 16 +- .../benchmarks/multiple_rec/CMakeLists.txt | 41 + .../multiple_rec/src/multiple_rec.cpp} | 9 +- .../benchmarks/multiple_snd/CMakeLists.txt | 41 + .../multiple_snd/src/multiple_snd.cpp | 54 +- .../performance_rec/CMakeLists.txt | 2 +- .../performance_rec/src/performance_rec.cpp} | 73 +- .../performance_snd/CMakeLists.txt | 2 +- .../src/binary_payload_writer.h | 55 + .../performance_snd/src/performance_snd.cpp | 101 + .../perftool}/CMakeLists.txt | 41 +- samples/cpp/benchmarks/perftool/Readme.md | 52 + samples/cpp/benchmarks/perftool/src/main.cpp | 249 ++ .../cpp/benchmarks/perftool/src/publisher.cpp | 180 ++ .../cpp/benchmarks/perftool/src/publisher.h | 85 + .../perftool/src/publisher_statistics.h | 129 + .../benchmarks/perftool/src/subscriber.cpp | 153 + .../cpp/benchmarks/perftool/src/subscriber.h | 91 + .../perftool/src/subscriber_statistics.h | 113 + .../cpp/capnp/addressbook_rec/CMakeLists.txt | 41 - .../addressbook_rec/src/addressbook.capnp | 56 - .../addressbook_rec/src/addressbook_rec.cpp | 101 - .../capnp/addressbook_rec_cb/CMakeLists.txt | 41 - .../addressbook_rec_cb/src/addressbook.capnp | 56 - .../src/addressbook_rec_cb.cpp | 109 - .../addressbook_rec_dynamic/CMakeLists.txt | 37 - .../src/addressbook_rec_dynamic.cpp | 130 - .../cpp/capnp/addressbook_snd/CMakeLists.txt | 41 - .../addressbook_snd/src/addressbook.capnp | 59 - .../addressbook_snd/src/addressbook_snd.cpp | 108 - samples/cpp/event/event_rec/src/event_rec.cpp | 65 - samples/cpp/event/event_snd/CMakeLists.txt | 39 - samples/cpp/event/event_snd/src/event_snd.cpp | 64 - .../monster_rec/src/flatbuffers/monster.fbs | 27 - .../monster_rec/src/monster_rec.cpp | 99 - .../monster_snd/src/flatbuffers/monster.fbs | 27 - .../monster_snd/src/monster_snd.cpp | 112 - samples/cpp/misc/process/CMakeLists.txt | 43 - samples/cpp/misc/process/src/process.cpp | 58 - samples/cpp/misc/time/src/time.cpp | 46 - .../monitoring_get_services/CMakeLists.txt | 41 - .../src/monitoring_get_services.cpp | 76 - .../monitoring_get_topics/CMakeLists.txt | 41 - .../src/monitoring_get_topics.cpp | 75 - .../monitoring/monitoring_rec/CMakeLists.txt | 17 +- .../monitoring_rec/src/monitoring_rec.cpp | 48 +- .../monitoring_rec/src/protobuf}/host.proto | 0 .../monitoring_rec/src/protobuf}/layer.proto | 13 - .../src/protobuf/monitoring.proto} | 28 +- .../src/protobuf}/process.proto | 10 +- .../src/protobuf}/service.proto | 9 +- .../monitoring_rec/src/protobuf/topic.proto | 61 + .../monitoring/monitoring_reg/CMakeLists.txt | 17 +- .../monitoring_reg/src/monitoring_reg.cpp | 5 +- .../monitoring_reg/src/protobuf}/ecal.proto | 35 +- .../monitoring_reg/src/protobuf/host.proto} | 13 +- .../monitoring_reg/src/protobuf/layer.proto | 64 + .../monitoring_reg/src/protobuf/process.proto | 74 + .../monitoring_reg/src/protobuf/service.proto | 94 + .../monitoring_reg/src/protobuf/topic.proto | 61 + .../msgpack/address_rec/src/address_rec.cpp | 81 - .../msgpack/address_snd/src/address_snd.cpp | 85 - .../multiple/multiple_rec_cb/CMakeLists.txt | 60 - .../cpp/multiple/multiple_rec_cb/src/main.cpp | 42 - .../multiple_rec_cb/src/multiple_rec_cb.h | 22 - .../src/multiple_rec_cb_person.cpp | 81 - .../src/multiple_rec_cb_person.h | 22 - .../src/multiple_rec_cb_vector.cpp | 77 - .../src/multiple_rec_cb_vector.h | 22 - .../cpp/multiple/multiple_snd/CMakeLists.txt | 59 - .../cpp/multiple/multiple_snd/src/main.cpp | 43 - .../multiple/multiple_snd/src/multiple_snd.h | 22 - .../multiple_snd/src/multiple_snd_person.cpp | 93 - .../multiple_snd/src/multiple_snd_person.h | 22 - .../multiple_snd/src/multiple_snd_vector.cpp | 80 - .../multiple_snd/src/multiple_snd_vector.h | 22 - .../orchestration/component1/CMakeLists.txt | 48 - .../component1/src/component1.cpp | 92 - .../component1/src/protobuf/component.proto | 34 - .../src/protobuf/orchestrator.proto | 35 - .../orchestration/component2/CMakeLists.txt | 48 - .../component2/src/component2.cpp | 113 - .../component2/src/protobuf/component.proto | 34 - .../src/protobuf/orchestrator.proto | 35 - .../orchestration/orchestrator/CMakeLists.txt | 47 - .../orchestrator/src/orchestrator.cpp | 61 - .../src/protobuf/orchestrator.proto | 35 - .../performance/dynsize_snd/CMakeLists.txt | 39 - .../dynsize_snd/src/dynsize_snd.cpp | 100 - .../performance_rec/src/performance_rec.cpp | 87 - .../performance_rec_cb/CMakeLists.txt | 39 - .../performance_snd/src/performance_snd.cpp | 139 - .../pubsub_throughput/CMakeLists.txt | 39 - .../src/pubsub_throughput.cpp | 214 -- .../person_rec_lambda_in_class/CMakeLists.txt | 51 - .../include/addressbook.h | 46 - .../src/addressbook.cpp | 89 - .../person_snd_dyn/src/person_snd_dyn.cpp | 99 - .../person_snd_dyn/src/protobuf/person.proto | 42 - .../person/person_snd_events/CMakeLists.txt | 49 - .../src/protobuf/animal.proto | 28 - .../src/protobuf/house.proto | 27 - .../person/person_snd_inproc/CMakeLists.txt | 49 - .../src/person_snd_inproc.cpp | 106 - .../src/protobuf/animal.proto | 28 - .../src/protobuf/house.proto | 27 - .../person_snd_multicast/CMakeLists.txt | 50 - .../src/protobuf/animal.proto | 28 - .../src/protobuf/house.proto | 27 - .../src/protobuf/person.proto | 42 - .../person_snd_tcp/src/person_snd_tcp.cpp | 79 - .../person_snd_tcp/src/protobuf/animal.proto | 28 - .../person_snd_tcp/src/protobuf/house.proto | 27 - .../person_snd_tcp/src/protobuf/person.proto | 42 - .../binary/binary_rec}/CMakeLists.txt | 10 +- .../binary/binary_rec/src/binary_rec.cpp} | 2 +- .../binary/binary_snd/CMakeLists.txt | 2 +- .../binary/binary_snd/src/binary_snd.cpp | 0 .../binary}/ping/CMakeLists.txt | 2 +- .../binary}/ping/src/ping.cpp | 5 +- .../binary}/pong/CMakeLists.txt | 2 +- .../binary}/pong/src/pong.cpp | 5 +- .../person_events_rec}/CMakeLists.txt | 14 +- .../src/person_events_rec.cpp} | 35 +- .../src/protobuf/animal.proto | 0 .../src/protobuf/house.proto | 0 .../src/protobuf/person.proto | 0 .../person_events_snd}/CMakeLists.txt | 15 +- .../src/person_events_snd.cpp} | 24 +- .../src/protobuf/animal.proto | 0 .../src/protobuf/house.proto | 0 .../src/protobuf/person.proto | 0 .../protobuf/person_loopback}/CMakeLists.txt | 14 +- .../person_loopback/src/person_loopback.cpp} | 38 +- .../src/protobuf/animal.proto | 0 .../person_loopback}/src/protobuf/house.proto | 0 .../src/protobuf/person.proto | 0 .../protobuf}/person_rec/CMakeLists.txt | 2 +- .../protobuf}/person_rec/src/person_rec.cpp | 0 .../person_rec}/src/protobuf/animal.proto | 0 .../person_rec}/src/protobuf/house.proto | 0 .../person_rec}/src/protobuf/person.proto | 0 .../protobuf}/person_snd/CMakeLists.txt | 2 +- .../protobuf}/person_snd/src/person_snd.cpp | 0 .../person_snd}/src/protobuf/animal.proto | 0 .../person_snd}/src/protobuf/house.proto | 0 .../person_snd}/src/protobuf/person.proto | 0 .../proto_dyn_json_rec}/CMakeLists.txt | 8 +- .../src/proto_dyn_json_rec.cpp} | 0 .../protobuf/proto_dyn_rec}/CMakeLists.txt | 8 +- .../proto_dyn_rec/src/proto_dyn_rec.cpp} | 0 .../string}/minimal_rec/CMakeLists.txt | 2 +- .../string}/minimal_rec/src/minimal_rec.cpp | 0 .../string}/minimal_rec_cb/CMakeLists.txt | 2 +- .../minimal_rec_cb/src/minimal_rec_cb.cpp | 0 .../string}/minimal_snd/CMakeLists.txt | 2 +- .../string}/minimal_snd/src/minimal_snd.cpp | 4 + .../services/ecalplayer_client/CMakeLists.txt | 42 - .../src/ecalplayer_client.cpp | 213 -- .../ecalplayer_gui_client/CMakeLists.txt | 111 - .../src/ecalplayer_gui_client.cpp | 245 -- .../ecalplayer_gui_client/src/main.cpp | 46 - .../ecalplayer_gui_client/src/main_window.ui | 543 ---- .../ecalsys_client/src/ecalsys_client.cpp | 100 - .../services/latency_client/CMakeLists.txt | 2 +- .../services/latency_server/CMakeLists.txt | 2 +- .../cpp/services/math_client/CMakeLists.txt | 2 +- .../services/math_client/src/math_client.cpp | 12 +- .../cpp/services/math_server/CMakeLists.txt | 2 +- .../services/minimal_client/CMakeLists.txt | 2 +- .../services/minimal_server/CMakeLists.txt | 2 +- .../cpp/services/ping_client/CMakeLists.txt | 2 +- .../services/ping_client_dyn/CMakeLists.txt | 44 - .../ping_client_dyn/src/ping_client_dyn.cpp | 125 - .../ping_client_dyn/src/proto_json_conv.cpp | 71 - .../ping_client_dyn/src/proto_json_conv.h | 58 - .../cpp/services/ping_server/CMakeLists.txt | 2 +- .../rec_client_service_cli/CMakeLists.txt | 43 - .../src/ecalrecorder_client.cpp | 182 -- .../rec_client_service_gui/CMakeLists.txt | 107 - .../src/EcalrecGuiClient.cpp | 276 -- .../src/EcalrecGuiClient.h | 67 - .../rec_client_service_gui/src/MainWindow.ui | 967 ------ .../rec_client_service_gui/src/main.cpp | 46 - .../rec_server_service_gui/CMakeLists.txt | 106 - .../rec_server_service_gui/src/main.cpp | 46 - .../src/rec_server_service_gui.cpp | 301 -- .../src/rec_server_service_gui.h | 77 - .../src/rec_server_service_gui.ui | 442 --- src/core/CMakeLists.txt | 742 +++++ {ecal => src}/core/cfg/CMakeLists.txt | 0 src/core/cfg/ecal.ini | 190 ++ .../include/ecal/cimpl/ecal_callback_cimpl.h | 27 +- .../include/ecal/cimpl/ecal_client_cimpl.h | 0 .../core/include/ecal/cimpl/ecal_core_cimpl.h | 0 .../core/include/ecal/cimpl/ecal_init_cimpl.h | 2 - .../core/include/ecal/cimpl/ecal_log_cimpl.h | 22 - .../ecal/cimpl/ecal_monitoring_cimpl.h | 0 .../include/ecal/cimpl/ecal_process_cimpl.h | 87 +- .../include/ecal/cimpl/ecal_publisher_cimpl.h | 180 ++ .../include/ecal/cimpl/ecal_server_cimpl.h | 0 .../include/ecal/cimpl/ecal_service_cimpl.h | 0 .../ecal/cimpl/ecal_service_info_cimpl.h | 0 .../ecal/cimpl/ecal_subscriber_cimpl.h | 99 +- .../core/include/ecal/cimpl/ecal_time_cimpl.h | 0 .../include/ecal/cimpl/ecal_timer_cimpl.h | 0 .../include/ecal/cimpl/ecal_tlayer_cimpl.h | 1 - .../core/include/ecal/cimpl/ecal_util_cimpl.h | 50 +- {ecal => src}/core/include/ecal/ecal.h | 12 - .../core/include/ecal/ecal_callback.h | 108 +- {ecal => src}/core/include/ecal/ecal_client.h | 54 +- {ecal => src}/core/include/ecal/ecal_config.h | 14 +- {ecal => src}/core/include/ecal/ecal_core.h | 0 src/core/include/ecal/ecal_deprecate.h | 61 + {ecal => src}/core/include/ecal/ecal_init.h | 0 {ecal => src}/core/include/ecal/ecal_log.h | 37 +- .../core/include/ecal/ecal_log_level.h | 5 +- .../core/include/ecal/ecal_monitoring.h | 40 +- {ecal => src}/core/include/ecal/ecal_os.h | 0 src/core/include/ecal/ecal_payload_writer.h | 119 + .../core/include/ecal/ecal_process.h | 74 +- .../core/include/ecal/ecal_process_mode.h | 0 .../core/include/ecal/ecal_process_severity.h | 0 src/core/include/ecal/ecal_publisher.h | 302 ++ {ecal => src}/core/include/ecal/ecal_server.h | 38 +- .../core/include/ecal/ecal_service_info.h | 2 +- .../core/include/ecal/ecal_subscriber.h | 137 +- {ecal => src}/core/include/ecal/ecal_time.h | 0 {ecal => src}/core/include/ecal/ecal_timer.h | 26 +- {ecal => src}/core/include/ecal/ecal_tlayer.h | 3 - src/core/include/ecal/ecal_types.h | 95 + {ecal => src}/core/include/ecal/ecal_util.h | 134 +- {ecal => src}/core/include/ecal/ecalc.h | 5 +- .../core/include/ecal/ecalc_types.h | 22 +- {ecal => src}/core/include/ecal/msg/dynamic.h | 0 .../core/include/ecal/msg/protobuf/client.h | 5 +- .../msg/protobuf/dynamic_json_subscriber.h | 266 ++ .../ecal/msg/protobuf/dynamic_publisher.h | 31 +- .../ecal/msg/protobuf/dynamic_subscriber.h | 59 +- .../ecal/msg/protobuf/ecal_proto_dyn.h | 419 +++ .../ecal/msg}/protobuf/ecal_proto_hlp.h | 4 +- .../include/ecal/msg/protobuf/publisher.h | 186 ++ .../core/include/ecal/msg/protobuf/server.h | 4 +- .../include/ecal/msg/protobuf/subscriber.h | 57 +- .../core/include/ecal/msg/publisher.h | 109 +- .../core/include/ecal/msg/string/publisher.h | 58 +- .../core/include/ecal/msg/string/subscriber.h | 50 +- .../core/include/ecal/msg/subscriber.h | 93 +- src/core/include/ecal/types/monitoring.h | 231 ++ .../core/src/config}/ecal_config.cpp | 34 +- .../core/src/config}/ecal_config_reader.cpp | 74 +- .../core/src/config}/ecal_config_reader.h | 3 +- .../core/src/config}/ecal_config_reader_hlp.h | 0 {ecal => src}/core/src/ecal.cpp | 55 +- {ecal => src}/core/src/ecal_def.h | 106 +- src/core/src/ecal_def_ini.h | 130 + src/core/src/ecal_defs.h.in | 44 + src/core/src/ecal_descgate.cpp | 323 ++ {ecal => src}/core/src/ecal_descgate.h | 57 +- {ecal => src}/core/src/ecal_event.cpp | 85 +- .../ecal => src/core/src}/ecal_event.h | 35 +- .../ecal => src/core/src}/ecal_eventhandle.h | 3 +- .../core/src/ecal_global_accessors.cpp | 60 +- .../core/src/ecal_global_accessors.h | 40 +- {ecal => src}/core/src/ecal_globals.cpp | 160 +- {ecal => src}/core/src/ecal_globals.h | 69 +- {ecal => src}/core/src/ecal_process.cpp | 264 +- {ecal => src}/core/src/ecal_process_stub.cpp | 0 {ecal => src}/core/src/ecal_process_stub.h | 0 src/core/src/ecal_sample_to_topicinfo.h | 56 + src/core/src/ecal_util.cpp | 239 ++ {ecal => src}/core/src/ecal_win_main.h | 1 + src/core/src/ecalc.cpp | 767 +++++ .../core/src/io/mtx}/ecal_named_mutex.cpp | 0 .../core/src/io/mtx}/ecal_named_mutex.h | 0 .../core/src/io/mtx}/ecal_named_mutex_base.h | 2 +- .../io/mtx}/linux/ecal_named_mutex_impl.cpp | 0 .../src/io/mtx}/linux/ecal_named_mutex_impl.h | 2 +- ...ecal_named_mutex_robust_clocklock_impl.cpp | 0 .../ecal_named_mutex_robust_clocklock_impl.h | 2 +- .../io/mtx}/win32/ecal_named_mutex_impl.cpp | 2 +- .../src/io/mtx}/win32/ecal_named_mutex_impl.h | 2 +- .../core/src/io/shm}/ecal_memfile.cpp | 84 +- .../io => src/core/src/io/shm}/ecal_memfile.h | 21 +- .../core/src/io/shm}/ecal_memfile_db.cpp | 20 +- .../core/src/io/shm}/ecal_memfile_db.h | 2 +- .../core/src/io/shm}/ecal_memfile_header.h | 2 +- .../core/src/io/shm}/ecal_memfile_info.h | 28 +- .../core/src/io/shm}/ecal_memfile_naming.cpp | 2 +- .../core/src/io/shm}/ecal_memfile_naming.h | 0 .../core/src/io/shm}/ecal_memfile_os.h | 0 .../core/src/io/shm}/ecal_memfile_pool.cpp | 124 +- .../core/src/io/shm}/ecal_memfile_pool.h | 19 +- .../core/src/io/shm}/ecal_memfile_sync.cpp | 78 +- .../core/src/io/shm}/ecal_memfile_sync.h | 10 +- .../src/io/shm}/linux/ecal_memfile_os.cpp | 15 +- .../src/io/shm}/win32/ecal_memfile_os.cpp | 2 +- .../src/io/udp/ecal_udp_configurations.cpp | 168 ++ src/core/src/io/udp/ecal_udp_configurations.h | 143 + .../src/io/udp/ecal_udp_sample_receiver.cpp | 276 ++ .../src/io/udp/ecal_udp_sample_receiver.h | 83 + .../src/io/udp/ecal_udp_sample_sender.cpp | 68 + .../core/src/io/udp/ecal_udp_sample_sender.h | 39 +- .../core/src/io/udp/ecal_udp_topic2mcast.h | 11 +- .../core/src/io/udp/fragmentation}/msg_type.h | 68 +- .../io/udp/fragmentation/rcv_fragments.cpp | 151 + .../src/io/udp/fragmentation/rcv_fragments.h | 75 + .../io/udp/fragmentation/snd_fragments.cpp | 180 ++ .../src/io/udp/fragmentation/snd_fragments.h | 23 +- .../src/io/udp/sendreceive/linux/socket_os.h | 88 + .../src/io/udp/sendreceive/udp_receiver.cpp | 106 + .../src/io/udp/sendreceive/udp_receiver.h | 93 + .../io/udp/sendreceive/udp_receiver_asio.cpp | 229 ++ .../io/udp/sendreceive}/udp_receiver_asio.h | 51 +- .../io/udp/sendreceive/udp_receiver_npcap.cpp | 130 + .../io/udp/sendreceive}/udp_receiver_npcap.h | 47 +- .../src/io/udp/sendreceive/udp_sender.cpp | 132 + .../core/src/io/udp/sendreceive/udp_sender.h | 56 +- .../src/io/udp/sendreceive/win32/socket_os.h | 0 .../src => src/core/src/logging}/ecal_log.cpp | 53 +- .../core/src/logging}/ecal_log_impl.cpp | 212 +- .../core/src/logging}/ecal_log_impl.h | 70 +- .../src/monitoring}/ecal_monitoring_def.cpp | 70 +- .../src/monitoring}/ecal_monitoring_def.h | 23 +- .../src/monitoring/ecal_monitoring_impl.cpp | 784 +++++ .../src/monitoring/ecal_monitoring_impl.h | 169 ++ src/core/src/pubsub/ecal_pubgate.cpp | 248 ++ {ecal => src}/core/src/pubsub/ecal_pubgate.h | 27 +- src/core/src/pubsub/ecal_publisher.cpp | 315 ++ src/core/src/pubsub/ecal_subgate.cpp | 348 +++ {ecal => src}/core/src/pubsub/ecal_subgate.h | 26 +- .../core/src/pubsub/ecal_subscriber.cpp | 176 +- .../core/src/readwrite/ecal_reader.cpp | 523 ++-- .../core/src/readwrite/ecal_reader.h | 75 +- .../core/src/readwrite/ecal_reader_layer.h | 18 +- src/core/src/readwrite/ecal_writer.cpp | 1256 ++++++++ src/core/src/readwrite/ecal_writer.h | 216 ++ .../core/src/readwrite/ecal_writer_base.h | 25 +- .../readwrite/ecal_writer_buffer_payload.h | 89 + .../core/src/readwrite/ecal_writer_data.h | 4 +- .../core/src/readwrite/ecal_writer_info.h | 9 +- .../src/readwrite/shm/ecal_reader_shm.cpp | 70 + .../core/src/readwrite/shm}/ecal_reader_shm.h | 12 +- .../src/readwrite/shm/ecal_writer_shm.cpp | 192 ++ .../core/src/readwrite/shm}/ecal_writer_shm.h | 24 +- .../src/readwrite/tcp}/ecal_reader_tcp.cpp | 92 +- .../core/src/readwrite/tcp}/ecal_reader_tcp.h | 22 +- .../readwrite/tcp}/ecal_tcp_pubsub_logger.h | 0 .../src/readwrite/tcp}/ecal_writer_tcp.cpp | 108 +- .../core/src/readwrite/tcp}/ecal_writer_tcp.h | 25 +- .../src/readwrite/udp/ecal_reader_udp_mc.cpp | 112 + .../src/readwrite/udp}/ecal_reader_udp_mc.h | 37 +- .../src/readwrite/udp/ecal_writer_udp_mc.cpp | 143 + .../src/readwrite/udp/ecal_writer_udp_mc.h | 25 +- .../ecal_registration_provider.cpp | 569 ++++ .../registration/ecal_registration_provider.h | 118 + .../ecal_registration_receiver.cpp | 392 +++ .../registration/ecal_registration_receiver.h | 106 + .../ecal_registration_receiver_shm.cpp | 85 + .../ecal_registration_receiver_shm.h | 63 + .../shm}/ecal_memfile_broadcast.cpp | 2 +- .../shm}/ecal_memfile_broadcast.h | 3 +- .../shm}/ecal_memfile_broadcast_reader.cpp | 2 +- .../shm}/ecal_memfile_broadcast_reader.h | 18 +- .../shm}/ecal_memfile_broadcast_writer.cpp | 4 +- .../shm}/ecal_memfile_broadcast_writer.h | 0 .../shm}/relocatable_circular_queue.h | 0 .../serialization/ecal_serialize_common.cpp | 397 +++ .../src/serialization/ecal_serialize_common.h | 62 + .../serialization/ecal_serialize_logging.cpp | 332 +++ .../serialization/ecal_serialize_logging.h | 43 + .../ecal_serialize_monitoring.cpp | 922 ++++++ .../serialization/ecal_serialize_monitoring.h | 25 +- .../ecal_serialize_sample_payload.cpp | 197 ++ .../ecal_serialize_sample_payload.h | 19 +- .../ecal_serialize_sample_registration.cpp | 614 ++++ .../ecal_serialize_sample_registration.h | 43 + .../serialization/ecal_serialize_service.cpp | 326 +++ .../serialization/ecal_serialize_service.h | 43 + .../src/serialization/ecal_struct_logging.h | 54 + .../serialization/ecal_struct_sample_common.h | 47 +- .../ecal_struct_sample_payload.h | 80 + .../ecal_struct_sample_registration.h | 196 ++ .../src/serialization/ecal_struct_service.h | 109 + src/core/src/serialization/nanopb/ecal.pb.c | 19 + src/core/src/serialization/nanopb/ecal.pb.h | 156 + src/core/src/serialization/nanopb/host.pb.c | 15 + src/core/src/serialization/nanopb/host.pb.h | 67 + src/core/src/serialization/nanopb/layer.pb.c | 28 + src/core/src/serialization/nanopb/layer.pb.h | 166 ++ .../src/serialization/nanopb/logging.pb.c | 15 + .../src/serialization/nanopb/logging.pb.h | 81 + .../src/serialization/nanopb/monitoring.pb.c | 12 + .../src/serialization/nanopb/monitoring.pb.h | 68 + src/core/src/serialization/nanopb/nanopb/pb.h | 917 ++++++ .../serialization/nanopb/nanopb/pb_common.c | 388 +++ .../serialization/nanopb/nanopb/pb_common.h | 49 + .../serialization/nanopb/nanopb/pb_decode.c | 1727 +++++++++++ .../serialization/nanopb/nanopb/pb_decode.h | 193 ++ .../serialization/nanopb/nanopb/pb_encode.c | 1000 +++++++ .../serialization/nanopb/nanopb/pb_encode.h | 185 ++ .../src/serialization/nanopb/process.pb.c | 18 + .../src/serialization/nanopb/process.pb.h | 155 + .../src/serialization/nanopb/service.pb.c | 28 + .../src/serialization/nanopb/service.pb.h | 242 ++ src/core/src/serialization/nanopb/topic.pb.c | 18 + src/core/src/serialization/nanopb/topic.pb.h | 151 + .../core/src/service/ecal_clientgate.cpp | 69 +- .../core/src/service/ecal_clientgate.h | 19 +- .../core/src/service/ecal_service_client.cpp | 42 +- .../src/service/ecal_service_client_impl.cpp | 735 +++++ .../src/service/ecal_service_client_impl.h | 73 +- .../core/src/service/ecal_service_server.cpp | 19 +- .../src/service/ecal_service_server_impl.cpp | 527 ++++ .../src/service/ecal_service_server_impl.h | 75 +- .../ecal_service_singleton_manager.cpp | 185 ++ .../service/ecal_service_singleton_manager.h | 80 + .../core/src/service/ecal_servicegate.cpp | 47 +- .../core/src/service/ecal_servicegate.h | 18 +- src/core/src/time/ecal_time.cpp | 141 + .../core/src/time}/ecal_timegate.cpp | 43 +- .../src => src/core/src/time}/ecal_timegate.h | 4 +- .../src => src/core/src/time}/ecal_timer.cpp | 14 +- .../core/src/util}/advanced_tclap_output.cpp | 20 +- .../core/src/util}/advanced_tclap_output.h | 7 +- .../src => src/core/src/util}/ecal_expmap.h | 156 +- src/core/src/util/ecal_thread.h | 123 + .../src => src/core/src/util}/getenvvar.h | 2 +- {ecal => src}/core/src/win32/dll/dllmain.cpp | 0 {ecal => src}/core/src/win32/dll/ecal.rc | Bin {ecal => src}/core/src/win32/dll/resource.h | 0 src/protobuf/ecal.proto | 71 + .../house.proto => src/protobuf/host.proto | 12 +- src/protobuf/layer.proto | 64 + .../protobuf/logging.proto | 20 +- .../protobuf/monitoring.proto | 28 +- src/protobuf/process.proto | 74 + src/protobuf/service.proto | 94 + src/protobuf/topic.proto | 61 + .../misc/timer => src/service}/CMakeLists.txt | 31 +- src/service/Readme.md | 177 ++ src/service/ecal_service/CMakeLists.txt | 104 + .../include/ecal/service/client_manager.h | 198 ++ .../include/ecal/service/client_session.h | 291 ++ .../ecal/service/client_session_types.h | 28 +- .../ecal_service/include/ecal/service/error.h | 110 + .../include/ecal/service/logger.h | 75 + .../include/ecal/service/server.h | 209 ++ .../include/ecal/service/server_manager.h | 184 ++ .../ecal/service/server_session_types.h | 34 +- .../ecal_service/include/ecal/service/state.h | 23 +- .../ecal_service/src/client_manager.cpp | 109 + .../ecal_service/src/client_session.cpp | 146 + .../src/client_session_impl_base.h | 94 + .../src/client_session_impl_v0.cpp | 516 ++++ .../ecal_service/src/client_session_impl_v0.h | 151 + .../src/client_session_impl_v1.cpp | 635 ++++ .../ecal_service/src/client_session_impl_v1.h | 160 + .../src/condition_variable_signaler.h | 54 + src/service/ecal_service/src/log_defs.h | 84 + src/service/ecal_service/src/log_helpers.h | 66 + .../ecal_service/src/protocol_layout.h | 73 + src/service/ecal_service/src/protocol_v0.cpp | 127 + src/service/ecal_service/src/protocol_v0.h | 46 + src/service/ecal_service/src/protocol_v1.cpp | 191 ++ src/service/ecal_service/src/protocol_v1.h | 46 + src/service/ecal_service/src/server.cpp | 94 + src/service/ecal_service/src/server_impl.cpp | 312 ++ src/service/ecal_service/src/server_impl.h | 114 + .../ecal_service/src/server_manager.cpp | 109 + .../src/server_session_impl_base.h | 107 + .../src/server_session_impl_v0.cpp | 239 ++ .../ecal_service/src/server_session_impl_v0.h | 96 + .../src/server_session_impl_v1.cpp | 324 ++ .../ecal_service/src/server_session_impl_v1.h | 98 + src/service/sample/CMakeLists.txt | 39 + src/service/sample/src/main.cpp | 92 + src/service/test/CMakeLists.txt | 43 + src/service/test/src/atomic_signalable.h | 208 ++ .../test/src/ecal_tcp_service_test.cpp | 2596 +++++++++++++++++ {samples/cpp/misc => src}/time/CMakeLists.txt | 24 +- src/time/include/dynamic_sleeper.h | 185 ++ src/time/include/ecaltime.h | 114 + src/time/linuxptp/CMakeLists.txt | 57 + src/time/linuxptp/src/clock.h | 74 + .../time/linuxptp/src/config/config.h | 59 +- src/time/linuxptp/src/config/ecaltime.ini | 15 + .../time/linuxptp}/src/convert_utf.cpp | 0 .../time/linuxptp}/src/convert_utf.h | 0 src/time/linuxptp/src/ecal_time_linuxptp.cpp | 146 + src/time/linuxptp/src/ecal_time_linuxptp.h | 90 + src/time/linuxptp/src/ecaltime.cpp | 79 + src/time/localtime/CMakeLists.txt | 56 + src/time/localtime/src/dllmain.cpp | 18 + src/time/localtime/src/ecaltime.cpp | 70 + .../utils}/CMakeLists.txt | 54 +- src/utils/include/ecal_utils/command_line.h | 33 + src/utils/include/ecal_utils/ecal_utils.h | 677 +++++ src/utils/include/ecal_utils/filesystem.h | 142 + .../include/ecal_utils/portable_endian.h | 134 + src/utils/include/ecal_utils/str_convert.h | 40 + src/utils/include/ecal_utils/string.h | 269 ++ src/utils/src/command_line.cpp | 70 + src/utils/src/filesystem.cpp | 876 ++++++ src/utils/src/str_convert.cpp | 78 + testing/ecal/clientserver_test/src/dummy.cpp | 0 .../src/pubsub_inproc_test.cpp | 105 - .../src/protobuf/animal.proto | 28 - .../src/protobuf/house.proto | 27 - .../src/protobuf/person.proto | 42 - .../dynproto_test/CMakeLists.txt | 49 - .../dynproto_test/src/dynproto_test.cpp | 328 --- .../dynproto_test/src/protobuf/animal.proto | 28 - .../dynproto_test/src/protobuf/attitude.proto | 28 - .../dynproto_test/src/protobuf/house.proto | 27 - .../dynproto_test/src/protobuf/person.proto | 44 - .../ecal_proto_test/CMakeLists.txt | 48 - .../ecal_proto_test/src/protobuf/animal.proto | 28 - .../ecal_proto_test/src/protobuf/house.proto | 27 - .../ecal_proto_test/src/protobuf/person.proto | 42 - .../ecal_proto_test/src/test_filters.cpp | 194 -- .../src/test_maximum_array_dimensions.cpp | 68 - .../src/test_proto_dyn_decoder.cpp | 134 - tests/CMakeLists.txt | 45 + .../clientserver_proto_test}/CMakeLists.txt | 10 +- .../src/clientserver_test_proto.cpp | 206 ++ .../src/protobuf/math.proto | 0 .../src/protobuf/ping.proto | 0 .../clientserver_test}/CMakeLists.txt | 17 +- .../clientserver_test/src/atomic_signalable.h | 202 ++ .../src/clientserver_test.cpp | 448 +-- .../ecal => tests}/core_test/CMakeLists.txt | 4 +- .../core_test/src/core_test.cpp | 87 +- .../ecal => tests}/expmap_test/CMakeLists.txt | 2 +- .../expmap_test/src/expmap_test.cpp | 67 +- .../io_memfile_test/CMakeLists.txt | 19 +- .../src/memfile_naming_test.cpp | 33 +- .../io_memfile_test/src/memfile_test.cpp | 29 +- .../pubsub_proto_test/CMakeLists.txt | 3 +- .../src/proto_dyn_subscriber_test.cpp | 133 + .../src/proto_publisher_test.cpp | 5 - .../src/proto_subscriber_test.cpp | 6 - .../src/protobuf/animal.proto | 0 .../src/protobuf/house.proto | 0 .../src/protobuf/person.proto | 0 .../ecal => tests}/pubsub_test/CMakeLists.txt | 2 +- .../pubsub_test/src/pubsub_receive_test.cpp | 9 +- .../pubsub_test/src/pubsub_test.cpp | 546 ++-- tests/serialization_test/CMakeLists.txt | 113 + .../src/common_generate.cpp | 39 + .../src/logging_compare.cpp | 21 +- .../src/logging_generate.cpp | 44 + .../src/logging_serialization_test.cpp | 92 + .../src/monitoring_compare.cpp | 188 ++ .../src/monitoring_generate.cpp | 161 + .../src/monitoring_serialization_test.cpp | 57 + .../src/payload_compare.cpp | 82 + .../src/payload_generate.cpp | 112 + .../src/payload_serialization_test.cpp | 144 + .../src/registration_compare.cpp | 194 ++ .../src/registration_generate.cpp | 155 + .../src/registration_serialization_test.cpp | 91 + .../src/service_compare.cpp | 53 + .../src/service_generate.cpp | 66 + .../src/service_serialization_test.cpp | 86 + .../topic2mcast_test/CMakeLists.txt | 2 +- .../topic2mcast_test/src/topic2mcast_test.cpp | 7 +- .../util_test}/CMakeLists.txt | 15 +- tests/util_test/src/util_test.cpp | 54 + thirdparty/asio | 1 - thirdparty/asio/Modules/Findasio.cmake | 1 + thirdparty/asio/build-asio.cmake | 7 + thirdparty/build-asio.cmake | 0 thirdparty/build-cmakefunctions.cmake | 1 - thirdparty/build-recycle.cmake | 2 - thirdparty/build-simpleini.cmake | 0 thirdparty/build-tclap.cmake | 0 thirdparty/build-tcp_pubsub.cmake | 3 - .../Modules/FindCMakeFunctions.cmake | 1 + .../cmakefunctions/build-cmakefunctions.cmake | 2 + .../cmake_functions/.gitignore | 0 .../cmake_functions/CMakeLists.txt | 2 +- .../cmake/CMakeFunctionsConfig.cmake.in | 0 .../cmake/cpack_variables.cmake | 0 .../cmake_functions/cmake_functions.cmake | 0 .../git/git_revision_information.cmake | 0 .../msvc_helper/msvc_macros.cmake | 0 .../protoc_generate_cpp.cmake | 0 .../protoc_generate_files.cmake | 0 .../protoc_generate_python.cmake | 0 .../target_definitions/targets_protobuf.cmake | 0 thirdparty/ecal-utils | 1 - thirdparty/googletest | 1 - thirdparty/gtest/Modules/FindGTest.cmake | 1 + thirdparty/{ => gtest}/build-gtest.cmake | 6 +- thirdparty/protobuf | 1 - .../protobuf/Modules/FindProtobuf.cmake | 1 + .../{ => protobuf}/build-protobuf.cmake | 6 +- thirdparty/recycle | 1 - thirdparty/recycle/Modules/Findrecycle.cmake | 1 + thirdparty/recycle/build-recycle.cmake | 4 + thirdparty/simpleini | 1 - .../simpleini/Modules/Findsimpleini.cmake | 1 + thirdparty/simpleini/build-simpleini.cmake | 6 + thirdparty/tclap | 1 - thirdparty/tclap/Modules/Findtclap.cmake | 1 + thirdparty/tclap/build-tclap.cmake | 6 + thirdparty/tcp_pubsub | 1 - .../tcp_pubsub/Modules/Findtcp_pubsub.cmake | 1 + thirdparty/tcp_pubsub/build-tcp_pubsub.cmake | 5 + thirdparty/udpcap | 1 - thirdparty/udpcap/Modules/Findudpcap.cmake | 1 + thirdparty/{ => udpcap}/build-udpcap.cmake | 6 +- 794 files changed, 44526 insertions(+), 35343 deletions(-) delete mode 100644 .github/workflows/build-ubuntu-20.yml delete mode 100644 .github/workflows/build-ubuntu-22-capnproto.yml delete mode 100644 .github/workflows/build-ubuntu-22.yml create mode 100644 .github/workflows/build-ubuntu.yml delete mode 100644 .github/workflows/run-clang-tidy.yml create mode 100644 DEPENDENCIES.txt create mode 100644 NOTICE.txt create mode 100644 build/linux/01_build_default.sh create mode 100644 build/linux/02_build_full.sh create mode 100644 build/linux/03_build_pubsub.sh create mode 100644 build/linux/04_build_pubsub_proto.sh create mode 100644 build/linux/05_build_pubsub_udp_only.sh create mode 100644 build/linux/06_build_pubsub_tcp_only.sh create mode 100644 build/linux/07_build_pubsub_shm_only.sh create mode 100644 build/linux/08_build_clientserver.sh create mode 100644 build/linux/09_build_clientserver_proto.sh create mode 100644 build/linux/10_build_monitoring_only.sh create mode 100644 build/windows/01_build_default.bat create mode 100644 build/windows/02_build_full.bat create mode 100644 build/windows/03_build_pubsub.bat create mode 100644 build/windows/04_build_pubsub_proto.bat create mode 100644 build/windows/05_build_pubsub_udp_only.bat create mode 100644 build/windows/06_build_pubsub_tcp_only.bat create mode 100644 build/windows/07_build_pubsub_shm_only.bat create mode 100644 build/windows/08_build_clientserver.bat create mode 100644 build/windows/09_build_clientserver_proto.bat create mode 100644 build/windows/10_build_monitoring_only.bat delete mode 100644 build/windows/Readme.md delete mode 100644 build/windows/download_npcap.ps1 delete mode 100644 build/windows/win_make_all.bat delete mode 100644 build/windows/win_make_build.bat delete mode 100644 build/windows/win_make_cmake.bat delete mode 100644 build/windows/win_make_setup.bat delete mode 100644 build/windows/win_run_tests.bat delete mode 100644 build/windows/win_set_vars.bat create mode 100644 cmake/Modules/eCALConfig.cmake create mode 100644 cmake/helper_functions/app.desktop.in create mode 100644 cpack/ecal_path.xml create mode 100644 cpack/innosetup/gfx/logo.svg create mode 100644 cpack/innosetup/gfx/logo_110_110.bmp create mode 100644 cpack/innosetup/gfx/logo_55_55.bmp delete mode 100644 cpack/innosetup/gfx/logo_icon.bmp delete mode 100644 cpack/innosetup/gfx/logo_icon.ico delete mode 100644 cpack/innosetup/gfx/logo_icon.xcf delete mode 100644 ecal/core/CMakeLists.txt delete mode 100644 ecal/core/cfg/ecal.ini delete mode 100644 ecal/core/include/ecal/cimpl/ecal_event_cimpl.h delete mode 100644 ecal/core/include/ecal/cimpl/ecal_proto_dyn_json_subscriber_cimpl.h delete mode 100644 ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h delete mode 100644 ecal/core/include/ecal/cimpl/ecal_qos_cimpl.h delete mode 100644 ecal/core/include/ecal/ecal_clang.h delete mode 100644 ecal/core/include/ecal/ecal_publisher.h delete mode 100644 ecal/core/include/ecal/ecal_qos.h delete mode 100644 ecal/core/include/ecal/ecal_service.h delete mode 100644 ecal/core/include/ecal/ecal_timed_cb.h delete mode 100644 ecal/core/include/ecal/ecalc_types.h delete mode 100644 ecal/core/include/ecal/msg/capnproto/dynamic.h delete mode 100644 ecal/core/include/ecal/msg/capnproto/helper.h delete mode 100644 ecal/core/include/ecal/msg/capnproto/publisher.h delete mode 100644 ecal/core/include/ecal/msg/capnproto/subscriber.h delete mode 100644 ecal/core/include/ecal/msg/flatbuffers/publisher.h delete mode 100644 ecal/core/include/ecal/msg/flatbuffers/subscriber.h delete mode 100644 ecal/core/include/ecal/msg/messagepack/publisher.h delete mode 100644 ecal/core/include/ecal/msg/messagepack/subscriber.h delete mode 100644 ecal/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h delete mode 100644 ecal/core/include/ecal/msg/protobuf/publisher.h delete mode 100644 ecal/core/src/_code.cppcheck delete mode 100644 ecal/core/src/ecal_clang.cpp delete mode 100644 ecal/core/src/ecal_def_ini.h delete mode 100644 ecal/core/src/ecal_defs.h.in delete mode 100644 ecal/core/src/ecal_descgate.cpp delete mode 100644 ecal/core/src/ecal_registration_provider.cpp delete mode 100644 ecal/core/src/ecal_registration_provider.h delete mode 100644 ecal/core/src/ecal_registration_receiver.cpp delete mode 100644 ecal/core/src/ecal_registration_receiver.h delete mode 100644 ecal/core/src/ecal_thread.cpp delete mode 100644 ecal/core/src/ecal_thread.h delete mode 100644 ecal/core/src/ecal_time.cpp delete mode 100644 ecal/core/src/ecal_util.cpp delete mode 100644 ecal/core/src/ecalc.cpp delete mode 100644 ecal/core/src/io/ecal_receiver.h delete mode 100644 ecal/core/src/io/ecal_sender.h delete mode 100644 ecal/core/src/io/linux/ecal_socket_option_linux.h delete mode 100644 ecal/core/src/io/rcv_sample.cpp delete mode 100644 ecal/core/src/io/rcv_sample.h delete mode 100644 ecal/core/src/io/snd_raw_buffer.cpp delete mode 100644 ecal/core/src/io/snd_sample.cpp delete mode 100644 ecal/core/src/io/udp_configurations.cpp delete mode 100644 ecal/core/src/io/udp_init.cpp delete mode 100644 ecal/core/src/io/udp_receiver.cpp delete mode 100644 ecal/core/src/io/udp_receiver_asio.cpp delete mode 100644 ecal/core/src/io/udp_receiver_base.h delete mode 100644 ecal/core/src/io/udp_receiver_npcap.cpp delete mode 100644 ecal/core/src/io/udp_sender.cpp delete mode 100644 ecal/core/src/mon/ecal_monitoring_impl.cpp delete mode 100644 ecal/core/src/mon/ecal_monitoring_impl.h delete mode 100644 ecal/core/src/mon/ecal_monitoring_threads.cpp delete mode 100644 ecal/core/src/mon/ecal_monitoring_threads.h delete mode 100644 ecal/core/src/pubsub/ecal_proto_dyn_json_sub.cpp delete mode 100644 ecal/core/src/pubsub/ecal_pubgate.cpp delete mode 100644 ecal/core/src/pubsub/ecal_publisher.cpp delete mode 100644 ecal/core/src/pubsub/ecal_subgate.cpp delete mode 100644 ecal/core/src/readwrite/ecal_reader_iceoryx.cpp delete mode 100644 ecal/core/src/readwrite/ecal_reader_iceoryx.h delete mode 100644 ecal/core/src/readwrite/ecal_reader_shm.cpp delete mode 100644 ecal/core/src/readwrite/ecal_reader_udp_mc.cpp delete mode 100644 ecal/core/src/readwrite/ecal_writer.cpp delete mode 100644 ecal/core/src/readwrite/ecal_writer.h delete mode 100644 ecal/core/src/readwrite/ecal_writer_iceoryx.cpp delete mode 100644 ecal/core/src/readwrite/ecal_writer_inproc.cpp delete mode 100644 ecal/core/src/readwrite/ecal_writer_shm.cpp delete mode 100644 ecal/core/src/readwrite/ecal_writer_udp_mc.cpp delete mode 100644 ecal/core/src/readwrite/ecal_writer_udp_mc.h delete mode 100644 ecal/core/src/service/asio_server.h delete mode 100644 ecal/core/src/service/ecal_service_client_impl.cpp delete mode 100644 ecal/core/src/service/ecal_service_server_impl.cpp delete mode 100644 ecal/core/src/service/ecal_tcpclient.cpp delete mode 100644 ecal/core/src/service/ecal_tcpclient.h delete mode 100644 ecal/core/src/service/ecal_tcpheader.h delete mode 100644 ecal/core/src/service/ecal_tcpserver.cpp delete mode 100644 ecal/core/src/service/ecal_tcpserver.h delete mode 100644 ecal/core/src/sys_usage.cpp delete mode 100644 ecal/core_pb/CMakeLists.txt delete mode 100644 ecal/core_pb/src/core_pb.cpp delete mode 100644 ecal/core_pb/src/ecal/core/pb/topic.proto delete mode 100644 lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_dyn.h delete mode 100644 lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_message_filter.h delete mode 100644 lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_visitor.h delete mode 100644 lib/ecal_protobuf/src/ecal_proto_decoder.cpp delete mode 100644 lib/ecal_protobuf/src/ecal_proto_dyn.cpp delete mode 100644 lib/ecal_protobuf/src/ecal_proto_maximum_array_dimensions.cpp delete mode 100644 lib/ecal_protobuf/src/ecal_proto_message_filter.cpp delete mode 100644 lib/ecal_protobuf/src/ecal_proto_visitor.cpp delete mode 100644 licenses/capnproto/LICENSE delete mode 100644 licenses/google-flatbuffers/LICENSE.txt create mode 100644 logos/ecal-cop-image.png rename samples/c/{minimal => pubsub/string}/minimal_rec/CMakeLists.txt (94%) rename samples/c/{minimal => pubsub/string}/minimal_rec/src/minimal_rec.c (96%) rename samples/c/{minimal => pubsub/string}/minimal_rec_cb/CMakeLists.txt (94%) rename samples/c/{minimal => pubsub/string}/minimal_rec_cb/src/minimal_rec_cb.c (96%) rename samples/c/{minimal => pubsub/string}/minimal_snd/CMakeLists.txt (94%) rename samples/c/{minimal => pubsub/string}/minimal_snd/src/minimal_snd.c (96%) delete mode 100644 samples/c/services/minimal_client_c/CMakeLists.txt delete mode 100644 samples/c/services/minimal_client_c/src/minimal_client_c.c delete mode 100644 samples/c/services/minimal_server_c/CMakeLists.txt delete mode 100644 samples/c/services/minimal_server_c/src/minimal_server_c.c rename samples/cpp/{binary/binary_rec_cb => benchmarks/counter_rec}/CMakeLists.txt (88%) rename samples/cpp/{counter/counter_rec_cb/src/counter_rec_cb.cpp => benchmarks/counter_rec/src/counter_rec.cpp} (64%) rename samples/cpp/{counter => benchmarks}/counter_snd/CMakeLists.txt (98%) rename samples/cpp/{counter => benchmarks}/counter_snd/src/counter_snd.cpp (67%) rename samples/cpp/{datarate => benchmarks}/datarate_rec/CMakeLists.txt (98%) rename samples/cpp/{datarate => benchmarks}/datarate_rec/src/datarate_rec.cpp (79%) rename samples/cpp/{datarate => benchmarks}/datarate_snd/CMakeLists.txt (98%) rename samples/cpp/{datarate => benchmarks}/datarate_snd/src/datarate_snd.cpp (53%) rename samples/cpp/{latency => benchmarks}/latency_rec/CMakeLists.txt (98%) rename samples/cpp/{latency => benchmarks}/latency_rec/src/latency_log.cpp (71%) rename samples/cpp/{latency => benchmarks}/latency_rec/src/latency_log.h (100%) rename samples/cpp/{latency => benchmarks}/latency_rec/src/latency_rec.cpp (94%) rename samples/cpp/{latency => benchmarks}/latency_snd/CMakeLists.txt (98%) rename samples/cpp/{services/ecalplayer_gui_client/src/ecalplayer_gui_client.h => benchmarks/latency_snd/src/binary_payload_writer.h} (50%) rename samples/cpp/{latency => benchmarks}/latency_snd/src/latency_snd.cpp (72%) delete mode 100644 samples/cpp/benchmarks/many_connections/CMakeLists.txt rename samples/cpp/{counter/counter_rec_cb => benchmarks/many_connections_rec}/CMakeLists.txt (85%) rename samples/cpp/benchmarks/{many_connections => many_connections_rec/src}/many_connections_rec.cpp (76%) create mode 100644 samples/cpp/benchmarks/many_connections_snd/CMakeLists.txt rename samples/cpp/benchmarks/{many_connections => many_connections_snd/src}/many_connections_snd.cpp (74%) create mode 100644 samples/cpp/benchmarks/multiple_rec/CMakeLists.txt rename samples/cpp/{multiple/multiple_rec_cb/src/multiple_rec_cb.cpp => benchmarks/multiple_rec/src/multiple_rec.cpp} (96%) create mode 100644 samples/cpp/benchmarks/multiple_snd/CMakeLists.txt rename samples/cpp/{multiple => benchmarks}/multiple_snd/src/multiple_snd.cpp (76%) rename samples/cpp/{performance => benchmarks}/performance_rec/CMakeLists.txt (98%) rename samples/cpp/{performance/performance_rec_cb/src/performance_rec_cb.cpp => benchmarks/performance_rec/src/performance_rec.cpp} (53%) rename samples/cpp/{performance => benchmarks}/performance_snd/CMakeLists.txt (98%) create mode 100644 samples/cpp/benchmarks/performance_snd/src/binary_payload_writer.h create mode 100644 samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp rename samples/cpp/{services/ecalsys_client => benchmarks/perftool}/CMakeLists.txt (59%) create mode 100644 samples/cpp/benchmarks/perftool/Readme.md create mode 100644 samples/cpp/benchmarks/perftool/src/main.cpp create mode 100644 samples/cpp/benchmarks/perftool/src/publisher.cpp create mode 100644 samples/cpp/benchmarks/perftool/src/publisher.h create mode 100644 samples/cpp/benchmarks/perftool/src/publisher_statistics.h create mode 100644 samples/cpp/benchmarks/perftool/src/subscriber.cpp create mode 100644 samples/cpp/benchmarks/perftool/src/subscriber.h create mode 100644 samples/cpp/benchmarks/perftool/src/subscriber_statistics.h delete mode 100644 samples/cpp/capnp/addressbook_rec/CMakeLists.txt delete mode 100644 samples/cpp/capnp/addressbook_rec/src/addressbook.capnp delete mode 100644 samples/cpp/capnp/addressbook_rec/src/addressbook_rec.cpp delete mode 100644 samples/cpp/capnp/addressbook_rec_cb/CMakeLists.txt delete mode 100644 samples/cpp/capnp/addressbook_rec_cb/src/addressbook.capnp delete mode 100644 samples/cpp/capnp/addressbook_rec_cb/src/addressbook_rec_cb.cpp delete mode 100644 samples/cpp/capnp/addressbook_rec_dynamic/CMakeLists.txt delete mode 100644 samples/cpp/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp delete mode 100644 samples/cpp/capnp/addressbook_snd/CMakeLists.txt delete mode 100644 samples/cpp/capnp/addressbook_snd/src/addressbook.capnp delete mode 100644 samples/cpp/capnp/addressbook_snd/src/addressbook_snd.cpp delete mode 100644 samples/cpp/event/event_rec/src/event_rec.cpp delete mode 100644 samples/cpp/event/event_snd/CMakeLists.txt delete mode 100644 samples/cpp/event/event_snd/src/event_snd.cpp delete mode 100644 samples/cpp/flatbuffer/monster_rec/src/flatbuffers/monster.fbs delete mode 100644 samples/cpp/flatbuffer/monster_rec/src/monster_rec.cpp delete mode 100644 samples/cpp/flatbuffer/monster_snd/src/flatbuffers/monster.fbs delete mode 100644 samples/cpp/flatbuffer/monster_snd/src/monster_snd.cpp delete mode 100644 samples/cpp/misc/process/CMakeLists.txt delete mode 100644 samples/cpp/misc/process/src/process.cpp delete mode 100644 samples/cpp/misc/time/src/time.cpp delete mode 100644 samples/cpp/monitoring/monitoring_get_services/CMakeLists.txt delete mode 100644 samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp delete mode 100644 samples/cpp/monitoring/monitoring_get_topics/CMakeLists.txt delete mode 100644 samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp rename {ecal/core_pb/src/ecal/core/pb => samples/cpp/monitoring/monitoring_rec/src/protobuf}/host.proto (100%) rename {ecal/core_pb/src/ecal/core/pb => samples/cpp/monitoring/monitoring_rec/src/protobuf}/layer.proto (72%) rename samples/cpp/{person/person_snd_events/src/protobuf/person.proto => monitoring/monitoring_rec/src/protobuf/monitoring.proto} (61%) rename {ecal/core_pb/src/ecal/core/pb => samples/cpp/monitoring/monitoring_rec/src/protobuf}/process.proto (91%) rename {ecal/core_pb/src/ecal/core/pb => samples/cpp/monitoring/monitoring_rec/src/protobuf}/service.proto (88%) create mode 100644 samples/cpp/monitoring/monitoring_rec/src/protobuf/topic.proto rename {ecal/core_pb/src/ecal/core/pb => samples/cpp/monitoring/monitoring_reg/src/protobuf}/ecal.proto (59%) rename samples/cpp/{person/person_snd_dyn/src/protobuf/animal.proto => monitoring/monitoring_reg/src/protobuf/host.proto} (71%) create mode 100644 samples/cpp/monitoring/monitoring_reg/src/protobuf/layer.proto create mode 100644 samples/cpp/monitoring/monitoring_reg/src/protobuf/process.proto create mode 100644 samples/cpp/monitoring/monitoring_reg/src/protobuf/service.proto create mode 100644 samples/cpp/monitoring/monitoring_reg/src/protobuf/topic.proto delete mode 100644 samples/cpp/msgpack/address_rec/src/address_rec.cpp delete mode 100644 samples/cpp/msgpack/address_snd/src/address_snd.cpp delete mode 100644 samples/cpp/multiple/multiple_rec_cb/CMakeLists.txt delete mode 100644 samples/cpp/multiple/multiple_rec_cb/src/main.cpp delete mode 100644 samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb.h delete mode 100644 samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.cpp delete mode 100644 samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.h delete mode 100644 samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.cpp delete mode 100644 samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.h delete mode 100644 samples/cpp/multiple/multiple_snd/CMakeLists.txt delete mode 100644 samples/cpp/multiple/multiple_snd/src/main.cpp delete mode 100644 samples/cpp/multiple/multiple_snd/src/multiple_snd.h delete mode 100644 samples/cpp/multiple/multiple_snd/src/multiple_snd_person.cpp delete mode 100644 samples/cpp/multiple/multiple_snd/src/multiple_snd_person.h delete mode 100644 samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.cpp delete mode 100644 samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.h delete mode 100644 samples/cpp/orchestration/component1/CMakeLists.txt delete mode 100644 samples/cpp/orchestration/component1/src/component1.cpp delete mode 100644 samples/cpp/orchestration/component1/src/protobuf/component.proto delete mode 100644 samples/cpp/orchestration/component1/src/protobuf/orchestrator.proto delete mode 100644 samples/cpp/orchestration/component2/CMakeLists.txt delete mode 100644 samples/cpp/orchestration/component2/src/component2.cpp delete mode 100644 samples/cpp/orchestration/component2/src/protobuf/component.proto delete mode 100644 samples/cpp/orchestration/component2/src/protobuf/orchestrator.proto delete mode 100644 samples/cpp/orchestration/orchestrator/CMakeLists.txt delete mode 100644 samples/cpp/orchestration/orchestrator/src/orchestrator.cpp delete mode 100644 samples/cpp/orchestration/orchestrator/src/protobuf/orchestrator.proto delete mode 100644 samples/cpp/performance/dynsize_snd/CMakeLists.txt delete mode 100644 samples/cpp/performance/dynsize_snd/src/dynsize_snd.cpp delete mode 100644 samples/cpp/performance/performance_rec/src/performance_rec.cpp delete mode 100644 samples/cpp/performance/performance_rec_cb/CMakeLists.txt delete mode 100644 samples/cpp/performance/performance_snd/src/performance_snd.cpp delete mode 100644 samples/cpp/performance/pubsub_throughput/CMakeLists.txt delete mode 100644 samples/cpp/performance/pubsub_throughput/src/pubsub_throughput.cpp delete mode 100644 samples/cpp/person/person_rec_lambda_in_class/CMakeLists.txt delete mode 100644 samples/cpp/person/person_rec_lambda_in_class/include/addressbook.h delete mode 100644 samples/cpp/person/person_rec_lambda_in_class/src/addressbook.cpp delete mode 100644 samples/cpp/person/person_snd_dyn/src/person_snd_dyn.cpp delete mode 100644 samples/cpp/person/person_snd_dyn/src/protobuf/person.proto delete mode 100644 samples/cpp/person/person_snd_events/CMakeLists.txt delete mode 100644 samples/cpp/person/person_snd_events/src/protobuf/animal.proto delete mode 100644 samples/cpp/person/person_snd_events/src/protobuf/house.proto delete mode 100644 samples/cpp/person/person_snd_inproc/CMakeLists.txt delete mode 100644 samples/cpp/person/person_snd_inproc/src/person_snd_inproc.cpp delete mode 100644 samples/cpp/person/person_snd_inproc/src/protobuf/animal.proto delete mode 100644 samples/cpp/person/person_snd_inproc/src/protobuf/house.proto delete mode 100644 samples/cpp/person/person_snd_multicast/CMakeLists.txt delete mode 100644 samples/cpp/person/person_snd_multicast/src/protobuf/animal.proto delete mode 100644 samples/cpp/person/person_snd_multicast/src/protobuf/house.proto delete mode 100644 samples/cpp/person/person_snd_multicast/src/protobuf/person.proto delete mode 100644 samples/cpp/person/person_snd_tcp/src/person_snd_tcp.cpp delete mode 100644 samples/cpp/person/person_snd_tcp/src/protobuf/animal.proto delete mode 100644 samples/cpp/person/person_snd_tcp/src/protobuf/house.proto delete mode 100644 samples/cpp/person/person_snd_tcp/src/protobuf/person.proto rename samples/cpp/{event/event_rec => pubsub/binary/binary_rec}/CMakeLists.txt (89%) rename samples/cpp/{binary/binary_rec_cb/src/binary_rec_cb.cpp => pubsub/binary/binary_rec/src/binary_rec.cpp} (97%) rename samples/cpp/{ => pubsub}/binary/binary_snd/CMakeLists.txt (98%) rename samples/cpp/{ => pubsub}/binary/binary_snd/src/binary_snd.cpp (100%) rename samples/cpp/{pingpong => pubsub/binary}/ping/CMakeLists.txt (98%) rename samples/cpp/{pingpong => pubsub/binary}/ping/src/ping.cpp (92%) rename samples/cpp/{pingpong => pubsub/binary}/pong/CMakeLists.txt (98%) rename samples/cpp/{pingpong => pubsub/binary}/pong/src/pong.cpp (91%) rename samples/cpp/{person/person_rec_events => pubsub/protobuf/person_events_rec}/CMakeLists.txt (85%) rename samples/cpp/{person/person_rec_events/src/person_rec_events.cpp => pubsub/protobuf/person_events_rec/src/person_events_rec.cpp} (71%) rename samples/cpp/{multiple/multiple_rec_cb => pubsub/protobuf/person_events_rec}/src/protobuf/animal.proto (100%) rename samples/cpp/{multiple/multiple_rec_cb => pubsub/protobuf/person_events_rec}/src/protobuf/house.proto (100%) rename samples/cpp/{multiple/multiple_rec_cb => pubsub/protobuf/person_events_rec}/src/protobuf/person.proto (100%) rename samples/cpp/{person/person_snd_tcp => pubsub/protobuf/person_events_snd}/CMakeLists.txt (85%) rename samples/cpp/{person/person_snd_events/src/person_snd_events.cpp => pubsub/protobuf/person_events_snd/src/person_events_snd.cpp} (77%) rename samples/cpp/{multiple/multiple_snd => pubsub/protobuf/person_events_snd}/src/protobuf/animal.proto (100%) rename samples/cpp/{multiple/multiple_snd => pubsub/protobuf/person_events_snd}/src/protobuf/house.proto (100%) rename samples/cpp/{multiple/multiple_snd => pubsub/protobuf/person_events_snd}/src/protobuf/person.proto (100%) rename samples/cpp/{person/person_snd_dyn => pubsub/protobuf/person_loopback}/CMakeLists.txt (86%) rename samples/cpp/{person/person_snd_multicast/src/person_snd_multicast.cpp => pubsub/protobuf/person_loopback/src/person_loopback.cpp} (58%) rename samples/cpp/{person/person_rec => pubsub/protobuf/person_loopback}/src/protobuf/animal.proto (100%) rename samples/cpp/{person/person_rec => pubsub/protobuf/person_loopback}/src/protobuf/house.proto (100%) rename samples/cpp/{person/person_rec => pubsub/protobuf/person_loopback}/src/protobuf/person.proto (100%) rename samples/cpp/{person => pubsub/protobuf}/person_rec/CMakeLists.txt (98%) rename samples/cpp/{person => pubsub/protobuf}/person_rec/src/person_rec.cpp (100%) rename samples/cpp/{person/person_rec_events => pubsub/protobuf/person_rec}/src/protobuf/animal.proto (100%) rename samples/cpp/{person/person_rec_events => pubsub/protobuf/person_rec}/src/protobuf/house.proto (100%) rename samples/cpp/{person/person_rec_events => pubsub/protobuf/person_rec}/src/protobuf/person.proto (100%) rename samples/cpp/{person => pubsub/protobuf}/person_snd/CMakeLists.txt (98%) rename samples/cpp/{person => pubsub/protobuf}/person_snd/src/person_snd.cpp (100%) rename samples/cpp/{person/person_rec_lambda_in_class => pubsub/protobuf/person_snd}/src/protobuf/animal.proto (100%) rename samples/cpp/{person/person_rec_lambda_in_class => pubsub/protobuf/person_snd}/src/protobuf/house.proto (100%) rename samples/cpp/{person/person_rec_lambda_in_class => pubsub/protobuf/person_snd}/src/protobuf/person.proto (100%) rename samples/cpp/{misc/proto_dyn_json => pubsub/protobuf/proto_dyn_json_rec}/CMakeLists.txt (89%) rename samples/cpp/{misc/proto_dyn_json/src/proto_dyn_json.cpp => pubsub/protobuf/proto_dyn_json_rec/src/proto_dyn_json_rec.cpp} (100%) rename samples/cpp/{misc/proto_dyn => pubsub/protobuf/proto_dyn_rec}/CMakeLists.txt (90%) rename samples/cpp/{misc/proto_dyn/src/proto_dyn.cpp => pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp} (100%) rename samples/cpp/{minimal => pubsub/string}/minimal_rec/CMakeLists.txt (98%) rename samples/cpp/{minimal => pubsub/string}/minimal_rec/src/minimal_rec.cpp (100%) rename samples/cpp/{minimal => pubsub/string}/minimal_rec_cb/CMakeLists.txt (98%) rename samples/cpp/{minimal => pubsub/string}/minimal_rec_cb/src/minimal_rec_cb.cpp (100%) rename samples/cpp/{minimal => pubsub/string}/minimal_snd/CMakeLists.txt (98%) rename samples/cpp/{minimal => pubsub/string}/minimal_snd/src/minimal_snd.cpp (94%) delete mode 100644 samples/cpp/services/ecalplayer_client/CMakeLists.txt delete mode 100644 samples/cpp/services/ecalplayer_client/src/ecalplayer_client.cpp delete mode 100644 samples/cpp/services/ecalplayer_gui_client/CMakeLists.txt delete mode 100644 samples/cpp/services/ecalplayer_gui_client/src/ecalplayer_gui_client.cpp delete mode 100644 samples/cpp/services/ecalplayer_gui_client/src/main.cpp delete mode 100644 samples/cpp/services/ecalplayer_gui_client/src/main_window.ui delete mode 100644 samples/cpp/services/ecalsys_client/src/ecalsys_client.cpp delete mode 100644 samples/cpp/services/ping_client_dyn/CMakeLists.txt delete mode 100644 samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp delete mode 100644 samples/cpp/services/ping_client_dyn/src/proto_json_conv.cpp delete mode 100644 samples/cpp/services/ping_client_dyn/src/proto_json_conv.h delete mode 100644 samples/cpp/services/rec_client_service_cli/CMakeLists.txt delete mode 100644 samples/cpp/services/rec_client_service_cli/src/ecalrecorder_client.cpp delete mode 100644 samples/cpp/services/rec_client_service_gui/CMakeLists.txt delete mode 100644 samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.cpp delete mode 100644 samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.h delete mode 100644 samples/cpp/services/rec_client_service_gui/src/MainWindow.ui delete mode 100644 samples/cpp/services/rec_client_service_gui/src/main.cpp delete mode 100644 samples/cpp/services/rec_server_service_gui/CMakeLists.txt delete mode 100644 samples/cpp/services/rec_server_service_gui/src/main.cpp delete mode 100644 samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.cpp delete mode 100644 samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.h delete mode 100644 samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.ui create mode 100644 src/core/CMakeLists.txt rename {ecal => src}/core/cfg/CMakeLists.txt (100%) create mode 100644 src/core/cfg/ecal.ini rename {ecal => src}/core/include/ecal/cimpl/ecal_callback_cimpl.h (76%) rename {ecal => src}/core/include/ecal/cimpl/ecal_client_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_core_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_init_cimpl.h (94%) rename {ecal => src}/core/include/ecal/cimpl/ecal_log_cimpl.h (74%) rename {ecal => src}/core/include/ecal/cimpl/ecal_monitoring_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_process_cimpl.h (69%) create mode 100644 src/core/include/ecal/cimpl/ecal_publisher_cimpl.h rename {ecal => src}/core/include/ecal/cimpl/ecal_server_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_service_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_service_info_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_subscriber_cimpl.h (76%) rename {ecal => src}/core/include/ecal/cimpl/ecal_time_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_timer_cimpl.h (100%) rename {ecal => src}/core/include/ecal/cimpl/ecal_tlayer_cimpl.h (98%) rename {ecal => src}/core/include/ecal/cimpl/ecal_util_cimpl.h (86%) rename {ecal => src}/core/include/ecal/ecal.h (85%) rename {ecal => src}/core/include/ecal/ecal_callback.h (52%) rename {ecal => src}/core/include/ecal/ecal_client.h (71%) rename {ecal => src}/core/include/ecal/ecal_config.h (94%) rename {ecal => src}/core/include/ecal/ecal_core.h (100%) create mode 100644 src/core/include/ecal/ecal_deprecate.h rename {ecal => src}/core/include/ecal/ecal_init.h (100%) rename {ecal => src}/core/include/ecal/ecal_log.h (70%) rename {ecal => src}/core/include/ecal/ecal_log_level.h (91%) rename {ecal => src}/core/include/ecal/ecal_monitoring.h (65%) rename {ecal => src}/core/include/ecal/ecal_os.h (100%) create mode 100644 src/core/include/ecal/ecal_payload_writer.h rename {ecal => src}/core/include/ecal/ecal_process.h (78%) rename {ecal => src}/core/include/ecal/ecal_process_mode.h (100%) rename {ecal => src}/core/include/ecal/ecal_process_severity.h (100%) create mode 100644 src/core/include/ecal/ecal_publisher.h rename {ecal => src}/core/include/ecal/ecal_server.h (72%) rename {ecal => src}/core/include/ecal/ecal_service_info.h (99%) rename {ecal => src}/core/include/ecal/ecal_subscriber.h (63%) rename {ecal => src}/core/include/ecal/ecal_time.h (100%) rename {ecal => src}/core/include/ecal/ecal_timer.h (78%) rename {ecal => src}/core/include/ecal/ecal_tlayer.h (91%) create mode 100644 src/core/include/ecal/ecal_types.h rename {ecal => src}/core/include/ecal/ecal_util.h (58%) rename {ecal => src}/core/include/ecal/ecalc.h (90%) rename ecal/core/src/sys_usage.h => src/core/include/ecal/ecalc_types.h (67%) rename {ecal => src}/core/include/ecal/msg/dynamic.h (100%) rename {ecal => src}/core/include/ecal/msg/protobuf/client.h (95%) create mode 100644 src/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h rename {ecal => src}/core/include/ecal/msg/protobuf/dynamic_publisher.h (85%) rename {ecal => src}/core/include/ecal/msg/protobuf/dynamic_subscriber.h (87%) create mode 100644 src/core/include/ecal/msg/protobuf/ecal_proto_dyn.h rename {lib/ecal_protobuf/include/ecal => src/core/include/ecal/msg}/protobuf/ecal_proto_hlp.h (97%) create mode 100644 src/core/include/ecal/msg/protobuf/publisher.h rename {ecal => src}/core/include/ecal/msg/protobuf/server.h (97%) rename {ecal => src}/core/include/ecal/msg/protobuf/subscriber.h (70%) rename {ecal => src}/core/include/ecal/msg/publisher.h (52%) rename {ecal => src}/core/include/ecal/msg/string/publisher.h (65%) rename {ecal => src}/core/include/ecal/msg/string/subscriber.h (69%) rename {ecal => src}/core/include/ecal/msg/subscriber.h (64%) create mode 100644 src/core/include/ecal/types/monitoring.h rename {ecal/core/src => src/core/src/config}/ecal_config.cpp (89%) rename {ecal/core/src => src/core/src/config}/ecal_config_reader.cpp (85%) rename {ecal/core/src => src/core/src/config}/ecal_config_reader.h (97%) rename {ecal/core/src => src/core/src/config}/ecal_config_reader_hlp.h (100%) rename {ecal => src}/core/src/ecal.cpp (84%) rename {ecal => src}/core/src/ecal_def.h (71%) create mode 100644 src/core/src/ecal_def_ini.h create mode 100644 src/core/src/ecal_defs.h.in create mode 100644 src/core/src/ecal_descgate.cpp rename {ecal => src}/core/src/ecal_descgate.h (67%) rename {ecal => src}/core/src/ecal_event.cpp (87%) rename {ecal/core/include/ecal => src/core/src}/ecal_event.h (64%) rename {ecal/core/include/ecal => src/core/src}/ecal_eventhandle.h (93%) rename {ecal => src}/core/src/ecal_global_accessors.cpp (72%) rename {ecal => src}/core/src/ecal_global_accessors.h (85%) rename {ecal => src}/core/src/ecal_globals.cpp (68%) rename {ecal => src}/core/src/ecal_globals.h (79%) rename {ecal => src}/core/src/ecal_process.cpp (89%) rename {ecal => src}/core/src/ecal_process_stub.cpp (100%) rename {ecal => src}/core/src/ecal_process_stub.h (100%) create mode 100644 src/core/src/ecal_sample_to_topicinfo.h create mode 100644 src/core/src/ecal_util.cpp rename {ecal => src}/core/src/ecal_win_main.h (98%) create mode 100644 src/core/src/ecalc.cpp rename {ecal/core/src/io => src/core/src/io/mtx}/ecal_named_mutex.cpp (100%) rename {ecal/core/src/io => src/core/src/io/mtx}/ecal_named_mutex.h (100%) rename {ecal/core/src/io => src/core/src/io/mtx}/ecal_named_mutex_base.h (97%) rename {ecal/core/src/io => src/core/src/io/mtx}/linux/ecal_named_mutex_impl.cpp (100%) rename {ecal/core/src/io => src/core/src/io/mtx}/linux/ecal_named_mutex_impl.h (97%) rename {ecal/core/src/io => src/core/src/io/mtx}/linux/ecal_named_mutex_robust_clocklock_impl.cpp (100%) rename {ecal/core/src/io => src/core/src/io/mtx}/linux/ecal_named_mutex_robust_clocklock_impl.h (97%) rename {ecal/core/src/io => src/core/src/io/mtx}/win32/ecal_named_mutex_impl.cpp (96%) rename {ecal/core/src/io => src/core/src/io/mtx}/win32/ecal_named_mutex_impl.h (97%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile.cpp (79%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile.h (91%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_db.cpp (88%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_db.h (96%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_header.h (98%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_info.h (72%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_naming.cpp (96%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_naming.h (100%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_os.h (100%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_pool.cpp (75%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_pool.h (76%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_sync.cpp (79%) rename {ecal/core/src/io => src/core/src/io/shm}/ecal_memfile_sync.h (81%) rename {ecal/core/src/io => src/core/src/io/shm}/linux/ecal_memfile_os.cpp (87%) rename {ecal/core/src/io => src/core/src/io/shm}/win32/ecal_memfile_os.cpp (99%) create mode 100644 src/core/src/io/udp/ecal_udp_configurations.cpp create mode 100644 src/core/src/io/udp/ecal_udp_configurations.h create mode 100644 src/core/src/io/udp/ecal_udp_sample_receiver.cpp create mode 100644 src/core/src/io/udp/ecal_udp_sample_receiver.h create mode 100644 src/core/src/io/udp/ecal_udp_sample_sender.cpp rename ecal/core/src/readwrite/ecal_writer_iceoryx.h => src/core/src/io/udp/ecal_udp_sample_sender.h (58%) rename ecal/core/src/topic2mcast.h => src/core/src/io/udp/ecal_udp_topic2mcast.h (95%) rename {ecal/core/src/io => src/core/src/io/udp/fragmentation}/msg_type.h (52%) create mode 100644 src/core/src/io/udp/fragmentation/rcv_fragments.cpp create mode 100644 src/core/src/io/udp/fragmentation/rcv_fragments.h create mode 100644 src/core/src/io/udp/fragmentation/snd_fragments.cpp rename ecal/core/src/io/udp_configurations.h => src/core/src/io/udp/fragmentation/snd_fragments.h (66%) create mode 100644 src/core/src/io/udp/sendreceive/linux/socket_os.h create mode 100644 src/core/src/io/udp/sendreceive/udp_receiver.cpp create mode 100644 src/core/src/io/udp/sendreceive/udp_receiver.h create mode 100644 src/core/src/io/udp/sendreceive/udp_receiver_asio.cpp rename {ecal/core/src/io => src/core/src/io/udp/sendreceive}/udp_receiver_asio.h (50%) create mode 100644 src/core/src/io/udp/sendreceive/udp_receiver_npcap.cpp rename {ecal/core/src/io => src/core/src/io/udp/sendreceive}/udp_receiver_npcap.h (52%) create mode 100644 src/core/src/io/udp/sendreceive/udp_sender.cpp rename lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_decoder.h => src/core/src/io/udp/sendreceive/udp_sender.h (53%) rename ecal/core/src/ecal_win_socket.h => src/core/src/io/udp/sendreceive/win32/socket_os.h (100%) rename {ecal/core/src => src/core/src/logging}/ecal_log.cpp (59%) rename {ecal/core/src => src/core/src/logging}/ecal_log_impl.cpp (54%) rename {ecal/core/src => src/core/src/logging}/ecal_log_impl.h (57%) rename {ecal/core/src/mon => src/core/src/monitoring}/ecal_monitoring_def.cpp (53%) rename {ecal/core/src/mon => src/core/src/monitoring}/ecal_monitoring_def.h (74%) create mode 100644 src/core/src/monitoring/ecal_monitoring_impl.cpp create mode 100644 src/core/src/monitoring/ecal_monitoring_impl.h create mode 100644 src/core/src/pubsub/ecal_pubgate.cpp rename {ecal => src}/core/src/pubsub/ecal_pubgate.h (63%) create mode 100644 src/core/src/pubsub/ecal_publisher.cpp create mode 100644 src/core/src/pubsub/ecal_subgate.cpp rename {ecal => src}/core/src/pubsub/ecal_subgate.h (59%) rename {ecal => src}/core/src/pubsub/ecal_subscriber.cpp (55%) rename {ecal => src}/core/src/readwrite/ecal_reader.cpp (59%) rename {ecal => src}/core/src/readwrite/ecal_reader.h (68%) rename {ecal => src}/core/src/readwrite/ecal_reader_layer.h (82%) create mode 100644 src/core/src/readwrite/ecal_writer.cpp create mode 100644 src/core/src/readwrite/ecal_writer.h rename {ecal => src}/core/src/readwrite/ecal_writer_base.h (57%) create mode 100644 src/core/src/readwrite/ecal_writer_buffer_payload.h rename {ecal => src}/core/src/readwrite/ecal_writer_data.h (91%) rename {ecal => src}/core/src/readwrite/ecal_writer_info.h (83%) create mode 100644 src/core/src/readwrite/shm/ecal_reader_shm.cpp rename {ecal/core/src/readwrite => src/core/src/readwrite/shm}/ecal_reader_shm.h (88%) create mode 100644 src/core/src/readwrite/shm/ecal_writer_shm.cpp rename {ecal/core/src/readwrite => src/core/src/readwrite/shm}/ecal_writer_shm.h (75%) rename {ecal/core/src/readwrite => src/core/src/readwrite/tcp}/ecal_reader_tcp.cpp (61%) rename {ecal/core/src/readwrite => src/core/src/readwrite/tcp}/ecal_reader_tcp.h (78%) rename {ecal/core/src/readwrite => src/core/src/readwrite/tcp}/ecal_tcp_pubsub_logger.h (100%) rename {ecal/core/src/readwrite => src/core/src/readwrite/tcp}/ecal_writer_tcp.cpp (50%) rename {ecal/core/src/readwrite => src/core/src/readwrite/tcp}/ecal_writer_tcp.h (73%) create mode 100644 src/core/src/readwrite/udp/ecal_reader_udp_mc.cpp rename {ecal/core/src/readwrite => src/core/src/readwrite/udp}/ecal_reader_udp_mc.h (64%) create mode 100644 src/core/src/readwrite/udp/ecal_writer_udp_mc.cpp rename ecal/core/src/readwrite/ecal_writer_inproc.h => src/core/src/readwrite/udp/ecal_writer_udp_mc.h (73%) create mode 100644 src/core/src/registration/ecal_registration_provider.cpp create mode 100644 src/core/src/registration/ecal_registration_provider.h create mode 100644 src/core/src/registration/ecal_registration_receiver.cpp create mode 100644 src/core/src/registration/ecal_registration_receiver.h create mode 100644 src/core/src/registration/ecal_registration_receiver_shm.cpp create mode 100644 src/core/src/registration/ecal_registration_receiver_shm.h rename {ecal/core/src/io => src/core/src/registration/shm}/ecal_memfile_broadcast.cpp (99%) rename {ecal/core/src/io => src/core/src/registration/shm}/ecal_memfile_broadcast.h (98%) rename {ecal/core/src/io => src/core/src/registration/shm}/ecal_memfile_broadcast_reader.cpp (99%) rename {ecal/core/src/io => src/core/src/registration/shm}/ecal_memfile_broadcast_reader.h (74%) rename {ecal/core/src/io => src/core/src/registration/shm}/ecal_memfile_broadcast_writer.cpp (97%) rename {ecal/core/src/io => src/core/src/registration/shm}/ecal_memfile_broadcast_writer.h (100%) rename {ecal/core/src/io => src/core/src/registration/shm}/relocatable_circular_queue.h (100%) create mode 100644 src/core/src/serialization/ecal_serialize_common.cpp create mode 100644 src/core/src/serialization/ecal_serialize_common.h create mode 100644 src/core/src/serialization/ecal_serialize_logging.cpp create mode 100644 src/core/src/serialization/ecal_serialize_logging.h create mode 100644 src/core/src/serialization/ecal_serialize_monitoring.cpp rename ecal/core/src/io/snd_raw_buffer.h => src/core/src/serialization/ecal_serialize_monitoring.h (60%) create mode 100644 src/core/src/serialization/ecal_serialize_sample_payload.cpp rename ecal/core/src/io/snd_sample.h => src/core/src/serialization/ecal_serialize_sample_payload.h (60%) create mode 100644 src/core/src/serialization/ecal_serialize_sample_registration.cpp create mode 100644 src/core/src/serialization/ecal_serialize_sample_registration.h create mode 100644 src/core/src/serialization/ecal_serialize_service.cpp create mode 100644 src/core/src/serialization/ecal_serialize_service.h create mode 100644 src/core/src/serialization/ecal_struct_logging.h rename ecal/core/src/io/udp_receiver.h => src/core/src/serialization/ecal_struct_sample_common.h (57%) create mode 100644 src/core/src/serialization/ecal_struct_sample_payload.h create mode 100644 src/core/src/serialization/ecal_struct_sample_registration.h create mode 100644 src/core/src/serialization/ecal_struct_service.h create mode 100644 src/core/src/serialization/nanopb/ecal.pb.c create mode 100644 src/core/src/serialization/nanopb/ecal.pb.h create mode 100644 src/core/src/serialization/nanopb/host.pb.c create mode 100644 src/core/src/serialization/nanopb/host.pb.h create mode 100644 src/core/src/serialization/nanopb/layer.pb.c create mode 100644 src/core/src/serialization/nanopb/layer.pb.h create mode 100644 src/core/src/serialization/nanopb/logging.pb.c create mode 100644 src/core/src/serialization/nanopb/logging.pb.h create mode 100644 src/core/src/serialization/nanopb/monitoring.pb.c create mode 100644 src/core/src/serialization/nanopb/monitoring.pb.h create mode 100644 src/core/src/serialization/nanopb/nanopb/pb.h create mode 100644 src/core/src/serialization/nanopb/nanopb/pb_common.c create mode 100644 src/core/src/serialization/nanopb/nanopb/pb_common.h create mode 100644 src/core/src/serialization/nanopb/nanopb/pb_decode.c create mode 100644 src/core/src/serialization/nanopb/nanopb/pb_decode.h create mode 100644 src/core/src/serialization/nanopb/nanopb/pb_encode.c create mode 100644 src/core/src/serialization/nanopb/nanopb/pb_encode.h create mode 100644 src/core/src/serialization/nanopb/process.pb.c create mode 100644 src/core/src/serialization/nanopb/process.pb.h create mode 100644 src/core/src/serialization/nanopb/service.pb.c create mode 100644 src/core/src/serialization/nanopb/service.pb.h create mode 100644 src/core/src/serialization/nanopb/topic.pb.c create mode 100644 src/core/src/serialization/nanopb/topic.pb.h rename {ecal => src}/core/src/service/ecal_clientgate.cpp (60%) rename {ecal => src}/core/src/service/ecal_clientgate.h (80%) rename {ecal => src}/core/src/service/ecal_service_client.cpp (84%) create mode 100644 src/core/src/service/ecal_service_client_impl.cpp rename {ecal => src}/core/src/service/ecal_service_client_impl.h (56%) rename {ecal => src}/core/src/service/ecal_service_server.cpp (85%) create mode 100644 src/core/src/service/ecal_service_server_impl.cpp rename {ecal => src}/core/src/service/ecal_service_server_impl.h (53%) create mode 100644 src/core/src/service/ecal_service_singleton_manager.cpp create mode 100644 src/core/src/service/ecal_service_singleton_manager.h rename {ecal => src}/core/src/service/ecal_servicegate.cpp (64%) rename {ecal => src}/core/src/service/ecal_servicegate.h (78%) create mode 100644 src/core/src/time/ecal_time.cpp rename {ecal/core/src => src/core/src/time}/ecal_timegate.cpp (94%) rename {ecal/core/src => src/core/src/time}/ecal_timegate.h (98%) rename {ecal/core/src => src/core/src/time}/ecal_timer.cpp (91%) rename {ecal/core/src/custom_tclap => src/core/src/util}/advanced_tclap_output.cpp (91%) rename {ecal/core/src/custom_tclap => src/core/src/util}/advanced_tclap_output.h (94%) rename {ecal/core/src => src/core/src/util}/ecal_expmap.h (69%) create mode 100644 src/core/src/util/ecal_thread.h rename {ecal/core/src => src/core/src/util}/getenvvar.h (99%) rename {ecal => src}/core/src/win32/dll/dllmain.cpp (100%) rename {ecal => src}/core/src/win32/dll/ecal.rc (100%) rename {ecal => src}/core/src/win32/dll/resource.h (100%) create mode 100644 src/protobuf/ecal.proto rename samples/cpp/person/person_snd_dyn/src/protobuf/house.proto => src/protobuf/host.proto (71%) create mode 100644 src/protobuf/layer.proto rename ecal/core_pb/src/ecal/core/pb/monitoring.proto => src/protobuf/logging.proto (62%) rename samples/cpp/person/person_snd_inproc/src/protobuf/person.proto => src/protobuf/monitoring.proto (61%) create mode 100644 src/protobuf/process.proto create mode 100644 src/protobuf/service.proto create mode 100644 src/protobuf/topic.proto rename {samples/cpp/misc/timer => src/service}/CMakeLists.txt (59%) create mode 100644 src/service/Readme.md create mode 100644 src/service/ecal_service/CMakeLists.txt create mode 100644 src/service/ecal_service/include/ecal/service/client_manager.h create mode 100644 src/service/ecal_service/include/ecal/service/client_session.h rename lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_maximum_array_dimensions.h => src/service/ecal_service/include/ecal/service/client_session_types.h (65%) create mode 100644 src/service/ecal_service/include/ecal/service/error.h create mode 100644 src/service/ecal_service/include/ecal/service/logger.h create mode 100644 src/service/ecal_service/include/ecal/service/server.h create mode 100644 src/service/ecal_service/include/ecal/service/server_manager.h rename ecal/core/src/io/udp_sender.h => src/service/ecal_service/include/ecal/service/server_session_types.h (59%) rename ecal/core/src/ecal_process.h => src/service/ecal_service/include/ecal/service/state.h (70%) create mode 100644 src/service/ecal_service/src/client_manager.cpp create mode 100644 src/service/ecal_service/src/client_session.cpp create mode 100644 src/service/ecal_service/src/client_session_impl_base.h create mode 100644 src/service/ecal_service/src/client_session_impl_v0.cpp create mode 100644 src/service/ecal_service/src/client_session_impl_v0.h create mode 100644 src/service/ecal_service/src/client_session_impl_v1.cpp create mode 100644 src/service/ecal_service/src/client_session_impl_v1.h create mode 100644 src/service/ecal_service/src/condition_variable_signaler.h create mode 100644 src/service/ecal_service/src/log_defs.h create mode 100644 src/service/ecal_service/src/log_helpers.h create mode 100644 src/service/ecal_service/src/protocol_layout.h create mode 100644 src/service/ecal_service/src/protocol_v0.cpp create mode 100644 src/service/ecal_service/src/protocol_v0.h create mode 100644 src/service/ecal_service/src/protocol_v1.cpp create mode 100644 src/service/ecal_service/src/protocol_v1.h create mode 100644 src/service/ecal_service/src/server.cpp create mode 100644 src/service/ecal_service/src/server_impl.cpp create mode 100644 src/service/ecal_service/src/server_impl.h create mode 100644 src/service/ecal_service/src/server_manager.cpp create mode 100644 src/service/ecal_service/src/server_session_impl_base.h create mode 100644 src/service/ecal_service/src/server_session_impl_v0.cpp create mode 100644 src/service/ecal_service/src/server_session_impl_v0.h create mode 100644 src/service/ecal_service/src/server_session_impl_v1.cpp create mode 100644 src/service/ecal_service/src/server_session_impl_v1.h create mode 100644 src/service/sample/CMakeLists.txt create mode 100644 src/service/sample/src/main.cpp create mode 100644 src/service/test/CMakeLists.txt create mode 100644 src/service/test/src/atomic_signalable.h create mode 100644 src/service/test/src/ecal_tcp_service_test.cpp rename {samples/cpp/misc => src}/time/CMakeLists.txt (64%) create mode 100644 src/time/include/dynamic_sleeper.h create mode 100644 src/time/include/ecaltime.h create mode 100644 src/time/linuxptp/CMakeLists.txt create mode 100644 src/time/linuxptp/src/clock.h rename samples/cpp/misc/timer/src/timer.cpp => src/time/linuxptp/src/config/config.h (53%) create mode 100644 src/time/linuxptp/src/config/ecaltime.ini rename {ecal/core => src/time/linuxptp}/src/convert_utf.cpp (100%) rename {ecal/core => src/time/linuxptp}/src/convert_utf.h (100%) create mode 100644 src/time/linuxptp/src/ecal_time_linuxptp.cpp create mode 100644 src/time/linuxptp/src/ecal_time_linuxptp.h create mode 100644 src/time/linuxptp/src/ecaltime.cpp create mode 100644 src/time/localtime/CMakeLists.txt create mode 100644 src/time/localtime/src/dllmain.cpp create mode 100644 src/time/localtime/src/ecaltime.cpp rename {lib/ecal_protobuf => src/utils}/CMakeLists.txt (50%) create mode 100644 src/utils/include/ecal_utils/command_line.h create mode 100644 src/utils/include/ecal_utils/ecal_utils.h create mode 100644 src/utils/include/ecal_utils/filesystem.h create mode 100644 src/utils/include/ecal_utils/portable_endian.h create mode 100644 src/utils/include/ecal_utils/str_convert.h create mode 100644 src/utils/include/ecal_utils/string.h create mode 100644 src/utils/src/command_line.cpp create mode 100644 src/utils/src/filesystem.cpp create mode 100644 src/utils/src/str_convert.cpp delete mode 100644 testing/ecal/clientserver_test/src/dummy.cpp delete mode 100644 testing/ecal/pubsub_inproc_test/src/pubsub_inproc_test.cpp delete mode 100644 testing/ecal/pubsub_proto_test/src/protobuf/animal.proto delete mode 100644 testing/ecal/pubsub_proto_test/src/protobuf/house.proto delete mode 100644 testing/ecal/pubsub_proto_test/src/protobuf/person.proto delete mode 100644 testing/lib/ecal_protobuf/dynproto_test/CMakeLists.txt delete mode 100644 testing/lib/ecal_protobuf/dynproto_test/src/dynproto_test.cpp delete mode 100644 testing/lib/ecal_protobuf/dynproto_test/src/protobuf/animal.proto delete mode 100644 testing/lib/ecal_protobuf/dynproto_test/src/protobuf/attitude.proto delete mode 100644 testing/lib/ecal_protobuf/dynproto_test/src/protobuf/house.proto delete mode 100644 testing/lib/ecal_protobuf/dynproto_test/src/protobuf/person.proto delete mode 100644 testing/lib/ecal_protobuf/ecal_proto_test/CMakeLists.txt delete mode 100644 testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/animal.proto delete mode 100644 testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/house.proto delete mode 100644 testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/person.proto delete mode 100644 testing/lib/ecal_protobuf/ecal_proto_test/src/test_filters.cpp delete mode 100644 testing/lib/ecal_protobuf/ecal_proto_test/src/test_maximum_array_dimensions.cpp delete mode 100644 testing/lib/ecal_protobuf/ecal_proto_test/src/test_proto_dyn_decoder.cpp create mode 100644 tests/CMakeLists.txt rename {testing/ecal/clientserver_test => tests/clientserver_proto_test}/CMakeLists.txt (90%) create mode 100644 tests/clientserver_proto_test/src/clientserver_test_proto.cpp rename {testing/ecal/clientserver_test => tests/clientserver_proto_test}/src/protobuf/math.proto (100%) rename {testing/ecal/clientserver_test => tests/clientserver_proto_test}/src/protobuf/ping.proto (100%) rename {testing/ecal/event_test => tests/clientserver_test}/CMakeLists.txt (79%) create mode 100644 tests/clientserver_test/src/atomic_signalable.h rename {testing/ecal => tests}/clientserver_test/src/clientserver_test.cpp (64%) rename {testing/ecal => tests}/core_test/CMakeLists.txt (99%) rename {testing/ecal => tests}/core_test/src/core_test.cpp (77%) rename {testing/ecal => tests}/expmap_test/CMakeLists.txt (99%) rename {testing/ecal => tests}/expmap_test/src/expmap_test.cpp (71%) rename {testing/ecal => tests}/io_memfile_test/CMakeLists.txt (72%) rename testing/ecal/event_test/src/event_test.cpp => tests/io_memfile_test/src/memfile_naming_test.cpp (63%) rename {testing/ecal => tests}/io_memfile_test/src/memfile_test.cpp (90%) rename {testing/ecal => tests}/pubsub_proto_test/CMakeLists.txt (97%) create mode 100644 tests/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp rename {testing/ecal => tests}/pubsub_proto_test/src/proto_publisher_test.cpp (97%) rename {testing/ecal => tests}/pubsub_proto_test/src/proto_subscriber_test.cpp (98%) rename {samples/cpp/person/person_snd => tests/pubsub_proto_test}/src/protobuf/animal.proto (100%) rename {samples/cpp/person/person_snd => tests/pubsub_proto_test}/src/protobuf/house.proto (100%) rename {samples/cpp/person/person_snd => tests/pubsub_proto_test}/src/protobuf/person.proto (100%) rename {testing/ecal => tests}/pubsub_test/CMakeLists.txt (99%) rename {testing/ecal => tests}/pubsub_test/src/pubsub_receive_test.cpp (98%) rename {testing/ecal => tests}/pubsub_test/src/pubsub_test.cpp (53%) create mode 100644 tests/serialization_test/CMakeLists.txt create mode 100644 tests/serialization_test/src/common_generate.cpp rename ecal/core/src/io/udp_init.h => tests/serialization_test/src/logging_compare.cpp (58%) create mode 100644 tests/serialization_test/src/logging_generate.cpp create mode 100644 tests/serialization_test/src/logging_serialization_test.cpp create mode 100644 tests/serialization_test/src/monitoring_compare.cpp create mode 100644 tests/serialization_test/src/monitoring_generate.cpp create mode 100644 tests/serialization_test/src/monitoring_serialization_test.cpp create mode 100644 tests/serialization_test/src/payload_compare.cpp create mode 100644 tests/serialization_test/src/payload_generate.cpp create mode 100644 tests/serialization_test/src/payload_serialization_test.cpp create mode 100644 tests/serialization_test/src/registration_compare.cpp create mode 100644 tests/serialization_test/src/registration_generate.cpp create mode 100644 tests/serialization_test/src/registration_serialization_test.cpp create mode 100644 tests/serialization_test/src/service_compare.cpp create mode 100644 tests/serialization_test/src/service_generate.cpp create mode 100644 tests/serialization_test/src/service_serialization_test.cpp rename {testing/ecal => tests}/topic2mcast_test/CMakeLists.txt (99%) rename {testing/ecal => tests}/topic2mcast_test/src/topic2mcast_test.cpp (98%) rename {testing/ecal/pubsub_inproc_test => tests/util_test}/CMakeLists.txt (80%) create mode 100644 tests/util_test/src/util_test.cpp delete mode 160000 thirdparty/asio create mode 100644 thirdparty/asio/Modules/Findasio.cmake create mode 100644 thirdparty/asio/build-asio.cmake delete mode 100644 thirdparty/build-asio.cmake delete mode 100644 thirdparty/build-cmakefunctions.cmake delete mode 100644 thirdparty/build-recycle.cmake delete mode 100644 thirdparty/build-simpleini.cmake delete mode 100644 thirdparty/build-tclap.cmake delete mode 100644 thirdparty/build-tcp_pubsub.cmake create mode 100644 thirdparty/cmakefunctions/Modules/FindCMakeFunctions.cmake create mode 100644 thirdparty/cmakefunctions/build-cmakefunctions.cmake rename thirdparty/{ => cmakefunctions}/cmake_functions/.gitignore (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/CMakeLists.txt (97%) rename thirdparty/{ => cmakefunctions}/cmake_functions/cmake/CMakeFunctionsConfig.cmake.in (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/cmake/cpack_variables.cmake (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/cmake_functions.cmake (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/git/git_revision_information.cmake (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/msvc_helper/msvc_macros.cmake (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/protoc_functions/protoc_generate_cpp.cmake (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/protoc_functions/protoc_generate_files.cmake (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/protoc_functions/protoc_generate_python.cmake (100%) rename thirdparty/{ => cmakefunctions}/cmake_functions/target_definitions/targets_protobuf.cmake (100%) delete mode 160000 thirdparty/ecal-utils delete mode 160000 thirdparty/googletest create mode 100644 thirdparty/gtest/Modules/FindGTest.cmake rename thirdparty/{ => gtest}/build-gtest.cmake (75%) delete mode 160000 thirdparty/protobuf create mode 100644 thirdparty/protobuf/Modules/FindProtobuf.cmake rename thirdparty/{ => protobuf}/build-protobuf.cmake (91%) delete mode 160000 thirdparty/recycle create mode 100644 thirdparty/recycle/Modules/Findrecycle.cmake create mode 100644 thirdparty/recycle/build-recycle.cmake delete mode 160000 thirdparty/simpleini create mode 100644 thirdparty/simpleini/Modules/Findsimpleini.cmake create mode 100644 thirdparty/simpleini/build-simpleini.cmake delete mode 160000 thirdparty/tclap create mode 100644 thirdparty/tclap/Modules/Findtclap.cmake create mode 100644 thirdparty/tclap/build-tclap.cmake delete mode 160000 thirdparty/tcp_pubsub create mode 100644 thirdparty/tcp_pubsub/Modules/Findtcp_pubsub.cmake create mode 100644 thirdparty/tcp_pubsub/build-tcp_pubsub.cmake delete mode 160000 thirdparty/udpcap create mode 100644 thirdparty/udpcap/Modules/Findudpcap.cmake rename thirdparty/{ => udpcap}/build-udpcap.cmake (83%) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 8f85be1..ec6e6da 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -4,18 +4,242 @@ on: push: pull_request: branches: - - master + - main jobs: build-macos: runs-on: macos-latest + strategy: + matrix: + build_configuration: + ######################################## + - name: "default" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "ON" + ECAL_CORE_COMMAND_LINE: "ON" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "ON" + ECAL_CORE_TIMEPLUGIN: "ON" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "full" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "ON" + ECAL_CORE_COMMAND_LINE: "ON" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "ON" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "ON" + ECAL_CORE_TIMEPLUGIN: "ON" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_proto" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_udp_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "OFF" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "OFF" + ECAL_CORE_TRANSPORT_SHM: "OFF" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_tcp_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "OFF" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "OFF" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_shm_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "OFF" + ECAL_CORE_TRANSPORT_TCP: "OFF" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "client_server" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "OFF" + ECAL_CORE_SUBSCRIBER: "OFF" + ECAL_CORE_SERVICE: "ON" + 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" + ######################################## + - name: "client_server_proto" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "OFF" + ECAL_CORE_SUBSCRIBER: "OFF" + ECAL_CORE_SERVICE: "ON" + 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" + ######################################## + - name: "monitoring_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "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: Install Dependencies - run: brew install ninja protobuf + run: brew install ninja protobuf pkg-config - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: 'true' fetch-depth: 0 @@ -25,34 +249,35 @@ jobs: mkdir "${{ runner.workspace }}/_build" cd "${{ runner.workspace }}/_build" cmake $GITHUB_WORKSPACE -G "Ninja" \ - -DHAS_CAPNPROTO=OFF \ - -DBUILD_SAMPLES=ON \ - -DBUILD_ECAL_TESTS=ON \ - -DECAL_LAYER_ICEORYX=OFF \ - -DECAL_INSTALL_SAMPLE_SOURCES=ON \ - -DECAL_JOIN_MULTICAST_TWICE=OFF \ - -DECAL_NPCAP_SUPPORT=OFF \ - -DECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS=ON \ - -DECAL_THIRDPARTY_BUILD_PROTOBUF=OFF \ - -DECAL_THIRDPARTY_BUILD_GTEST=ON \ - -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ - -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_CXX_STANDARD=14 + -DECAL_CORE_BUILD_SAMPLES=${{ matrix.build_configuration.ECAL_CORE_BUILD_SAMPLES }} \ + -DECAL_CORE_BUILD_SAMPLES_PROTOBUF=${{ matrix.build_configuration.ECAL_CORE_BUILD_SAMPLES_PROTOBUF }} \ + -DECAL_CORE_BUILD_TESTS=${{ matrix.build_configuration.ECAL_CORE_BUILD_TESTS }} \ + -DECAL_CORE_BUILD_TESTS_PROTOBUF=${{ matrix.build_configuration.ECAL_CORE_BUILD_TESTS_PROTOBUF }} \ + -DECAL_CORE_CONFIG_INIFILE=${{ matrix.build_configuration.ECAL_CORE_CONFIG_INIFILE }} \ + -DECAL_CORE_COMMAND_LINE=${{ matrix.build_configuration.ECAL_CORE_COMMAND_LINE }} \ + -DECAL_CORE_REGISTRATION=${{ matrix.build_configuration.ECAL_CORE_REGISTRATION }} \ + -DECAL_CORE_REGISTRATION_SHM=${{ matrix.build_configuration.ECAL_CORE_REGISTRATION_SHM }} \ + -DECAL_CORE_MONITORING=${{ matrix.build_configuration.ECAL_CORE_MONITORING }} \ + -DECAL_CORE_PUBLISHER=${{ matrix.build_configuration.ECAL_CORE_PUBLISHER }} \ + -DECAL_CORE_SUBSCRIBER=${{ matrix.build_configuration.ECAL_CORE_SUBSCRIBER }} \ + -DECAL_CORE_SERVICE=${{ matrix.build_configuration.ECAL_CORE_SERVICE }} \ + -DECAL_CORE_TIMEPLUGIN=${{ matrix.build_configuration.ECAL_CORE_TIMEPLUGIN }} \ + -DECAL_CORE_TRANSPORT_UDP=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_UDP }} \ + -DECAL_CORE_TRANSPORT_TCP=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_TCP }} \ + -DECAL_CORE_TRANSPORT_SHM=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_SHM }} \ + -DECAL_CORE_NPCAP_SUPPORT=${{ matrix.build_configuration.ECAL_CORE_NPCAP_SUPPORT }} \ + -DBUILD_SHARED_LIBS=${{ matrix.build_configuration.BUILD_SHARED_LIBS }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_configuration.CMAKE_BUILD_TYPE }} \ + -DCMAKE_CXX_STANDARD=17 \ + -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON sudo mkdir /etc/ecal - sudo cp "$GITHUB_WORKSPACE/ecal/core/cfg/ecal.ini" /etc/ecal + sudo cp "$GITHUB_WORKSPACE/src/core/cfg/ecal.ini" /etc/ecal shell: bash - name: Build Release run: cmake --build . --config Release working-directory: ${{ runner.workspace }}/_build - - name: Pack - run: cpack -G DragNDrop - working-directory: ${{ runner.workspace }}/_build - - - name: Upload DMG - uses: actions/upload-artifact@v2 - with: - name: macos-dmg - path: ${{ runner.workspace }}/_build/_deploy/*.dmg +# - name: Run Tests +# run: ctest -V +# working-directory: ${{ runner.workspace }}/_build diff --git a/.github/workflows/build-ubuntu-20.yml b/.github/workflows/build-ubuntu-20.yml deleted file mode 100644 index f66ee86..0000000 --- a/.github/workflows/build-ubuntu-20.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: Build Ubuntu 20.04 - -on: - push: - pull_request: - branches: - - master - -jobs: - build-ubuntu: - runs-on: ubuntu-20.04 - - steps: - - name: Install Dependencies - run: | - sudo apt update - sudo apt-get install ninja-build libprotobuf-dev libprotoc-dev protobuf-compiler - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: 'true' - fetch-depth: 0 - - - name: CMake - run: | - export CC=/usr/bin/gcc-9 - export CXX=/usr/bin/g++-9 - mkdir "${{ runner.workspace }}/_build" - cd "${{ runner.workspace }}/_build" - cmake $GITHUB_WORKSPACE -G "Ninja" \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DHAS_CAPNPROTO=OFF \ - -DBUILD_SAMPLES=ON \ - -DBUILD_ECAL_TESTS=ON \ - -DECAL_LAYER_ICEORYX=OFF \ - -DECAL_INSTALL_SAMPLE_SOURCES=OFF \ - -DECAL_JOIN_MULTICAST_TWICE=OFF \ - -DECAL_NPCAP_SUPPORT=OFF \ - -DECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS=ON \ - -DECAL_THIRDPARTY_BUILD_PROTOBUF=OFF \ - -DECAL_THIRDPARTY_BUILD_GTEST=ON \ - -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ - -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_SYSCONFDIR=/etc \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_INSTALL_LOCALSTATEDIR=/var \ - -DCMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu - sudo mkdir /etc/ecal - sudo cp "$GITHUB_WORKSPACE/ecal/core/cfg/ecal.ini" /etc/ecal - shell: bash - - - name: Build Release - run: cmake --build . --config Release - working-directory: ${{ runner.workspace }}/_build - - - name: Run Tests - run: ctest -V - working-directory: ${{ runner.workspace }}/_build - - - name: Pack - run: cpack -G DEB - working-directory: ${{ runner.workspace }}/_build - - - name: Upload Debian - uses: actions/upload-artifact@v3 - with: - name: ubuntu-debian - path: ${{ runner.workspace }}/_build/_deploy/*.deb - - #--------------------------------------------------------------------------- - # GNU tar - Excluding Some Files - # https://www.gnu.org/software/tar/manual/html_node/exclude.html - # the emtpy '.git/' drectory is required, it marks the root directory -# - name: 'Create a tarball' -# run: | -# cd ${{ runner.workspace }} -# tar --exclude-vcs-ignore \ -# --exclude='bin' \ -# --exclude='doc' \ -# --exclude='_build/_CPack_Packages' \ -# --exclude='_build/_deploy' \ -# --exclude='_build/lib' \ -# --exclude='*.deb' \ -# --exclude='*.a' \ -# --exclude='*.o' \ -# --exclude='*.so' \ -# --exclude=ecal-core/.git/* \ -# -czf ecal-core.tar.gz ecal-core/ _build/ -# du -sh ecal-core.tar.gz - - # https://github.com/actions/upload-artifact -# - name: Upload the whole directory -# uses: actions/upload-artifact@v3 -# with: -# name: ecal-dir -# path: ${{ runner.workspace }}/ecal-core.tar.gz - -# call-clang-tidy: -# if: github.event_name == 'pull_request' -# needs: build-ubuntu -# uses: ./.github/workflows/run-clang-tidy.yml diff --git a/.github/workflows/build-ubuntu-22-capnproto.yml b/.github/workflows/build-ubuntu-22-capnproto.yml deleted file mode 100644 index de105e9..0000000 --- a/.github/workflows/build-ubuntu-22-capnproto.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Build Ubuntu 22.04 (CapnProto) - -on: - push: - pull_request: - branches: - - master - -jobs: - build-ubuntu: - runs-on: ubuntu-22.04 - - steps: - - name: Install Dependencies - run: | - sudo apt update - sudo apt-get install ninja-build libprotobuf-dev libprotoc-dev protobuf-compiler - - - name: Install Cap'n Proto - run: | - mkdir "${{ runner.workspace }}/capnp" - cd "${{ runner.workspace }}/capnp" - curl -O https://capnproto.org/capnproto-c++-0.9.0.tar.gz - tar zxf capnproto-c++-0.9.0.tar.gz - cd capnproto-c++-0.9.0 - ./configure - make -j - sudo make install - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: 'true' - fetch-depth: 0 - - - name: CMake - run: | - export CC=/usr/bin/gcc-11 - export CXX=/usr/bin/g++-11 - mkdir "${{ runner.workspace }}/_build" - cd "${{ runner.workspace }}/_build" - cmake $GITHUB_WORKSPACE -G "Ninja" \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DHAS_CAPNPROTO=ON \ - -DBUILD_SAMPLES=ON \ - -DBUILD_ECAL_TESTS=ON \ - -DECAL_LAYER_ICEORYX=OFF \ - -DECAL_INSTALL_SAMPLE_SOURCES=OFF \ - -DECAL_JOIN_MULTICAST_TWICE=OFF \ - -DECAL_NPCAP_SUPPORT=OFF \ - -DECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS=ON \ - -DECAL_THIRDPARTY_BUILD_PROTOBUF=OFF \ - -DECAL_THIRDPARTY_BUILD_GTEST=ON \ - -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ - -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_SYSCONFDIR=/etc \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_INSTALL_LOCALSTATEDIR=/var \ - -DCMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu - sudo mkdir /etc/ecal - sudo cp "$GITHUB_WORKSPACE/ecal/core/cfg/ecal.ini" /etc/ecal - shell: bash - - - name: Build Release - run: cmake --build . --config Release - working-directory: ${{ runner.workspace }}/_build - - - name: Run Tests - run: ctest -V - working-directory: ${{ runner.workspace }}/_build - - - name: Pack - run: cpack -G DEB - working-directory: ${{ runner.workspace }}/_build - - - name: Upload Debian - uses: actions/upload-artifact@v3 - with: - name: ubuntu-debian - path: ${{ runner.workspace }}/_build/_deploy/*.deb diff --git a/.github/workflows/build-ubuntu-22.yml b/.github/workflows/build-ubuntu-22.yml deleted file mode 100644 index 2e72895..0000000 --- a/.github/workflows/build-ubuntu-22.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Build Ubuntu 22.04 - -on: - push: - pull_request: - branches: - - master - -jobs: - build-ubuntu: - runs-on: ubuntu-22.04 - - steps: - - name: Install Dependencies - run: | - sudo apt update - sudo apt-get install ninja-build libprotobuf-dev libprotoc-dev protobuf-compiler - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: 'true' - fetch-depth: 0 - - - name: CMake - run: | - export CC=/usr/bin/gcc-11 - export CXX=/usr/bin/g++-11 - mkdir "${{ runner.workspace }}/_build" - cd "${{ runner.workspace }}/_build" - cmake $GITHUB_WORKSPACE -G "Ninja" \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DHAS_CAPNPROTO=OFF \ - -DBUILD_SAMPLES=ON \ - -DBUILD_ECAL_TESTS=ON \ - -DECAL_LAYER_ICEORYX=OFF \ - -DECAL_INSTALL_SAMPLE_SOURCES=OFF \ - -DECAL_JOIN_MULTICAST_TWICE=OFF \ - -DECAL_NPCAP_SUPPORT=OFF \ - -DECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS=ON \ - -DECAL_THIRDPARTY_BUILD_PROTOBUF=OFF \ - -DECAL_THIRDPARTY_BUILD_GTEST=ON \ - -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ - -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_SYSCONFDIR=/etc \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_INSTALL_LOCALSTATEDIR=/var \ - -DCMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu - sudo mkdir /etc/ecal - sudo cp "$GITHUB_WORKSPACE/ecal/core/cfg/ecal.ini" /etc/ecal - shell: bash - - - name: Build Release - run: cmake --build . --config Release - working-directory: ${{ runner.workspace }}/_build - - - name: Run Tests - run: ctest -V - working-directory: ${{ runner.workspace }}/_build - - - name: Pack - run: cpack -G DEB - working-directory: ${{ runner.workspace }}/_build - - - name: Upload Debian - uses: actions/upload-artifact@v3 - with: - name: ubuntu-debian - path: ${{ runner.workspace }}/_build/_deploy/*.deb diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml new file mode 100644 index 0000000..91d28fc --- /dev/null +++ b/.github/workflows/build-ubuntu.yml @@ -0,0 +1,289 @@ +name: Build on Ubuntu + +on: + push: + pull_request: + branches: + - main + +jobs: + build-ubuntu: + strategy: + matrix: + os: [ubuntu-22.04, ubuntu-20.04] + + build_configuration: + ######################################## + - name: "default" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "ON" + ECAL_CORE_COMMAND_LINE: "ON" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "ON" + ECAL_CORE_TIMEPLUGIN: "ON" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "full" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "ON" + ECAL_CORE_COMMAND_LINE: "ON" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "ON" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "ON" + ECAL_CORE_TIMEPLUGIN: "ON" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_proto" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_udp_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "OFF" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "OFF" + ECAL_CORE_TRANSPORT_SHM: "OFF" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_tcp_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "OFF" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "OFF" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_shm_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "OFF" + ECAL_CORE_TRANSPORT_TCP: "OFF" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "client_server" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "OFF" + ECAL_CORE_SUBSCRIBER: "OFF" + ECAL_CORE_SERVICE: "ON" + 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" + ######################################## + - name: "client_server_proto" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "OFF" + ECAL_CORE_SUBSCRIBER: "OFF" + ECAL_CORE_SERVICE: "ON" + 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" + ######################################## + - name: "monitoring_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "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" + + runs-on: ${{ matrix.os }} + + steps: + - name: Install dependencies + run: | + sudo apt update + sudo apt-get install ninja-build libprotobuf-dev libprotoc-dev protobuf-compiler + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 0 + + - name: CMake + run: | + mkdir "${{ runner.workspace }}/_build" + cd "${{ runner.workspace }}/_build" + cmake $GITHUB_WORKSPACE -G "Ninja" \ + -DECAL_CORE_BUILD_SAMPLES=${{ matrix.build_configuration.ECAL_CORE_BUILD_SAMPLES }} \ + -DECAL_CORE_BUILD_SAMPLES_PROTOBUF=${{ matrix.build_configuration.ECAL_CORE_BUILD_SAMPLES_PROTOBUF }} \ + -DECAL_CORE_BUILD_TESTS=${{ matrix.build_configuration.ECAL_CORE_BUILD_TESTS }} \ + -DECAL_CORE_BUILD_TESTS_PROTOBUF=${{ matrix.build_configuration.ECAL_CORE_BUILD_TESTS_PROTOBUF }} \ + -DECAL_CORE_CONFIG_INIFILE=${{ matrix.build_configuration.ECAL_CORE_CONFIG_INIFILE }} \ + -DECAL_CORE_COMMAND_LINE=${{ matrix.build_configuration.ECAL_CORE_COMMAND_LINE }} \ + -DECAL_CORE_REGISTRATION=${{ matrix.build_configuration.ECAL_CORE_REGISTRATION }} \ + -DECAL_CORE_REGISTRATION_SHM=${{ matrix.build_configuration.ECAL_CORE_REGISTRATION_SHM }} \ + -DECAL_CORE_MONITORING=${{ matrix.build_configuration.ECAL_CORE_MONITORING }} \ + -DECAL_CORE_PUBLISHER=${{ matrix.build_configuration.ECAL_CORE_PUBLISHER }} \ + -DECAL_CORE_SUBSCRIBER=${{ matrix.build_configuration.ECAL_CORE_SUBSCRIBER }} \ + -DECAL_CORE_SERVICE=${{ matrix.build_configuration.ECAL_CORE_SERVICE }} \ + -DECAL_CORE_TIMEPLUGIN=${{ matrix.build_configuration.ECAL_CORE_TIMEPLUGIN }} \ + -DECAL_CORE_TRANSPORT_UDP=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_UDP }} \ + -DECAL_CORE_TRANSPORT_TCP=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_TCP }} \ + -DECAL_CORE_TRANSPORT_SHM=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_SHM }} \ + -DECAL_CORE_NPCAP_SUPPORT=${{ matrix.build_configuration.ECAL_CORE_NPCAP_SUPPORT }} \ + -DBUILD_SHARED_LIBS=${{ matrix.build_configuration.BUILD_SHARED_LIBS }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_configuration.CMAKE_BUILD_TYPE }} \ + -DCMAKE_INSTALL_SYSCONFDIR=/etc \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LOCALSTATEDIR=/var \ + -DCMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu + sudo mkdir /etc/ecal + sudo cp "$GITHUB_WORKSPACE/src/core/cfg/ecal.ini" /etc/ecal + shell: bash + + - name: Build Release + run: cmake --build . --config Release + working-directory: ${{ runner.workspace }}/_build + + - name: Run Tests + run: ctest -V + working-directory: ${{ runner.workspace }}/_build diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 457b4a1..fd462fe 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -1,124 +1,278 @@ name: Build Windows Server 2019 -on: +on: push: pull_request: branches: - - master + - main jobs: build-windows: runs-on: windows-2019 + strategy: + matrix: + build_configuration: + ######################################## + - name: "default" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "ON" + ECAL_CORE_COMMAND_LINE: "ON" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "ON" + ECAL_CORE_TIMEPLUGIN: "ON" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "full" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "ON" + ECAL_CORE_COMMAND_LINE: "ON" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "ON" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "ON" + ECAL_CORE_TIMEPLUGIN: "ON" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "ON" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_proto" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_udp_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "OFF" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "ON" + ECAL_CORE_TRANSPORT_TCP: "OFF" + ECAL_CORE_TRANSPORT_SHM: "OFF" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_tcp_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "OFF" + ECAL_CORE_TRANSPORT_TCP: "ON" + ECAL_CORE_TRANSPORT_SHM: "OFF" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "pubsub_shm_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "OFF" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "ON" + ECAL_CORE_SUBSCRIBER: "ON" + ECAL_CORE_SERVICE: "OFF" + ECAL_CORE_TIMEPLUGIN: "OFF" + ECAL_CORE_TRANSPORT_UDP: "OFF" + ECAL_CORE_TRANSPORT_TCP: "OFF" + ECAL_CORE_TRANSPORT_SHM: "ON" + ECAL_CORE_NPCAP_SUPPORT: "OFF" + BUILD_SHARED_LIBS: "OFF" + CMAKE_BUILD_TYPE: "Release" + ######################################## + - name: "client_server" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "OFF" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "OFF" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "OFF" + ECAL_CORE_SUBSCRIBER: "OFF" + ECAL_CORE_SERVICE: "ON" + 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" + ######################################## + - name: "client_server_proto" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "OFF" + ECAL_CORE_COMMAND_LINE: "OFF" + ECAL_CORE_REGISTRATION: "ON" + ECAL_CORE_REGISTRATION_SHM: "ON" + ECAL_CORE_MONITORING: "OFF" + ECAL_CORE_PUBLISHER: "OFF" + ECAL_CORE_SUBSCRIBER: "OFF" + ECAL_CORE_SERVICE: "ON" + 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" + ######################################## + - name: "monitoring_only" + ######################################## + ECAL_CORE_BUILD_SAMPLES: "ON" + ECAL_CORE_BUILD_SAMPLES_PROTOBUF: "ON" + ECAL_CORE_BUILD_TESTS: "ON" + ECAL_CORE_BUILD_TESTS_PROTOBUF: "ON" + ECAL_CORE_CONFIG_INIFILE: "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 - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: 'true' fetch-depth: 0 - - - name: Download NPCAP - run: | - cd %GITHUB_WORKSPACE% - powershell -Command "& 'build\windows\download_npcap.ps1'" - shell: cmd - - - name: CMake Debug - run: | - mkdir "${{ runner.workspace }}\_build\sdk" - cd "${{ runner.workspace }}/_build\sdk" - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v140 ^ - -DBUILD_SAMPLES=OFF ^ - -DBUILD_ECAL_TESTS=OFF ^ - -DECAL_LAYER_ICEORYX=OFF ^ - -DECAL_INSTALL_SAMPLE_SOURCES=OFF ^ - -DECAL_JOIN_MULTICAST_TWICE=OFF ^ - -DECAL_NPCAP_SUPPORT=ON ^ - -DECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS=ON ^ - -DECAL_THIRDPARTY_BUILD_PROTOBUF=ON ^ - -DECAL_THIRDPARTY_BUILD_GTEST=ON ^ - -DECAL_THIRDPARTY_BUILD_RECYCLE=ON ^ - -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON ^ - -DECAL_THIRDPARTY_BUILD_UDPCAP=ON ^ - -DBUILD_SHARED_LIBS=OFF ^ - -DCPACK_PACK_WITH_INNOSETUP=OFF ^ - -DCMAKE_BUILD_TYPE=Debug - shell: cmd - - name: CMake Complete + - name: CMake run: | - mkdir "${{ runner.workspace }}\_build\complete" - cd "${{ runner.workspace }}/_build\complete" - cmake %GITHUB_WORKSPACE% -G "Visual Studio 16 2019" -A x64 -T v140 ^ - -DBUILD_SAMPLES=ON ^ - -DBUILD_ECAL_TESTS=ON ^ - -DECAL_LAYER_ICEORYX=OFF ^ - -DECAL_INSTALL_SAMPLE_SOURCES=ON ^ - -DECAL_JOIN_MULTICAST_TWICE=OFF ^ - -DECAL_NPCAP_SUPPORT=ON ^ - -DECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS=ON ^ - -DECAL_THIRDPARTY_BUILD_PROTOBUF=ON ^ - -DECAL_THIRDPARTY_BUILD_GTEST=ON ^ - -DECAL_THIRDPARTY_BUILD_RECYCLE=ON ^ - -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON ^ - -DECAL_THIRDPARTY_BUILD_UDPCAP=ON ^ - -DBUILD_SHARED_LIBS=OFF ^ - -DCPACK_PACK_WITH_INNOSETUP=ON ^ - -DCMAKE_BUILD_TYPE=Release + mkdir "${{ runner.workspace }}\_build\" + cd "${{ runner.workspace }}/_build" + cmake %GITHUB_WORKSPACE% -A x64 ^ + -DECAL_CORE_BUILD_SAMPLES=${{ matrix.build_configuration.ECAL_CORE_BUILD_SAMPLES }} ^ + -DECAL_CORE_BUILD_SAMPLES_PROTOBUF=${{ matrix.build_configuration.ECAL_CORE_BUILD_SAMPLES_PROTOBUF }} ^ + -DECAL_CORE_BUILD_TESTS=${{ matrix.build_configuration.ECAL_CORE_BUILD_TESTS }} ^ + -DECAL_CORE_BUILD_TESTS_PROTOBUF=${{ matrix.build_configuration.ECAL_CORE_BUILD_TESTS_PROTOBUF }} ^ + -DECAL_CORE_CONFIG_INIFILE=${{ matrix.build_configuration.ECAL_CORE_CONFIG_INIFILE }} ^ + -DECAL_CORE_COMMAND_LINE=${{ matrix.build_configuration.ECAL_CORE_COMMAND_LINE }} ^ + -DECAL_CORE_REGISTRATION=${{ matrix.build_configuration.ECAL_CORE_REGISTRATION }} ^ + -DECAL_CORE_REGISTRATION_SHM=${{ matrix.build_configuration.ECAL_CORE_REGISTRATION_SHM }} ^ + -DECAL_CORE_MONITORING=${{ matrix.build_configuration.ECAL_CORE_MONITORING }} ^ + -DECAL_CORE_PUBLISHER=${{ matrix.build_configuration.ECAL_CORE_PUBLISHER }} ^ + -DECAL_CORE_SUBSCRIBER=${{ matrix.build_configuration.ECAL_CORE_SUBSCRIBER }} ^ + -DECAL_CORE_SERVICE=${{ matrix.build_configuration.ECAL_CORE_SERVICE }} ^ + -DECAL_CORE_TIMEPLUGIN=${{ matrix.build_configuration.ECAL_CORE_TIMEPLUGIN }} ^ + -DECAL_CORE_TRANSPORT_UDP=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_UDP }} ^ + -DECAL_CORE_TRANSPORT_TCP=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_TCP }} ^ + -DECAL_CORE_TRANSPORT_SHM=${{ matrix.build_configuration.ECAL_CORE_TRANSPORT_SHM }} ^ + -DECAL_CORE_NPCAP_SUPPORT=${{ matrix.build_configuration.ECAL_CORE_NPCAP_SUPPORT }} ^ + -DBUILD_SHARED_LIBS=${{ matrix.build_configuration.BUILD_SHARED_LIBS }} ^ + -DCMAKE_BUILD_TYPE=${{ matrix.build_configuration.CMAKE_BUILD_TYPE }} mkdir "%ALLUSERSPROFILE%\eCAL" - copy "%GITHUB_WORKSPACE%\ecal\core\cfg\ecal.ini" "%ALLUSERSPROFILE%\eCAL" + copy "%GITHUB_WORKSPACE%\src\core\cfg\ecal.ini" "%ALLUSERSPROFILE%\eCAL" shell: cmd - - name: Build Debug - run: cmake --build . --config Debug - working-directory: ${{ runner.workspace }}/_build/sdk - - name: Build Release run: cmake --build . --config Release - working-directory: ${{ runner.workspace }}/_build/complete + working-directory: ${{ runner.workspace }}/_build - name: Run Tests run: ctest -C Release -V - working-directory: ${{ runner.workspace }}/_build/complete - - - name: Pack SDK - run: cpack -C Debug - working-directory: ${{ runner.workspace }}/_build/sdk - - - name: Pack Complete - run: cpack -C Release - working-directory: ${{ runner.workspace }}/_build/complete - - - name: Detect certificate - id: cert - run: | - if [[ -n "${{ secrets.CERT_BODY }}" \ - && -n "${{ secrets.CERT_PSWD }}" \ - && -n "${{ secrets.CERT_ALGO }}" \ - && -n "${{ secrets.CERT_HASH }}" ]] - then - echo "ATTENTION: a certificate is available" - echo "::set-output name=is_available::true" - else - echo "WARNING: A certificate is not available" - echo "::set-output name=is_available::false" - fi - shell: bash - - # https://github.com/OrhanKupusoglu/code-sign-action - - name: Sign the installer - if: fromJSON(steps.cert.outputs.is_available) - uses: OrhanKupusoglu/code-sign-action@v5.5.1 - with: - cert_body: ${{ secrets.CERT_BODY }} - cert_pswd: ${{ secrets.CERT_PSWD }} - cert_algo: ${{ secrets.CERT_ALGO }} - cert_hash: ${{ secrets.CERT_HASH }} - folder: ${{ runner.workspace }}/_build/complete/_deploy - debug: true - - - name: Upload Windows setup - uses: actions/upload-artifact@v2 - with: - name: windows-setup - path: ${{ runner.workspace }}/_build/complete/_deploy/*.exe + working-directory: ${{ runner.workspace }}/_build diff --git a/.github/workflows/run-clang-tidy.yml b/.github/workflows/run-clang-tidy.yml deleted file mode 100644 index deae79a..0000000 --- a/.github/workflows/run-clang-tidy.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Run clang-tidy - -on: - workflow_call: - -jobs: - clang-tidy-scan: - runs-on: ubuntu-20.04 - continue-on-error: true - - steps: - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install ninja-build doxygen graphviz libcurl4-openssl-dev libprotobuf-dev libprotoc-dev protobuf-compiler libhdf5-dev qt5-default - sudo apt-get install clang-tidy - - - name: Display version info - run: | - echo $(g++ --version) - echo $(clang --version) - echo $(cmake --version) - echo $(clang-tidy --version) - - # https://github.com/actions/download-artifact - - name: Download the built directory - uses: actions/download-artifact@v3 - with: - name: ecal-dir - - # replace the working directory - - name: Extract the tarball archive - run: mv ecal.tar.gz ../. && cd .. && rm -rf ecal && tar -xzf ecal.tar.gz && cd ecal - - # https://github.com/Ana06/get-changed-files - - name: Detect the changeset - id: changed_files - uses: Ana06/get-changed-files@v2.1.0 - - # see: ./build.sh --help - - name: Run clang-tidy on the changeset - id: suffix - run: | - build_linux/clang-tidy/build.sh -a -b ../../../_build -f ${{ steps.changed_files.outputs.added_modified }} - - # https://github.com/actions/upload-artifact - # If make or clang-tidy is never called, artifact upload will fail (no log files) with just a warning. - - name: Archive logs as artifact - uses: actions/upload-artifact@v3 - with: - name: clang_tidy_log_${{ steps.suffix.outputs.timestamp }} - path: ~/work/ecal/_build/clang_tidy_log_*.txt - if-no-files-found: warn - - - name: Detect number of clang-tidy warnings - id: num_warnings - run: | - NUM_WARNINGS=0 - NUM_LOG_FILES=$(ls ~/work/ecal/_build/clang_tidy_log_*.txt 2> /dev/null | wc -l) - if [[ ${NUM_LOG_FILES} -gt 0 ]] - then - NUM_WARNINGS=$(grep -oE "\w+\.\w+:\w+:\w+:\s?warning:\s?.*" -- ~/work/ecal/_build/clang_tidy_log_*.txt | wc -l) - fi - echo "::set-output name=value::${NUM_WARNINGS}" - - # https://github.com/actions/github-script - - name: Check clang-tidy warnings - if: ${{ steps.num_warnings.outputs.value > 0 }} - uses: actions/github-script@v6 - with: - script: | - core.setFailed('number of clang-tidy warnings: ${{ steps.num_warnings.outputs.value}}') diff --git a/.gitignore b/.gitignore index 259148f..c4211a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,32 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app +# Windows files +Thumbs.db + +# CMake build folders +/_0* +/_build* +/_setup +/_vs_out* + +# Binary libraries +/thirdparty/npcap/ + +# autogenerated files +/ecal/include/ecal/ecal_defs.h + +# Doxygen output +/doc/*.tmp +/doc/*.chm +/doc/html/* + +# CLion project files +/.idea +cmake-build-* + +# macOS +.DS_Store + +# Visual Studio 2015/2017 cache/options directory +.vs/ + +# Visual Studio code +*.vscode diff --git a/.gitmodules b/.gitmodules index 8cd63c6..de593d4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,27 +1,24 @@ -[submodule "thirdparty/asio"] - path = thirdparty/asio +[submodule "thirdparty/asio/asio"] + path = thirdparty/asio/asio url = https://github.com/chriskohlhoff/asio.git -[submodule "thirdparty/googletest"] - path = thirdparty/googletest +[submodule "thirdparty/gtest/googletest"] + path = thirdparty/gtest/googletest url = https://github.com/google/googletest.git -[submodule "thirdparty/protobuf"] - path = thirdparty/protobuf +[submodule "thirdparty/protobuf/protobuf"] + path = thirdparty/protobuf/protobuf url = https://github.com/protocolbuffers/protobuf.git -[submodule "thirdparty/recycle"] - path = thirdparty/recycle - url = https://github.com/steinwurf/recycle.git -[submodule "thirdparty/simpleini"] - path = thirdparty/simpleini +[submodule "thirdparty/simpleini/simpleini"] + path = thirdparty/simpleini/simpleini url = https://github.com/brofield/simpleini.git -[submodule "thirdparty/tclap"] - path = thirdparty/tclap +[submodule "thirdparty/tclap/tclap"] + path = thirdparty/tclap/tclap url = https://github.com/xguerin/tclap.git -[submodule "thirdparty/tcp_pubsub"] - path = thirdparty/tcp_pubsub - url = https://github.com/eclipse-ecal/tcp_pubsub.git -[submodule "thirdparty/ecal-utils"] - path = thirdparty/ecal-utils - url = https://github.com/eclipse-ecal/ecal-utils.git -[submodule "thirdparty/udpcap"] - path = thirdparty/udpcap +[submodule "thirdparty/udpcap/udpcap"] + path = thirdparty/udpcap/udpcap url = https://github.com/eclipse-ecal/udpcap +[submodule "thirdparty/tcp_pubsub/tcp_pubsub"] + path = thirdparty/tcp_pubsub/tcp_pubsub + url = https://github.com/continental/tcp_pubsub.git +[submodule "thirdparty/recycle/recycle"] + path = thirdparty/recycle/recycle + url = https://github.com/steinwurf/recycle.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e264fcf..08e7068 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,12 @@ include(CMakeDependentOption) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) + +set(eCAL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) + +if(MSVC) + set(CMAKE_GENERATOR_PLATFORM x64) +endif() project(eCAL) set(ECAL_PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}") @@ -34,25 +40,16 @@ if(MSVC) message(STATUS "MSVC detected - Adding flags") set_property(GLOBAL PROPERTY USE_FOLDERS ON) add_definitions(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4") # support Windows 7 and newer add_definitions(-D_WIN32_WINNT=0x0601) endif() -# -------------------------------------------------------- -# set mingw specific options -# -------------------------------------------------------- -if(MINGW) - add_definitions(-std=c++14 -DWINVER=0x0601) -endif() - # -------------------------------------------------------- # set gcc specific options # -------------------------------------------------------- if(UNIX) message(STATUS "GCC detected - Adding flags") - set(CMAKE_CXX_STANDARD 14) - add_definitions(-Wall -Wextra -std=c++14) + add_definitions(-Wall -Wextra) set(ATOMIC_TEST_CODE " #include @@ -66,44 +63,82 @@ if(UNIX) endif () endif() -# -------------------------------------------------------- -# command line build options -# use it that way cmake .. -DBUILD_SAMPLES=ON -# -------------------------------------------------------- -option(HAS_CAPNPROTO "Platform supports Cap'n Proto library" OFF) - -option(BUILD_SAMPLES "Build the eCAL samples" ON) -option(BUILD_ECAL_TESTS "Build the eCAL google tests" ON) - -option(ECAL_JOIN_MULTICAST_TWICE "Specific Multicast Network Bug Workaround" OFF) -option(ECAL_NPCAP_SUPPORT "Enable the eCAL Npcap Receiver (i.e. the Win10 performance fix)" OFF) -option(ECAL_USE_CLOCKLOCK_MUTEX "Use native mutex with monotonic clock (requires glibc >= 2.30)" OFF) +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +# configurable build options +# command line usage: cmake .. -DBUILD_SAMPLES=ON -DBUILD_ECAL_TESTS=OFF -DECAL_CORE_CONFIG_INIFILE=OFF +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +# additional builds (adapt to your needs) +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +option(ECAL_CORE_BUILD_SAMPLES "Build the eCAL samples" ON) +option(ECAL_CORE_BUILD_SAMPLES_PROTOBUF "Build the eCAL samples using google protobuf message definition" OFF) +option(ECAL_CORE_BUILD_TESTS "Build the eCAL google tests" ON) +option(ECAL_CORE_BUILD_TESTS_PROTOBUF "Build the eCAL google tests using google protobuf message definition" OFF) + +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +# core internal feature configuration (adapt to your needs) +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +option(ECAL_CORE_CONFIG_INIFILE "Enables to configure eCAL via ecal.ini file" ON) +option(ECAL_CORE_COMMAND_LINE "Enables eCAL application cmd line interfaces" ON) +option(ECAL_CORE_REGISTRATION "Enables the eCAL registration layer" ON) +option(ECAL_CORE_MONITORING "Enables the eCAL monitoring functionality" OFF) +option(ECAL_CORE_PUBLISHER "Enables the eCAL publisher functionality" ON) +option(ECAL_CORE_SUBSCRIBER "Enables the eCAL subscriber functionality" ON) +option(ECAL_CORE_SERVICE "Enables the eCAL server/client functionality" ON) +option(ECAL_CORE_TIMEPLUGIN "Enables the eCAL time plugin functionality" ON) +option(ECAL_CORE_NPCAP_SUPPORT "Enable the eCAL Npcap Receiver (Win10 performance fix for UDP communication)" OFF) + +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +# core registration layer options (change with care) +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +option(ECAL_CORE_REGISTRATION_SHM "Enables the eCAL registration layer over shared memory" ON) + +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +# core transport layer options (change with care) +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +option(ECAL_CORE_TRANSPORT_UDP "Enables the eCAL to transport payload via UDP multicast" ON) +option(ECAL_CORE_TRANSPORT_TCP "Enables the eCAL to transport payload via TCP" OFF) +option(ECAL_CORE_TRANSPORT_SHM "Enables the eCAL to transport payload via local shared memory" ON) + +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +# third party library options (do not change) +# ------------------------------------------------------------------------------------------------------------------------------------------------------------------ +option(ECAL_THIRDPARTY_BUILD_ASIO "Build asio with eCAL" ON) +option(ECAL_THIRDPARTY_BUILD_CMAKEFUNCTIONS "Build CMakeFunctions with eCAL" ON) + +cmake_dependent_option(ECAL_THIRDPARTY_BUILD_GTEST "Build gtest with eCAL" ON "ECAL_CORE_BUILD_TESTS" OFF) +cmake_dependent_option(ECAL_THIRDPARTY_BUILD_TCP_PUBSUB "Build tcp_pubsub library with eCAL" ON "ECAL_CORE_TRANSPORT_TCP" OFF) +cmake_dependent_option(ECAL_THIRDPARTY_BUILD_RECYCLE "Build steinwurf::recylce with eCAL" ON "ECAL_CORE_TRANSPORT_TCP" OFF) +cmake_dependent_option(ECAL_THIRDPARTY_BUILD_SIMPLEINI "Build simpleini with eCAL" ON "ECAL_CORE_CONFIG_INIFILE" OFF) +cmake_dependent_option(ECAL_THIRDPARTY_BUILD_TCLAP "Build tclap library with eCAL" ON "ECAL_CORE_COMMAND_LINE" OFF) -option(ECAL_LAYER_ICEORYX "Use iceoryx shared memory as local communication layer" OFF) - -option(ECAL_INSTALL_SAMPLE_SOURCES "Install the sources of eCAL samples" ON) - -# Set option regarding third party library builds -option(ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS "Build CMakeFunctions with eCAL" ON) -option(ECAL_THIRDPARTY_BUILD_TCP_PUBSUB "Build tcp_pubsub library with eCAL" ON) -option(ECAL_THIRDPARTY_BUILD_RECYCLE "Build steinwurf::recylce with eCAL" ON) -option(ECAL_THIRDPARTY_BUILD_GTEST "Build gtest with eCAL" ON) -option(ECAL_THIRDPARTY_BUILD_UDPCAP "Build udpcap library with eCAL" OFF) if(WIN32) - option(ECAL_THIRDPARTY_BUILD_PROTOBUF "Build protobuf with eCAL" ON) + cmake_dependent_option(ECAL_THIRDPARTY_BUILD_PROTOBUF "Build protobuf with eCAL" OFF "NOT ECAL_CORE_BUILD_SAMPLES_PROTOBUF; NOT ECAL_CORE_BUILD_TESTS_PROTOBUF" ON) else() - option(ECAL_THIRDPARTY_BUILD_PROTOBUF "Build protobuf with eCAL" OFF) + option(ECAL_CORE_NPCAP_SUPPORT "Enable the eCAL Npcap Receiver (i.e. the Win10 performance fix)" OFF) + option(ECAL_THIRDPARTY_BUILD_PROTOBUF "Build protobuf with eCAL" OFF) endif() -option(CPACK_PACK_WITH_INNOSETUP "Create Innosetup installer for the Windows build" ON) +cmake_dependent_option(ECAL_THIRDPARTY_BUILD_UDPCAP "Build udpcap library with eCAL" ON "ECAL_CORE_NPCAP_SUPPORT" OFF) -set(ECAL_BUILD_VERSION "0.0.0" CACHE STRING "Inject a build version if not building from a git repository") - -# there is a CMake issue with testing threading availibility via TEST_RUN -if(${CMAKE_CROSSCOMPILING}) - set(THREADS_PTHREAD_ARG "2" CACHE STRING "Forcibly set by CMakeLists.txt." FORCE) +if(NOT ECAL_CORE_REGISTRATION) + if(ECAL_CORE_TRANSPORT_TCP) + message(STATUS "Switching ECAL_CORE_TRANSPORT_TCP to OFF because registration (ECAL_CORE_REGISTRATION) is not activated") + set(ECAL_CORE_TRANSPORT_TCP OFF) + endif() + if(ECAL_CORE_TRANSPORT_SHM) + message(STATUS "Switching ECAL_CORE_TRANSPORT_SHM to OFF because registration (ECAL_CORE_REGISTRATION) is not activated") + set(ECAL_CORE_TRANSPORT_SHM OFF) + endif() + if(ECAL_CORE_MONITORING) + message(STATUS "Switching ECAL_CORE_MONITORING to OFF because registration (ECAL_CORE_REGISTRATION) is not activated") + set(ECAL_CORE_MONITORING OFF) + endif() endif() +set(ECAL_BUILD_VERSION "0.0.0" CACHE STRING "Inject a build version if not building from a git repository") + include(build_location) # find_project(eCAL calls shall be ignored, eCAL is build as part of the project) @@ -133,41 +168,25 @@ if(MSVC) endif() # This is a list of subprojects, that might be build with eCAL -# according to how options ECAL_BUILD_ are set +# according to how CMake options are set set(possible_subprojects + asio + CMakeFunctions + GTest Protobuf recycle + simpleini + tclap tcp_pubsub udpcap - GTest - CMakeFunctions ) -# We should rename the option, but don't know how to do in in a -# backwards compatible way -set(ECAL_THIRDPARTY_BUILD_CMAKEFUNCTIONS ${ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS}) - -# For each dependency, check if option to build was set or not -# if so append to `as_subproject` list -foreach (dep IN LISTS possible_subprojects) - string(TOUPPER ${dep} dep_upper) - if (ECAL_THIRDPARTY_BUILD_${dep_upper}) - list(APPEND as_subproject ${dep}) - endif () -endforeach() - -macro(find_package) - if(NOT "${ARGV0}" IN_LIST as_subproject) - _find_package(${ARGV}) - endif() -endmacro() - # if a package does need to be build, include the cmake file with build instructions foreach (dep IN LISTS possible_subprojects) string(TOUPPER ${dep} dep_upper) string(TOLOWER ${dep} dep_lower) if (ECAL_THIRDPARTY_BUILD_${dep_upper}) - include(thirdparty/build-${dep_lower}.cmake) + include(thirdparty/${dep_lower}/build-${dep_lower}.cmake) endif () endforeach () @@ -222,9 +241,12 @@ set(eCAL_install_samples_dir ${CMAKE_INSTALL_BINDIR}) set(eCAL_install_samples_src_dir ${CMAKE_INSTALL_DATADIR}/ecal/samples/) set(eCAL_install_tests_dir ${CMAKE_INSTALL_BINDIR}) -set(eCAL_config_dir ${eCAL_BINARY_DIR}/cmake/) +set(eCAL_config_dir ${CMAKE_CURRENT_BINARY_DIR}/_generated/) set(eCAL_config ${eCAL_config_dir}/eCALConfig.cmake) set(eCAL_config_version ${eCAL_config_dir}/eCALConfigVersion.cmake) + +file(MAKE_DIRECTORY ${eCAL_config_dir}) + if(WIN32) set(eCAL_install_config_dir cfg) set(eCAL_install_doc_dir doc) @@ -243,50 +265,48 @@ if(WIN32) endif() # -------------------------------------------------------- -# ecal core internal protobuf +# core # -------------------------------------------------------- -add_subdirectory(ecal/core_pb) +add_subdirectory(src/core) # -------------------------------------------------------- -# ecal core +# core config # -------------------------------------------------------- -add_subdirectory(ecal/core) +add_subdirectory(src/core/cfg) # -------------------------------------------------------- -# custom libs +# services # -------------------------------------------------------- -add_subdirectory(lib/ecal_protobuf) -add_subdirectory(thirdparty/ecal-utils) +if(ECAL_CORE_SERVICE) + add_subdirectory(src/service) +endif() # -------------------------------------------------------- -# samples +# time plugins # -------------------------------------------------------- -if(BUILD_SAMPLES) - add_subdirectory(samples) +if(ECAL_CORE_TIMEPLUGIN) + add_subdirectory(src/time) endif() -# ------------------------------------------------------ -# unit tests # -------------------------------------------------------- -if(BUILD_ECAL_TESTS) - enable_testing() +# utils +# -------------------------------------------------------- +add_subdirectory(src/utils) - #add_subdirectory(testing/ecal/clientserver_test) - add_subdirectory(testing/ecal/core_test) - add_subdirectory(testing/ecal/event_test) - add_subdirectory(testing/ecal/expmap_test) - add_subdirectory(testing/ecal/io_memfile_test) - add_subdirectory(testing/ecal/pubsub_inproc_test) - add_subdirectory(testing/ecal/pubsub_proto_test) - add_subdirectory(testing/ecal/pubsub_test) - add_subdirectory(testing/lib/ecal_protobuf/dynproto_test) - add_subdirectory(testing/lib/ecal_protobuf/ecal_proto_test) +# -------------------------------------------------------- +# samples +# -------------------------------------------------------- +if(ECAL_CORE_BUILD_SAMPLES) + add_subdirectory(samples) endif() # -------------------------------------------------------- -# configs +# tests # -------------------------------------------------------- -add_subdirectory(ecal/core/cfg) +if(ECAL_CORE_BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() # -------------------------------------------------------- # debscripts @@ -330,33 +350,42 @@ install(EXPORT eCALCoreTargets COMPONENT sdk ) -if(ECAL_INSTALL_SAMPLE_SOURCES) - install(DIRECTORY samples/ - DESTINATION ${eCAL_install_samples_src_dir} - ) -endif() - # -------------------------------------------------------- # create package # -------------------------------------------------------- -include(cpack/cpack_variables.cmake) +#include(cpack/cpack_variables.cmake) +message(STATUS "--------------------------------------------------------------------------------") message(STATUS "Build Options:") message(STATUS "--------------------------------------------------------------------------------") message(STATUS "CMAKE_EXPORT_COMPILE_COMMANDS : ${CMAKE_EXPORT_COMPILE_COMMANDS}") -message(STATUS "HAS_CAPNPROTO : ${HAS_CAPNPROTO}") -message(STATUS "BUILD_SAMPLES : ${BUILD_SAMPLES}") -message(STATUS "BUILD_ECAL_TESTS : ${BUILD_ECAL_TESTS}") -message(STATUS "ECAL_LAYER_ICEORYX : ${ECAL_LAYER_ICEORYX}") -message(STATUS "ECAL_INSTALL_SAMPLE_SOURCES : ${ECAL_INSTALL_SAMPLE_SOURCES}") -message(STATUS "ECAL_JOIN_MULTICAST_TWICE : ${ECAL_JOIN_MULTICAST_TWICE}") -message(STATUS "ECAL_NPCAP_SUPPORT : ${ECAL_NPCAP_SUPPORT}") -message(STATUS "ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS : ${ECAL_THIRDPARTY_BUILD_CMAKE_FUNCTIONS}") -message(STATUS "ECAL_THIRDPARTY_BUILD_TCP_PUBSUB : ${ECAL_THIRDPARTY_BUILD_TCP_PUBSUB}") -message(STATUS "ECAL_THIRDPARTY_BUILD_RECYCLE : ${ECAL_THIRDPARTY_BUILD_RECYCLE}") +message(STATUS "--------------------------------------------------------------------------------") +message(STATUS "ECAL_CORE_BUILD_SAMPLES : ${ECAL_CORE_BUILD_SAMPLES}") +message(STATUS "ECAL_CORE_BUILD_SAMPLES_PROTOBUF : ${ECAL_CORE_BUILD_SAMPLES_PROTOBUF}") +message(STATUS "ECAL_CORE_BUILD_TESTS : ${ECAL_CORE_BUILD_TESTS}") +message(STATUS "ECAL_CORE_BUILD_TESTS_PROTOBUF : ${ECAL_CORE_BUILD_TESTS_PROTOBUF}") +message(STATUS "--------------------------------------------------------------------------------") +message(STATUS "ECAL_CORE_CONFIG_INIFILE : ${ECAL_CORE_CONFIG_INIFILE}") +message(STATUS "ECAL_CORE_COMMAND_LINE : ${ECAL_CORE_COMMAND_LINE}") +message(STATUS "ECAL_CORE_REGISTRATION : ${ECAL_CORE_REGISTRATION}") +message(STATUS "ECAL_CORE_MONITORING : ${ECAL_CORE_MONITORING}") +message(STATUS "ECAL_CORE_PUBLISHER : ${ECAL_CORE_PUBLISHER}") +message(STATUS "ECAL_CORE_SUBSCRIBER : ${ECAL_CORE_SUBSCRIBER}") +message(STATUS "ECAL_CORE_SERVICE : ${ECAL_CORE_SERVICE}") +message(STATUS "ECAL_CORE_TIMEPLUGIN : ${ECAL_CORE_TIMEPLUGIN}") +message(STATUS "ECAL_CORE_NPCAP_SUPPORT : ${ECAL_CORE_NPCAP_SUPPORT}") +message(STATUS "--------------------------------------------------------------------------------") +message(STATUS "ECAL_CORE_REGISTRATION_SHM : ${ECAL_CORE_REGISTRATION_SHM}") +message(STATUS "--------------------------------------------------------------------------------") +message(STATUS "ECAL_CORE_TRANSPORT_UDP : ${ECAL_CORE_TRANSPORT_UDP}") +message(STATUS "ECAL_CORE_TRANSPORT_TCP : ${ECAL_CORE_TRANSPORT_TCP}") +message(STATUS "ECAL_CORE_TRANSPORT_SHM : ${ECAL_CORE_TRANSPORT_SHM}") +message(STATUS "--------------------------------------------------------------------------------") +message(STATUS "ECAL_THIRDPARTY_BUILD_ASIO : ${ECAL_THIRDPARTY_BUILD_ASIO}") +message(STATUS "ECAL_THIRDPARTY_BUILD_CMAKEFUNCTIONS : ${ECAL_THIRDPARTY_BUILD_CMAKEFUNCTIONS}") message(STATUS "ECAL_THIRDPARTY_BUILD_GTEST : ${ECAL_THIRDPARTY_BUILD_GTEST}") message(STATUS "ECAL_THIRDPARTY_BUILD_PROTOBUF : ${ECAL_THIRDPARTY_BUILD_PROTOBUF}") +message(STATUS "ECAL_THIRDPARTY_BUILD_SIMPLEINI : ${ECAL_THIRDPARTY_BUILD_SIMPLEINI}") +message(STATUS "ECAL_THIRDPARTY_BUILD_TCLAP : ${ECAL_THIRDPARTY_BUILD_TCLAP}") message(STATUS "ECAL_THIRDPARTY_BUILD_UDPCAP : ${ECAL_THIRDPARTY_BUILD_UDPCAP}") -message(STATUS "CPACK_PACK_WITH_INNOSETUP : ${CPACK_PACK_WITH_INNOSETUP}") - message(STATUS "--------------------------------------------------------------------------------") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f123a65..1bb38b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ How to contribute ================= -This repository is maintained by the Continental Organization. +This repository is maintained by the Eclipse Foundation, Inc. Contributions via pull requests are very welcome! Please discuss a change you wish to make via issue (preferred), email, @@ -44,14 +44,13 @@ besides a technical review: included into the documentation (both docstrings and the docs sources). - The new code and included third party libraries **do not infringe any trademark, patent, copyright, or open source compliance**. -- Please make sure to sign the **Contributor License Agreement** - provided to you by the maintainers upon request. +- Please make sure to sign the [Eclipse Contributor Agreement](https://www.eclipse.org/legal/ECA.php) + and link your GitHub Account to your Eclipse Account. Once we received your pull request, you will get feedback from the maintainers within 10 working days. -Once you have the sign-off of at least one maintainer from the Continental -organization, you may request the second reviewer to merge the Pull Request for -you. +Once you have the sign-off of at least one maintainer you may request the +second reviewer to merge the Pull Request for you. *We are looking forward to your contributions.* diff --git a/DEPENDENCIES.txt b/DEPENDENCIES.txt new file mode 100644 index 0000000..22543cf --- /dev/null +++ b/DEPENDENCIES.txt @@ -0,0 +1,29 @@ +ASIO: + path = thirdparty/asio/asio + url = https://github.com/chriskohlhoff/asio.git + version = 1.29.0 + +Googletest: + path = thirdparty/gtest/googletest + url = https://github.com/google/googletest.git + version = v1.14.0 + +Protobuf: + path = thirdparty/protobuf/protobuf + url = https://github.com/protocolbuffers/protobuf.git + version = v3.11.0 + +Simpleini: + path = thirdparty/simpleini/simpleini + url = https://github.com/brofield/simpleini.git + version = v4.20 + +Tclap: + path = thirdparty/tclap/tclap + url = https://github.com/xguerin/tclap.git + version = v1.2.5 + +Udpcap: + path = thirdparty/udpcap/udpcap + url = https://github.com/eclipse-ecal/udpcap + version = 1.0.2 diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 0000000..dbc5f98 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,83 @@ +This project includes software developed by +Continental Corporation (https://www.continental-automotive.com) + +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. + + +This project has dependencies - list with dependency name and license follows. + +1. Used Licenses (overall) + + - Apache License 2.0 - https://www.apache.org/licenses/LICENSE-2.0 + - Boost Software License - http://www.boost.org/LICENSE_1_0.txt + - BSD 3-Clause License - https://opensource.org/licenses/BSD-3-Clause + - GNU Lesser General Public License v2.1 - https://www.gnu.org/licenses/old-licenses/lgpl-2.1.de.html + - GNU Lesser General Public License v3 - https://www.gnu.org/licenses/lgpl-3.0.de.html + - MIT License - https://opensource.org/licenses/MIT + - LGPL v3 - http://www.gnu.org/licenses/lgpl-3.0.html + - Some libraries like Google Protobuf define their own license model, see Details section + + +2. Details + +2.1 Networking / Transport + +2.1.1 ASIO + - Copyright © 2003-2018 Christopher M. Kohlhoff + - URL: https://think-async.com + - License: Boost Software License - http://www.boost.org/LICENSE_1_0.txt + +2.1.2 NPCAP + - Copyright 2013-2016 by Insecure.Com LLC + - URL: https://nmap.org/npcap/ + - License: Npcap License - https://github.com/nmap/npcap/blob/master/LICENSE + + +2.2 Serialization + +2.2.1 Google Protouf + - Copyright 2008 Google Inc. + - URL: https://developers.google.com/protocol-buffers + - License: https://github.com/google/protobuf/blob/master/LICENSE + + +2.3 Utilities + +2.3.1 SimpleIni + - Copyright (c) 2006-2013 Brodie Thiesfield + - URL: https://github.com/brofield/simpleini + - License: MIT License - https://github.com/brofield/simpleini/blob/master/LICENCE.txt + +2.3.2 TCLAP + - Copyright (c) 2003 Michael E. Smoot + - Copyright (c) 2004 Daniel Aarno + - Copyright (c) 2017 Google Inc. + - URL: http://tclap.sourceforge.net + - License: MIT License - https://opensource.org/licenses/mit-license.php + +2.3.3 convert_utf.cpp/.h + - Copyright 2001-2004 Unicode, Inc. + +2.3.4 modpath.iss + - Copyright (c) Jared Breland + - URL: http://www.legroom.net/software + - License: LGPL 3 + + +2.4 Testing + +2.4.1 GoogleTest + - Copyright 2008 Google Inc. + - URL: https://github.com/google/googletest + - License: BSD 3-Clause License - https://github.com/google/googletest/blob/master/LICENSE.txt diff --git a/README.md b/README.md index 994551d..9844b9c 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,105 @@ -# ecal-core -This is the new ecal "core-only" repository. - -## How to build +# The eCAL Cop (core only project) +[![License](https://img.shields.io/github/license/continental/ecal.svg?style=flat)](LICENSE.txt) -### Install requirements + -#### Windows +The **e**nhanced **C**ommunication **A**bstraction **L**ayer (eCAL) is a middleware that enables scalable, high performance interprocess communication on a single computer node or between different nodes in a computer network. -- [CMake](https://cmake.org/download/) -- [Visual Studio](https://visualstudio.microsoft.com/de/downloads/) -- [Inno Setup](https://jrsoftware.org/isdl.php#stable) +This eCAL core version has an reduced functionality compared to [Eclipse-eCAL](https://github.com/eclipse-ecal/ecal) (see _Differences to Eclipse-eCAL_). The goal of this reduced approach is to create a modular core with clearly defined dependencies depending on the configured features. -#### Ubuntu 20.04 & 22.04 +Visit the eCAL Documentation at 🌐 https://ecal.io for more information. -``` -sudo apt update -sudo apt-get install libprotobuf-dev libprotoc-dev protobuf-compiler -``` +## Differences to Eclipse-eCAL + - communication core only, no additional eco system tools + - C++ and C language support only + - reduced API functionality (removal of all deprecated functions) +## How to build -### Check it out +### Clone the repository and its submodules -``` -git clone https://github.com/eclipse-ecal/ecal-core.git +```bash +git clone https://github.com/ecal-io/ecal-cop.git +cd ecal-cop git submodule init git submodule update ``` ### Build it -``` +```bash mkdir _build cd _build - -cmake .. -cmake --build . --parallel --config Release -cpack +cmake .. -A x64 +cmake --build . ``` + +### CMake Options + +This section provides documentation for the CMake options used in configuring the eCAL library. These options allow you to customize the build and enable or disable specific features. Please adapt the options according to your project requirements. + +#### Additional Builds + +##### `ECAL_CORE_BUILD_SAMPLES` (Default: ON) +- This option controls whether the eCAL samples should be built. If not needed, you can disable this option to reduce build time. + +##### `ECAL_CORE_BUILD_SAMPLES_PROTOBUF` (Default: OFF) +- This option controls whether the eCAL protobuf samples should be built. If not needed, you can disable this option to reduce build time. +- Requires google::protobuf serialization library. + +##### `ECAL_CORE_BUILD_TESTS` (Default: ON) +- This option determines whether the eCAL Google Tests should be built. If enabled, it includes the compilation of unit tests for eCAL. Disabling this option will skip the build of tests if they are not required in your development environment. +- Requires gtest library. + +##### `ECAL_CORE_BUILD_TESTS_PROTOBUF` (Default: OFF) +- This option determines whether the eCAL Google Tests using protobuf serialization should be built. +- Requires gtest library and google::protobuf serialization library. + +#### Core Internal Feature Configuration + +##### `ECAL_CORE_CONFIG_INIFILE` (Default: ON) +- Enabling this option allows eCAL to be configured via an `ecal.ini` file. This file is used to set various configuration parameters for eCAL, providing flexibility in adjusting its behavior without modifying the source code. +- Requires simpleini library. + +##### `ECAL_CORE_COMMAND_LINE` (Default: ON) +- Enabling this option includes support for eCAL application command-line interfaces (cmd line). This allows you to interact with eCAL applications through the command line, providing additional runtime configuration options. +- Requires tclap library. + +##### `ECAL_CORE_REGISTRATION` (Default: ON) +- Enabling this option activates the eCAL registration layer. This layer is responsible for managing and registering different components within the eCAL ecosystem, facilitating communication between them. + +##### `ECAL_CORE_MONITORING` (Default: OFF) +- Enabling this option activates the eCAL monitoring functionality. + +##### `ECAL_CORE_PUBLISHER` (Default: ON) +- Enabling this option includes the eCAL publisher functionality. This is essential for components that need to publish data to the eCAL communication infrastructure. + +##### `ECAL_CORE_SUBSCRIBER` (Default: ON) +- Enabling this option includes the eCAL subscriber functionality. This is crucial for components that need to subscribe to and receive data from the eCAL communication infrastructure. + +##### `ECAL_CORE_SERVICE` (Default: ON) +- Enabling this option includes the eCAL server/client functionality. + +##### `ECAL_CORE_TIMEPLUGIN` (Default: ON) +- Enabling this option includes the eCAL time plugin functionality. This allows for precise synchronization of time across different components using eCAL. + +##### `ECAL_CORE_NPCAP_SUPPORT` (Default: OFF) +- Enabling this option replaces the standard ASIO UDP receiver by a NPCAP based UDP receiver to increase the performance on Windows platforms. +- Requires udpcap library. + +##### `ECAL_CORE_REGISTRATION_SHM` (Default: ON) +- Enabling this option activates the eCAL registration layer based on shared memory for high performance local communication scenarios only. + +##### `ECAL_CORE_TRANSPORT_UDP` (Default: ON) +- Enabling this option includes the eCAL UDP Multicast message transport layer. + +##### `ECAL_CORE_TRANSPORT_TCP` (Default: OFF) +- Enabling this option includes the eCAL TCP message transport layer. +- Requires tcp_pubsub library. + +##### `ECAL_CORE_TRANSPORT_SHM` (Default: ON) +- Enabling this option includes the eCAL local shared memory message transport layer. + +Note: Please adjust these options based on your project's needs, ensuring that the selected features align with your desired functionality and dependencies. diff --git a/build/linux/01_build_default.sh b/build/linux/01_build_default.sh new file mode 100644 index 0000000..4f0eb3b --- /dev/null +++ b/build/linux/01_build_default.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +mkdir -p _01_build_default +cd _01_build_default + +cmake .. -DCMAKE_BUILD_TYPE=Release +make -j + +# Run tests if available +make test diff --git a/build/linux/02_build_full.sh b/build/linux/02_build_full.sh new file mode 100644 index 0000000..c8b04a0 --- /dev/null +++ b/build/linux/02_build_full.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../../" + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON \ +-DECAL_CORE_CONFIG_INIFILE=ON \ +-DECAL_CORE_COMMAND_LINE=ON \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=ON \ +-DECAL_CORE_MONITORING=ON \ +-DECAL_CORE_PUBLISHER=ON \ +-DECAL_CORE_SUBSCRIBER=ON \ +-DECAL_CORE_SERVICE=ON \ +-DECAL_CORE_TIMEPLUGIN=ON \ +-DECAL_CORE_TRANSPORT_UDP=ON \ +-DECAL_CORE_TRANSPORT_TCP=ON \ +-DECAL_CORE_TRANSPORT_SHM=ON \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _02_build_full +cd _02_build_full + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/03_build_pubsub.sh b/build/linux/03_build_pubsub.sh new file mode 100644 index 0000000..9c52318 --- /dev/null +++ b/build/linux/03_build_pubsub.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=ON \ +-DECAL_CORE_MONITORING=OFF \ +-DECAL_CORE_PUBLISHER=ON \ +-DECAL_CORE_SUBSCRIBER=ON \ +-DECAL_CORE_SERVICE=OFF \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=ON \ +-DECAL_CORE_TRANSPORT_TCP=ON \ +-DECAL_CORE_TRANSPORT_SHM=ON \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _03_build_pubsub +cd _03_build_pubsub + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/04_build_pubsub_proto.sh b/build/linux/04_build_pubsub_proto.sh new file mode 100644 index 0000000..48681a8 --- /dev/null +++ b/build/linux/04_build_pubsub_proto.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=ON \ +-DECAL_CORE_MONITORING=OFF \ +-DECAL_CORE_PUBLISHER=ON \ +-DECAL_CORE_SUBSCRIBER=ON \ +-DECAL_CORE_SERVICE=OFF \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=ON \ +-DECAL_CORE_TRANSPORT_TCP=ON \ +-DECAL_CORE_TRANSPORT_SHM=ON \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _04_build_pubsub_proto +cd _04_build_pubsub_proto + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/05_build_pubsub_udp_only.sh b/build/linux/05_build_pubsub_udp_only.sh new file mode 100644 index 0000000..b8ac21a --- /dev/null +++ b/build/linux/05_build_pubsub_udp_only.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=OFF \ +-DECAL_CORE_REGISTRATION_SHM=OFF \ +-DECAL_CORE_MONITORING=OFF \ +-DECAL_CORE_PUBLISHER=ON \ +-DECAL_CORE_SUBSCRIBER=ON \ +-DECAL_CORE_SERVICE=OFF \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=ON \ +-DECAL_CORE_TRANSPORT_TCP=OFF \ +-DECAL_CORE_TRANSPORT_SHM=OFF \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _05_build_pubsub_udp_only +cd _05_build_pubsub_udp_only + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/06_build_pubsub_tcp_only.sh b/build/linux/06_build_pubsub_tcp_only.sh new file mode 100644 index 0000000..64c1bc4 --- /dev/null +++ b/build/linux/06_build_pubsub_tcp_only.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=OFF \ +-DECAL_CORE_MONITORING=OFF \ +-DECAL_CORE_PUBLISHER=ON \ +-DECAL_CORE_SUBSCRIBER=ON \ +-DECAL_CORE_SERVICE=OFF \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=OFF \ +-DECAL_CORE_TRANSPORT_TCP=ON \ +-DECAL_CORE_TRANSPORT_SHM=OFF \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _06_build_pubsub_tcp_only +cd _06_build_pubsub_tcp_only + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/07_build_pubsub_shm_only.sh b/build/linux/07_build_pubsub_shm_only.sh new file mode 100644 index 0000000..1e79a69 --- /dev/null +++ b/build/linux/07_build_pubsub_shm_only.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=OFF \ +-DECAL_CORE_MONITORING=OFF \ +-DECAL_CORE_PUBLISHER=ON \ +-DECAL_CORE_SUBSCRIBER=ON \ +-DECAL_CORE_SERVICE=OFF \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=OFF \ +-DECAL_CORE_TRANSPORT_TCP=OFF \ +-DECAL_CORE_TRANSPORT_SHM=ON \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _07_build_pubsub_shm_only +cd _07_build_pubsub_shm_only + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/08_build_clientserver.sh b/build/linux/08_build_clientserver.sh new file mode 100644 index 0000000..d0d397d --- /dev/null +++ b/build/linux/08_build_clientserver.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=ON \ +-DECAL_CORE_MONITORING=OFF \ +-DECAL_CORE_PUBLISHER=OFF \ +-DECAL_CORE_SUBSCRIBER=OFF \ +-DECAL_CORE_SERVICE=ON \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=OFF \ +-DECAL_CORE_TRANSPORT_TCP=OFF \ +-DECAL_CORE_TRANSPORT_SHM=OFF \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _08_build_clientserver +cd _08_build_clientserver + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/09_build_clientserver_proto.sh b/build/linux/09_build_clientserver_proto.sh new file mode 100644 index 0000000..c894b11 --- /dev/null +++ b/build/linux/09_build_clientserver_proto.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=ON \ +-DECAL_CORE_MONITORING=OFF \ +-DECAL_CORE_PUBLISHER=OFF \ +-DECAL_CORE_SUBSCRIBER=OFF \ +-DECAL_CORE_SERVICE=ON \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=OFF \ +-DECAL_CORE_TRANSPORT_TCP=OFF \ +-DECAL_CORE_TRANSPORT_SHM=OFF \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _09_build_clientserver_proto +cd _09_build_clientserver_proto + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test + diff --git a/build/linux/10_build_monitoring_only.sh b/build/linux/10_build_monitoring_only.sh new file mode 100644 index 0000000..5feeede --- /dev/null +++ b/build/linux/10_build_monitoring_only.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +cd "$(dirname "$0")/../.." + +CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ +-DECAL_CORE_BUILD_SAMPLES=ON \ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON \ +-DECAL_CORE_BUILD_TESTS=ON \ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON \ +-DECAL_CORE_CONFIG_INIFILE=OFF \ +-DECAL_CORE_COMMAND_LINE=OFF \ +-DECAL_CORE_REGISTRATION=ON \ +-DECAL_CORE_REGISTRATION_SHM=OFF \ +-DECAL_CORE_MONITORING=ON \ +-DECAL_CORE_PUBLISHER=OFF \ +-DECAL_CORE_SUBSCRIBER=OFF \ +-DECAL_CORE_SERVICE=OFF \ +-DECAL_CORE_TIMEPLUGIN=OFF \ +-DECAL_CORE_TRANSPORT_UDP=OFF \ +-DECAL_CORE_TRANSPORT_TCP=OFF \ +-DECAL_CORE_TRANSPORT_SHM=OFF \ +-DECAL_CORE_NPCAP_SUPPORT=OFF" + +mkdir -p _10_monitoring_only +cd _10_monitoring_only + +cmake .. -DCMAKE_BUILD_TYPE=Release ${CMAKE_OPTIONS} +make -j + +# Run tests if available +make test diff --git a/build/windows/01_build_default.bat b/build/windows/01_build_default.bat new file mode 100644 index 0000000..6740fe2 --- /dev/null +++ b/build/windows/01_build_default.bat @@ -0,0 +1,12 @@ +@echo off + +pushd "%~dp0\..\.." + +mkdir _01_build_default +cd _01_build_default + +cmake .. -A x64 +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/02_build_full.bat b/build/windows/02_build_full.bat new file mode 100644 index 0000000..8192be3 --- /dev/null +++ b/build/windows/02_build_full.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON ^ +-DECAL_CORE_CONFIG_INIFILE=ON ^ +-DECAL_CORE_COMMAND_LINE=ON ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_REGISTRATION_SHM=ON ^ +-DECAL_CORE_MONITORING=ON ^ +-DECAL_CORE_PUBLISHER=ON ^ +-DECAL_CORE_SUBSCRIBER=ON ^ +-DECAL_CORE_SERVICE=ON ^ +-DECAL_CORE_TIMEPLUGIN=ON ^ +-DECAL_CORE_TRANSPORT_UDP=ON ^ +-DECAL_CORE_TRANSPORT_TCP=ON ^ +-DECAL_CORE_TRANSPORT_SHM=ON ^ +-DECAL_CORE_NPCAP_SUPPORT=ON + +mkdir _02_build_full +cd _02_build_full + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/03_build_pubsub.bat b/build/windows/03_build_pubsub.bat new file mode 100644 index 0000000..ffde54c --- /dev/null +++ b/build/windows/03_build_pubsub.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_REGISTRATION_SHM=ON ^ +-DECAL_CORE_MONITORING=OFF ^ +-DECAL_CORE_PUBLISHER=ON ^ +-DECAL_CORE_SUBSCRIBER=ON ^ +-DECAL_CORE_SERVICE=OFF ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=ON ^ +-DECAL_CORE_TRANSPORT_TCP=ON ^ +-DECAL_CORE_TRANSPORT_SHM=ON ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _03_build_pubsub +cd _03_build_pubsub + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/04_build_pubsub_proto.bat b/build/windows/04_build_pubsub_proto.bat new file mode 100644 index 0000000..af49834 --- /dev/null +++ b/build/windows/04_build_pubsub_proto.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_REGISTRATION_SHM=ON ^ +-DECAL_CORE_MONITORING=OFF ^ +-DECAL_CORE_PUBLISHER=ON ^ +-DECAL_CORE_SUBSCRIBER=ON ^ +-DECAL_CORE_SERVICE=OFF ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=ON ^ +-DECAL_CORE_TRANSPORT_TCP=ON ^ +-DECAL_CORE_TRANSPORT_SHM=ON ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _04_build_pubsub_proto +cd _04_build_pubsub_proto + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/05_build_pubsub_udp_only.bat b/build/windows/05_build_pubsub_udp_only.bat new file mode 100644 index 0000000..88f86dd --- /dev/null +++ b/build/windows/05_build_pubsub_udp_only.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=OFF ^ +-DECAL_CORE_REGISTRATION_SHM=OFF ^ +-DECAL_CORE_MONITORING=OFF ^ +-DECAL_CORE_PUBLISHER=ON ^ +-DECAL_CORE_SUBSCRIBER=ON ^ +-DECAL_CORE_SERVICE=OFF ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=ON ^ +-DECAL_CORE_TRANSPORT_TCP=OFF ^ +-DECAL_CORE_TRANSPORT_SHM=OFF ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _05_build_pubsub_udp_only +cd _05_build_pubsub_udp_only + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/06_build_pubsub_tcp_only.bat b/build/windows/06_build_pubsub_tcp_only.bat new file mode 100644 index 0000000..9bbc503 --- /dev/null +++ b/build/windows/06_build_pubsub_tcp_only.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_MONITORING=OFF ^ +-DECAL_CORE_PUBLISHER=ON ^ +-DECAL_CORE_SUBSCRIBER=ON ^ +-DECAL_CORE_SERVICE=OFF ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_REGISTRATION_SHM=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=OFF ^ +-DECAL_CORE_TRANSPORT_TCP=ON ^ +-DECAL_CORE_TRANSPORT_SHM=OFF ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _06_build_pubsub_tcp_only +cd _06_build_pubsub_tcp_only + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/07_build_pubsub_shm_only.bat b/build/windows/07_build_pubsub_shm_only.bat new file mode 100644 index 0000000..6e024a3 --- /dev/null +++ b/build/windows/07_build_pubsub_shm_only.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_REGISTRATION_SHM=OFF ^ +-DECAL_CORE_MONITORING=OFF ^ +-DECAL_CORE_PUBLISHER=ON ^ +-DECAL_CORE_SUBSCRIBER=ON ^ +-DECAL_CORE_SERVICE=OFF ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=OFF ^ +-DECAL_CORE_TRANSPORT_TCP=OFF ^ +-DECAL_CORE_TRANSPORT_SHM=ON ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _07_build_pubsub_shm_only +cd _07_build_pubsub_shm_only + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/08_build_clientserver.bat b/build/windows/08_build_clientserver.bat new file mode 100644 index 0000000..4663404 --- /dev/null +++ b/build/windows/08_build_clientserver.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=OFF ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=OFF ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_REGISTRATION_SHM=ON ^ +-DECAL_CORE_MONITORING=OFF ^ +-DECAL_CORE_PUBLISHER=OFF ^ +-DECAL_CORE_SUBSCRIBER=OFF ^ +-DECAL_CORE_SERVICE=ON ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=OFF ^ +-DECAL_CORE_TRANSPORT_TCP=OFF ^ +-DECAL_CORE_TRANSPORT_SHM=OFF ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _08_build_clientserver +cd _08_build_clientserver + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/09_build_clientserver_proto.bat b/build/windows/09_build_clientserver_proto.bat new file mode 100644 index 0000000..70f1e39 --- /dev/null +++ b/build/windows/09_build_clientserver_proto.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_REGISTRATION_SHM=ON ^ +-DECAL_CORE_MONITORING=OFF ^ +-DECAL_CORE_PUBLISHER=OFF ^ +-DECAL_CORE_SUBSCRIBER=OFF ^ +-DECAL_CORE_SERVICE=ON ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=OFF ^ +-DECAL_CORE_TRANSPORT_TCP=OFF ^ +-DECAL_CORE_TRANSPORT_SHM=OFF ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _09_build_clientserver_proto +cd _09_build_clientserver_proto + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/10_build_monitoring_only.bat b/build/windows/10_build_monitoring_only.bat new file mode 100644 index 0000000..4deadd9 --- /dev/null +++ b/build/windows/10_build_monitoring_only.bat @@ -0,0 +1,31 @@ +@echo off + +pushd "%~dp0\..\.." + +set CMAKE_OPTIONS=-DCMAKE_INSTALL_PREFIX=_install ^ +-DECAL_CORE_BUILD_SAMPLES=ON ^ +-DECAL_CORE_BUILD_SAMPLES_PROTOBUF=ON ^ +-DECAL_CORE_BUILD_TESTS=ON ^ +-DECAL_CORE_BUILD_TESTS_PROTOBUF=ON ^ +-DECAL_CORE_CONFIG_INIFILE=OFF ^ +-DECAL_CORE_COMMAND_LINE=OFF ^ +-DECAL_CORE_REGISTRATION=ON ^ +-DECAL_CORE_REGISTRATION_SHM=OFF ^ +-DECAL_CORE_MONITORING=ON ^ +-DECAL_CORE_PUBLISHER=OFF ^ +-DECAL_CORE_SUBSCRIBER=OFF ^ +-DECAL_CORE_SERVICE=OFF ^ +-DECAL_CORE_TIMEPLUGIN=OFF ^ +-DECAL_CORE_TRANSPORT_UDP=OFF ^ +-DECAL_CORE_TRANSPORT_TCP=OFF ^ +-DECAL_CORE_TRANSPORT_SHM=OFF ^ +-DECAL_CORE_NPCAP_SUPPORT=OFF + +mkdir _10_build_monitoring_only +cd _10_build_monitoring_only + +cmake .. -A x64 %CMAKE_OPTIONS% +cmake --build . --parallel --config Release +ctest -C Release -V + +popd diff --git a/build/windows/Readme.md b/build/windows/Readme.md deleted file mode 100644 index 658ee71..0000000 --- a/build/windows/Readme.md +++ /dev/null @@ -1,36 +0,0 @@ -## How to build - windows platform - -### Requirements - -- Visual Studio :-) -- [Inno Setup](https://jrsoftware.org/isdl.php#stable) - -### The easy way - -If you want to build a complete eCAL setup with the default Visual Studio Version 2019 then simply call: - -```win_make_all.bat``` - -### Step by step - -If you want to cmake / build eCAL step by step do the following: - -for Visual Studio 2019: - -``` -win_make_cmake.bat v142 -win_make_build.bat -``` - -for Visual Studio 2022: - -``` -win_make_cmake.bat v143 -win_make_build.bat -``` - -To create a windows setup finally call: - -``` -win_make_setup.bat -``` diff --git a/build/windows/download_npcap.ps1 b/build/windows/download_npcap.ps1 deleted file mode 100644 index e591dad..0000000 --- a/build/windows/download_npcap.ps1 +++ /dev/null @@ -1,9 +0,0 @@ - -$target_dir = "$PSScriptRoot\..\..\thirdparty\npcap\" - -New-Item "$target_dir" -ItemType "directory" -Force - -$webclient = New-Object System.Net.WebClient - -$webclient.DownloadFile("https://npcap.com/dist/npcap-sdk-1.13.zip","$target_dir\npcap-sdk.zip") -$webclient.DownloadFile("https://github.com/seladb/PcapPlusPlus/releases/download/v22.05/pcapplusplus-22.05-windows-vs2015.zip","$target_dir\pcapplusplus.zip") \ No newline at end of file diff --git a/build/windows/win_make_all.bat b/build/windows/win_make_all.bat deleted file mode 100644 index 3230f27..0000000 --- a/build/windows/win_make_all.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off - -pushd %~dp0\..\.. - -call build\windows\win_make_cmake.bat -call build\windows\win_make_build.bat -call build\windows\win_run_tests.bat -call build\windows\win_make_setup.bat - -popd diff --git a/build/windows/win_make_build.bat b/build/windows/win_make_build.bat deleted file mode 100644 index 449277e..0000000 --- a/build/windows/win_make_build.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off - -pushd %~dp0\..\.. - -call build\windows\win_set_vars.bat - -cmake --build "%BUILD_DIR_COMPLETE%" --parallel --config Release -cmake --build "%BUILD_DIR_SDK%" --parallel --config Debug - -popd diff --git a/build/windows/win_make_cmake.bat b/build/windows/win_make_cmake.bat deleted file mode 100644 index 1d07df7..0000000 --- a/build/windows/win_make_cmake.bat +++ /dev/null @@ -1,22 +0,0 @@ -@echo off - -pushd %~dp0\..\.. - -call build\windows\win_set_vars.bat - -echo Downloading NPCAP -powershell -Command "& '%~dp0\download_npcap.ps1'" - -set "CMAKE_OPTIONS_COMPLETE=-DCMAKE_INSTALL_PREFIX=_install -DBUILD_ECAL_TESTS=ON -DECAL_THIRDPARTY_BUILD_GTEST=ON -DBUILD_SHARED_LIBS=OFF -DECAL_NPCAP_SUPPORT=ON -DECAL_THIRDPARTY_BUILD_UDPCAP=ON" -set "CMAKE_OPTIONS_SDK=-DCMAKE_INSTALL_PREFIX=_install -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Debug -DBUILD_SAMPLES=OFF -DBUILD_ECAL_TESTS=OFF -DECAL_LAYER_ICEORYX=OFF -DECAL_INSTALL_SAMPLE_SOURCES=OFF -DCPACK_PACK_WITH_INNOSETUP=OFF -DECAL_NPCAP_SUPPORT=ON -DECAL_THIRDPARTY_BUILD_UDPCAP=ON" - -if not exist "%BUILD_DIR_COMPLETE%" mkdir "%BUILD_DIR_COMPLETE%" -if not exist "%BUILD_DIR_SDK%" mkdir "%BUILD_DIR_SDK%" - -cd /d "%WORKSPACE%\%BUILD_DIR_COMPLETE%" -cmake ../.. -A x64 %CMAKE_OPTIONS_COMPLETE% %* - -cd /d "%WORKSPACE%\%BUILD_DIR_SDK%" -cmake ../.. -A x64 %CMAKE_OPTIONS_SDK% %* - -popd diff --git a/build/windows/win_make_setup.bat b/build/windows/win_make_setup.bat deleted file mode 100644 index de2af7f..0000000 --- a/build/windows/win_make_setup.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off - -pushd %~dp0\..\.. - -call build\windows\win_set_vars.bat - -cd /d %WORKSPACE%\%BUILD_DIR_SDK% -cpack -C Debug - -cd /d %WORKSPACE%\%BUILD_DIR_COMPLETE% -cpack -C Release - -popd diff --git a/build/windows/win_run_tests.bat b/build/windows/win_run_tests.bat deleted file mode 100644 index 516563e..0000000 --- a/build/windows/win_run_tests.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off - -pushd %~dp0\..\.. - -call build\windows\win_set_vars.bat - -cd %BUILD_DIR_COMPLETE% -ctest -C Release --verbose -cd .. - -popd diff --git a/build/windows/win_set_vars.bat b/build/windows/win_set_vars.bat deleted file mode 100644 index 009b1d3..0000000 --- a/build/windows/win_set_vars.bat +++ /dev/null @@ -1,14 +0,0 @@ -@echo off - -if ["%~1"]==[""] ( - set VERSION=v142 - ) else ( - set VERSION=%1 - ) - -rem - base folder -set "WORKSPACE=%~dp0\..\..\" - -rem - cmake will generate the build solution here -set BUILD_DIR_COMPLETE=_build\complete -set BUILD_DIR_SDK=_build\sdk diff --git a/cmake/Modules/Findasio.cmake b/cmake/Modules/Findasio.cmake index f28df02..bd712cc 100644 --- a/cmake/Modules/Findasio.cmake +++ b/cmake/Modules/Findasio.cmake @@ -1,10 +1,5 @@ find_path(asio_INCLUDE_DIR NAMES asio.hpp - HINTS - "${CONAN_ASIO_ROOT}/include" - "${ECAL_PROJECT_ROOT}/thirdparty/asio/asio/include" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH ) if(asio_INCLUDE_DIR-NOTFOUND) diff --git a/cmake/Modules/Findsimpleini.cmake b/cmake/Modules/Findsimpleini.cmake index 69b171a..4954f15 100644 --- a/cmake/Modules/Findsimpleini.cmake +++ b/cmake/Modules/Findsimpleini.cmake @@ -1,10 +1,5 @@ find_path(simpleini_INCLUDE_DIR NAMES SimpleIni.h - HINTS - "${CONAN_SIMPLEINI_ROOT}/include" - "${ECAL_PROJECT_ROOT}/thirdparty/simpleini" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH ) if(simpleini_INCLUDE_DIR-NOTFOUND) diff --git a/cmake/Modules/Findtclap.cmake b/cmake/Modules/Findtclap.cmake index f6ffa6d..a0de08f 100644 --- a/cmake/Modules/Findtclap.cmake +++ b/cmake/Modules/Findtclap.cmake @@ -1,10 +1,5 @@ find_path(tclap_INCLUDE_DIR NAMES tclap/Arg.h - HINTS - "${CONAN_TCLAP_ROOT}/include" - "${ECAL_PROJECT_ROOT}/thirdparty/tclap/include" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH ) if(tclap_INCLUDE_DIR-NOTFOUND) diff --git a/cmake/Modules/eCALConfig.cmake b/cmake/Modules/eCALConfig.cmake new file mode 100644 index 0000000..d20affb --- /dev/null +++ b/cmake/Modules/eCALConfig.cmake @@ -0,0 +1 @@ +set(eCAL_FOUND TRUE) \ No newline at end of file diff --git a/cmake/helper_functions/app.desktop.in b/cmake/helper_functions/app.desktop.in new file mode 100644 index 0000000..daf1bef --- /dev/null +++ b/cmake/helper_functions/app.desktop.in @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=@ECAL_INSTALL_APP_START_MENU_NAME@ +Comment=@ECAL_INSTALL_APP_START_MENU_NAME@ +Exec=@CMAKE_INSTALL_PREFIX@/@eCAL_install_app_dir@/@TARGET_NAME@ +Terminal=false +Type=Application +# Icon=ecalmon diff --git a/cmake/helper_functions/ecal_add_functions.cmake b/cmake/helper_functions/ecal_add_functions.cmake index c466203..8c48d47 100644 --- a/cmake/helper_functions/ecal_add_functions.cmake +++ b/cmake/helper_functions/ecal_add_functions.cmake @@ -52,6 +52,62 @@ function(ecal_add_gtest TARGET_NAME) OUTPUT_NAME ecal_${TARGET_NAME}) endfunction() +function(ecal_add_app_gui TARGET_NAME) + add_executable(${TARGET_NAME} ${ARGN}) + ecal_set_subsystem_windows(${TARGET_NAME}) + set_target_properties(${TARGET_NAME} PROPERTIES + VERSION ${eCAL_VERSION_STRING} + SOVERSION ${eCAL_VERSION_MAJOR} + OUTPUT_NAME ecal_${TARGET_NAME}) +endfunction() + +function(ecal_add_app_qt TARGET_NAME) + add_executable(${TARGET_NAME} ${ARGN}) + set_target_properties(${TARGET_NAME} PROPERTIES + VERSION ${eCAL_VERSION_STRING} + SOVERSION ${eCAL_VERSION_MAJOR} + OUTPUT_NAME ecal_${TARGET_NAME}) + if(WIN32) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") + endif() +endfunction() + +function(ecal_add_mon_plugin TARGET_NAME) + set(options "") + set(oneValueArgs METADATA) + set(multiValueArgs SOURCES) + cmake_parse_arguments(MON_PLUGIN + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + add_library(${TARGET_NAME} MODULE ${MON_PLUGIN_SOURCES} ${MON_PLUGIN_METADATA}) + set_target_properties(${TARGET_NAME} PROPERTIES + VERSION $:${${TARGET_NAME}_VERSION}>> + SOVERSION $:${${TARGET_NAME}_VERSION_MAJOR}>> + LIBRARY_OUTPUT_DIRECTORY $,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/ecalmon_plugins,${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ecal/plugins/mon> + ) + target_compile_definitions(${TARGET_NAME} + PRIVATE + $<$:QT_NO_DEBUG> + $<$:QT_NO_DEBUG> + $<$:QT_NO_DEBUG> + ) + +endfunction() + +function(ecal_add_rec_addon TARGET_NAME) + add_executable(${TARGET_NAME} ${ARGN}) + set_target_properties(${TARGET_NAME} PROPERTIES + #VERSION ${${TARGET_NAME}_VERSION} + #SOVERSION ${${TARGET_NAME}_VERSION_MAJOR} + OUTPUT_NAME ecal_${TARGET_NAME} + RUNTIME_OUTPUT_DIRECTORY $,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/ecalrec_addons,${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ecal/addons/rec> + ) + +endfunction() + function(ecal_add_time_plugin TARGET_NAME) add_library(${TARGET_NAME} MODULE ${ARGN}) set_target_properties(${TARGET_NAME} PROPERTIES @@ -124,3 +180,4 @@ function(ecal_add_sample TARGET_NAME) SOVERSION ${eCAL_VERSION_MAJOR} OUTPUT_NAME ecal_sample_${TARGET_NAME}) endfunction() + diff --git a/cmake/helper_functions/ecal_install_functions.cmake b/cmake/helper_functions/ecal_install_functions.cmake index dd88fea..35dfc86 100644 --- a/cmake/helper_functions/ecal_install_functions.cmake +++ b/cmake/helper_functions/ecal_install_functions.cmake @@ -129,6 +129,7 @@ function(ecal_install_gtest TARGET_NAME) ) endfunction() + # Samples are sample applications that demonstrate eCAL capability # They will be installed to "${INSTALL_BIN_DIR}/ecal_samples" function(ecal_install_sample TARGET_NAME) @@ -145,3 +146,13 @@ install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION "${eCAL_install_lib_dir}/${ECAL_TIME_PLUGIN_DIR}" COMPONENT app ) endfunction() + +# Use this function to install monitor plugins +# We need to provide a similar function for installing custom build plugins. +# For some unknown reason, a MODULE dll on Windows is considered as LIBRARY, not RUNTIME +function(ecal_install_mon_plugin TARGET_NAME) +install(TARGETS ${TARGET_NAME} + RUNTIME DESTINATION "${eCAL_install_bin_dir}/${ECAL_MON_PLUGIN_DIR}" COMPONENT app + LIBRARY DESTINATION $,${eCAL_install_bin_dir}/${ECAL_MON_PLUGIN_DIR},${eCAL_install_lib_dir}/${ECAL_MON_PLUGIN_DIR}> COMPONENT app + ) +endfunction() diff --git a/cpack/cpack_variables.cmake b/cpack/cpack_variables.cmake index c63cdaf..3fbbccc 100644 --- a/cpack/cpack_variables.cmake +++ b/cpack/cpack_variables.cmake @@ -12,6 +12,24 @@ set(CPACK_PACKAGE_CONTACT "rex.schilasky@continental-corporation.com") set(CPACK_SOURCE_STRIP_FILES "") SET(CPACK_OUTPUT_FILE_PREFIX _deploy) +# if(WIN32) + # set(CPACK_GENERATOR "WIX") + # set(CPACK_WIX_PRODUCT_GUID "87A6B30D-670A-4752-A10F-37521D01822F") + # set(CPACK_WIX_UPGRADE_GUID "A025EF91-496F-4E1C-96D1-1E0CC7B00C74") + # set(CPACK_WIX_SKIP_PROGRAM_FOLDER ON) + # cpack_add_component(libprotobuf HIDDEN) + # cpack_add_component(libprotoc HIDDEN) + # cpack_add_component(protoc HIDDEN) + # set(CPACK_WIX_PRODUCT_ICON ${ECAL_PROJECT_ROOT}/cpack/ecalproduct.ico) + # set(CPACK_WIX_UI_BANNER ${ECAL_PROJECT_ROOT}/cpack/banner.png) + # set(CPACK_WIX_UI_DIALOG ${ECAL_PROJECT_ROOT}/cpack/background.png) + # set(CPACK_WIX_UI_REF "WixUI_Mondo") + # set(CPACK_WIX_EXTENSIONS "WixUtilExtension") + ##set(CPACK_WIX_PATCH_FILE ${ECAL_PROJECT_ROOT}/cpack/ecal_path.xml) + ##set(CPACK_WIX_TEMPLATE "${ECAL_PROJECT_ROOT}/cpack/custom_template.wxs.in") + ##configure_file("${ECAL_PROJECT_ROOT}/cpack/custom_template.wxs.in" "${CMAKE_BINARY_DIR}/custom_template.wxi" #@ONLY) + +#endif() if(WIN32) set(CPACK_GENERATOR "External") set(CPACK_EXTERNAL_ENABLE_STAGING ON) @@ -20,6 +38,7 @@ if(WIN32) endif() endif() + if(UNIX) set(CPACK_GENERATOR "DEB") set(CPACK_SOURCE_GENERATOR "TGZ") @@ -38,6 +57,7 @@ set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON) set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/cpack/debscripts/postinst" "${CMAKE_BINARY_DIR}/cpack/debscripts/postrm") set(CPACK_DEBIAN_PACKAGE_DEBUG ON) + set(CPACK_RESOURCE_FILE_LICENSE ${ECAL_PROJECT_ROOT}/LICENSE.txt) set(CPACK_RESOURCE_FILE_README ${ECAL_PROJECT_ROOT}/README.md) @@ -47,9 +67,9 @@ list(REMOVE_ITEM CPACK_COMPONENTS_ALL #"protobuf-export" #"protobuf-headers" #"protobuf-protos" - #"tinyxml2_config" - #"tinyxml2_headers" - #"tinyxml2_libraries" + "tinyxml2_config" + "tinyxml2_headers" + "tinyxml2_libraries" ) include(CPack) diff --git a/cpack/ecal_path.xml b/cpack/ecal_path.xml new file mode 100644 index 0000000..54b6633 --- /dev/null +++ b/cpack/ecal_path.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/cpack/innosetup.cmake b/cpack/innosetup.cmake index fa31c87..221879e 100644 --- a/cpack/innosetup.cmake +++ b/cpack/innosetup.cmake @@ -19,8 +19,6 @@ find_program(ISSC_PATH PATHS "C:/Program Files (x86)/Inno Setup 6" "C:/Program Files/Inno Setup 6" - "C:/Program Files (x86)/Inno Setup 5" - "C:/Program Files/Inno Setup 5" ) if (ISSC_PATH STREQUAL "ISSC_PATH-NOTFOUND") diff --git a/cpack/innosetup/ecal_setup.iss.in b/cpack/innosetup/ecal_setup.iss.in index 77b2b24..bfe4d41 100644 --- a/cpack/innosetup/ecal_setup.iss.in +++ b/cpack/innosetup/ecal_setup.iss.in @@ -40,13 +40,15 @@ AppPublisher={#AppPublisher} DefaultDirName={sd}\{#AppName} DefaultGroupName={#AppName} WizardImageFile=gfx\logo_iss.bmp -WizardSmallImageFile=gfx\logo_icon.bmp +WizardSmallImageFile=gfx\logo_55_55.bmp,gfx\logo_110_110.bmp OutputDir={#OutputDir} OutputBaseFilename={#OutputName} Compression=lzma2 SolidCompression=yes ChangesAssociations=yes -PrivilegesRequired=admin +; PrivilegesRequired=admin +; Only available in Version 6 and above: +PrivilegesRequiredOverridesAllowed=dialog ArchitecturesInstallIn64BitMode=x64 ChangesEnvironment=true VersionInfoVersion={#AppVersion} @@ -54,12 +56,16 @@ LicenseFile={#LicenseFile} [Components] Name: runtime; Description: "eCAL Runtime"; Types: full compact custom; Flags: disablenouninstallwarning fixed +Name: applications; Description: "Applications (Monitor, Recorder, Player)"; Types: full compact custom; Flags: disablenouninstallwarning Name: samples; Description: "Samples"; Types: full; Flags: disablenouninstallwarning +Name: doc; Description: "Documentation"; Types: full; Flags: disablenouninstallwarning Name: sdk; Description: "SDK"; Types: full; Name: sdk\ecal; Description: "eCAL SDK"; Types: full; Flags: disablenouninstallwarning Name: sdk\protobuf; Description: "Protobuf"; Types: full; Flags: disablenouninstallwarning +Name: sdk\hdf5; Description: "HDF5"; Types: full; Flags: disablenouninstallwarning [Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked Name: "replaceconf"; Description: "Overwrite existing configuration"; Flags: checkedonce Name: "modifypath"; Description: "Add application directory to your environmental path"; Flags: checkedonce @@ -67,20 +73,32 @@ Name: "modifypath"; Description: "Add application directory to your environmen ; runtime Source: "{#ComponentStagingDir}\runtime\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: runtime Source: "{#ComponentStagingDir}\Unspecified\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: runtime +Source: "{#ComponentStagingDir}\libraries\bin\*"; DestDir: "{app}\bin\"; Flags: ignoreversion recursesubdirs; Components: runtime Source: "{#ComponentStagingDir}\configuration\cfg\ecal.ini"; DestDir: "{commonappdata}\eCAL\"; Tasks: not replaceconf; Flags: ignoreversion confirmoverwrite; Components: runtime Source: "{#ComponentStagingDir}\configuration\cfg\ecal.ini"; DestDir: "{commonappdata}\eCAL\"; Tasks: replaceconf; Flags: ignoreversion; Components: runtime +Source: "{#ComponentStagingDir}\configuration\cfg\ecaltime.ini"; DestDir: "{commonappdata}\eCAL\"; Flags: ignoreversion; Components: runtime + +; applications +Source: "{#ComponentStagingDir}\app\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: applications +Source: "{#ComponentStagingDir}\qwt_runtime\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: applications ; samples Source: "{#ComponentStagingDir}\samples\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: samples +; doc +Source: "{#ComponentStagingDir}\doc\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: doc + ; sdk\ecal -;Source: "{#ComponentStagingDir}\headers\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal +Source: "{#ComponentStagingDir}\headers\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal Source: "{#ComponentStagingDir}\sdk\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal Source: "{#DebugSdkStagingDir}\runtime\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal Source: "{#DebugSdkStagingDir}\Unspecified\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal +Source: "{#DebugSdkStagingDir}\app\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal + +;Source: "{#DebugSdkStagingDir}\headers\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal Source: "{#DebugSdkStagingDir}\sdk\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\ecal ; sdk\protobuf @@ -96,21 +114,79 @@ Source: "{#DebugSdkStagingDir}\libprotobuf\*"; DestDir: "{app}"; Source: "{#DebugSdkStagingDir}\libprotobuf-lite\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\protobuf Source: "{#DebugSdkStagingDir}\libprotoc\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\protobuf Source: "{#DebugSdkStagingDir}\protobuf-export\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\protobuf +;Source: "{#DebugSdkStagingDir}\protobuf-headers\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\protobuf +;Source: "{#DebugSdkStagingDir}\protobuf-protos\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\protobuf +; Source: "{#DebugSdkStagingDir}\protoc\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\protobuf + +; sdk\hdf5 +Source: "{#ComponentStagingDir}\configinstall\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 +Source: "{#ComponentStagingDir}\headers\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 +Source: "{#ComponentStagingDir}\libraries\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 + +Source: "{#DebugSdkStagingDir}\configinstall\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 +Source: "{#DebugSdkStagingDir}\libraries\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 +Source: "{#DebugSdkStagingDir}\libraries\bin\*"; DestDir: "{app}\bin\"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 [Icons] +Name: "{group}\eCAL Launcher"; Filename: "{app}\bin\ecal_launcher.exe"; Components: applications +Name: "{group}\eCAL Monitor"; Filename: "{app}\bin\ecal_mon_gui.exe"; Components: applications +Name: "{group}\eCAL Recorder"; Filename: "{app}\bin\ecal_rec_gui.exe"; Components: applications +Name: "{group}\eCAL Player"; Filename: "{app}\bin\ecal_play_gui.exe"; Components: applications +Name: "{group}\eCAL Sys"; Filename: "{app}\bin\ecal_sys_gui.exe"; Components: applications Name: "{group}\Configuration\Edit ecal.ini"; Filename: "{commonappdata}\eCAL\ecal.ini"; Components: runtime Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}" +Name: "{commondesktop}\eCAL Launcher"; Filename: "{app}\bin\ecal_launcher.exe"; Tasks: desktopicon; Components: applications [Registry] -Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "ECAL_HOME"; ValueData: "{app}"; Flags: uninsdeletevalue -Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "ECAL_DATA"; ValueData: "{commonappdata}\eCAL"; Flags: uninsdeletevalue +; Root key HKA is only available in Version 6 and above +; In Case of Non-Admin install mode, HKA will be resolved to HKCU, otherwise HKLM - see https://jrsoftware.org/ishelp/index.php?topic=setup_privilegesrequiredoverridesallowed +Root: HKA; Subkey: {code:GetEnvironmentSubkey}; ValueType: string; ValueName: "ECAL_HOME"; ValueData: "{app}"; Flags: uninsdeletevalue; Components: applications +Root: HKA; Subkey: {code:GetEnvironmentSubkey}; ValueType: string; ValueName: "ECAL_DATA"; ValueData: "{commonappdata}\eCAL"; Flags: uninsdeletevalue; Components: applications + +Root: HKA; Subkey: "SOFTWARE\Classes\.ecalrec"; ValueType: string; ValueName: ""; ValueData: "ecal_rec_gui"; Flags: uninsdeletevalue; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_rec_gui"; ValueType: string; ValueName: ""; ValueData: "eCAL Recorder Configuration"; Flags: uninsdeletekey; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_rec_gui\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\bin\ecal_rec_gui.exe,0"; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_rec_gui\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\bin\ecal_rec_gui.exe"" ""%1"""; Components: applications + +Root: HKA; Subkey: "SOFTWARE\Classes\.ecalmeas"; ValueType: string; ValueName: ""; ValueData: "ecal_play_gui"; Flags: uninsdeletevalue; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_play_gui"; ValueType: string; ValueName: ""; ValueData: "eCAL Player Configuration"; Flags: uninsdeletekey; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_play_gui\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\bin\ecal_play_gui.exe,0"; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_play_gui\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\bin\ecal_play_gui.exe"" ""%1"""; Components: applications + +Root: HKA; Subkey: "SOFTWARE\Classes\.ecalsys"; ValueType: string; ValueName: ""; ValueData: "ecal_sys_gui"; Flags: uninsdeletevalue; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_sys_gui"; ValueType: string; ValueName: ""; ValueData: "eCAL Sys Configuration"; Flags: uninsdeletekey; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_sys_gui\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\bin\ecal_sys_gui.exe,0"; Components: applications +Root: HKA; Subkey: "SOFTWARE\Classes\ecal_sys_gui\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\bin\ecal_sys_gui.exe"" ""%1"""; Components: applications [Code] // add eCAL to system path const ModPathName = 'modifypath'; - ModPathType = 'system'; +var + ModPathType: String; + +// Settings for modpath.iss weather PATH should be extented for user only or system wide +function PrepareToInstall(var NeedsRestart: Boolean) : String; +begin + if (IsAdminInstallMode() = true) then begin + ModPathType := 'system'; + end else begin + ModPathType := 'user'; + end; + NeedsRestart := false; + Result := ''; +end; + +// Get reg key for environment variables depending on install mode +function GetEnvironmentSubkey(Param: String) : String; +begin + if (IsAdminInstallMode() = true) then begin + Result := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; + end else begin + Result := 'Environment'; + end; +end; function ModPathDir(): TArrayOfString; begin diff --git a/cpack/innosetup/gfx/logo.svg b/cpack/innosetup/gfx/logo.svg new file mode 100644 index 0000000..e4bb8b4 --- /dev/null +++ b/cpack/innosetup/gfx/logo.svg @@ -0,0 +1,189 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cpack/innosetup/gfx/logo_110_110.bmp b/cpack/innosetup/gfx/logo_110_110.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ad9a02faccae626ed06d6e36c71777c4e6dc0c3d GIT binary patch literal 36574 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 literal 0 HcmV?d00001 diff --git a/cpack/innosetup/gfx/logo_55_55.bmp b/cpack/innosetup/gfx/logo_55_55.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b93d430c19bc7eb2d31161f42c23ad83be049e4e GIT binary patch literal 9294 zcmeI12~bsK6vtUzKooIL#a$6(5d@Tnpr9Zsh$1}26$LlkP17_La9>Ep4Fo};sivlB za>k}9(-~3A8e44&Fk7a~va~EK7wq@A&gbR6_wKv!cr!ZFoA>UV``z=M^MB_%+xOk5 z_=MhewVx2KUi?BI{_O2K){b}Nc|}X9&MsRyKO8tE-Kn8sj{F{(N+Fv{^y6e}Hx{ zD4;5EV#&q@*?ZO|*S(dFC4af)e(pVaxyFA928C7SIPr~)j0}yUw6Jx{-0xKMdyl!h z2PDn+Tyn(W*gyTIuF*0P9vndKt8$zKjz%F`*ak;LIh6j}f6^lx_ME%B+2J3x%$0Zu zE3x`=oY~pg+J#u;^h=x#&M;lL;0%gMw0q$}NK~8{Q71NS+GG-EN=l0Q#Ckm)G;$oM zxQbam6Ep2g?*xpUB1Y5+l9frEjh-jnc7Nuw|4Qia81Mktbx&opLqFJ;-*1!su7AP| z1;`;oBNZdyF^N;bX60?ju$ZQYt~M$7#o^fB&GW9v$`>Mqh8{s4&*^)CnX}GOND^5>EWwQ@~;u> zn3QXm6{Yh3XbYTu(y|;%Zj*LYfYo_KPRKshBKJ~g#BgTe&%x1S>M(?bH5yJ`I1V`^ zd?;CkJlSVfjzW>jZMTI7=~9A5C8_W<0#03c_sso{M{nW(AapFg)p7MJDjF4!q#A0m z)3Y9D_xXFBkNoO2b#+ke1n)`9oeFPuTK%d5jJez35o0Mpbb96(#JKf1or`Yvo4&5Y z+BdLgtL-29j!(BMzpo{NaFUigKXTu8VTi1i62KIMl{kZ=M*|N_f(?T2z_F=xaiCQw zGQNFk<%>=8e+t&t-C2o~)ZtWo%PVzd+pKp0Ngel3m~K~ILn$`~CLi&d@(h2+FL9Q9 zP=&S@XINO*B}XvJfaJ`enDKt&(p>gdx#WFK0;CrTjH}bfh7KD^7e{+f??K%4%Gj;~ zO!3HioVXb|nQqOn^yT+))X>Oi(qNmclZFm6>m+Cy>9FoNb_Ty8lNa4+xBi3ziTbVe zI3@YCZDdZmO1V0gwe^;B`~`NJ*xAq$>%wU;$PZnX70Wp-(v5{vMUgjMP&+GRrFZ41291a(=t!RCP#KRmI!>0pf9lFkt6p-+ zy9^lizwMtpf#1qAK)-2gX@-q=M#k{Oh!&h7QKRU$2acX3w~3GdL82zoDbwt9XpEhT zZel}j8I;&Mt$MlH!S88Lqlv=8PDlUfzF;2#MMW^=gtEJa3>KS2 zT4SSu=Rx0enqF~Lv= zspx=j?RdS0%)%^XNA>q2icFiuaH_{Z1TfLjlhWul7Nm){#WTHk8tMRfI;my2G) zK?qm3ClSgl+@7d}W&_Bs&~xD-g^vko^&$u^oQJ-S*lD@rB46aA1zp`2A^e4zi3%c+D^vY*h#3?a{ zMnnl4k^?aUdqdx(d2&$}X>tMvH00RY6h|_lD^SkT=?uDzJ{oZ<=8<6xdA0NoSLtAr zWWw1IN1gI>PBDubQER{(gfj%mTs+c9Gz_OC+>qr8uDfqN-+IegGBb-sZXs7wz9j~^ z0Wl-S!YU)qIg4Ra^26IIip=3b(tC`_&G5ECrL((SWoc&#UxJN*QBLAY{@KYAvy>j&qap^ zB+a8t(^>Jh$l@&R46|p?HuwGr@RgO7+M}ueM(vSbl0@8ftgD2xdq ze}5#6pF4L>J5-Ar8^ECD{iXyJt7>+y literal 0 HcmV?d00001 diff --git a/cpack/innosetup/gfx/logo_icon.bmp b/cpack/innosetup/gfx/logo_icon.bmp deleted file mode 100644 index 39c87ddf01d31678dbc24272ca9d5e0b4a8927df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40490 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 diff --git a/cpack/innosetup/gfx/logo_icon.ico b/cpack/innosetup/gfx/logo_icon.ico deleted file mode 100644 index aa9d4a3fbf19d3a3b05e90750d58e4aa62ea1b6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55742 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 Ie=2zVAG|eDDF6Tf diff --git a/cpack/innosetup/gfx/logo_icon.xcf b/cpack/innosetup/gfx/logo_icon.xcf deleted file mode 100644 index eafc9cddca5f992072f3a6ff018b3def9010fbba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36077 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 P;1N~mFO0@bAK3o_Fk9@I diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt deleted file mode 100644 index 4114f69..0000000 --- a/ecal/core/CMakeLists.txt +++ /dev/null @@ -1,586 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -project(core VERSION ${eCAL_VERSION_STRING}) - -find_package(Threads REQUIRED) -find_package(asio REQUIRED) -find_package(tclap REQUIRED) -find_package(simpleini REQUIRED) -find_package(tcp_pubsub REQUIRED) -if (ECAL_NPCAP_SUPPORT) - find_package(udpcap REQUIRED) -endif() - -if (ECAL_JOIN_MULTICAST_TWICE) - message(STATUS "eCAL ${PROJECT_NAME}: Enabling Specific Multicast Network Bug Workaround") - add_definitions(-DECAL_JOIN_MULTICAST_TWICE) -endif(ECAL_JOIN_MULTICAST_TWICE) - -# If we're currently doing a build within a git repository, we will configure the header files. -# Else, (e.g. for source packages such as debian source packages) we will use a preconfigured file. -# If there is really no information available, it will generate a dummy version file 0.0.0 -if (IS_GIT_TREE OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/ecal/ecal_defs.h") - configure_file(src/ecal_defs.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/ecal/ecal_defs.h" @ONLY) -endif (IS_GIT_TREE OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/ecal/ecal_defs.h") - -if(UNIX) - include (CheckSymbolExists) - set(CMAKE_REQUIRED_DEFINITIONS "-D__USE_GNU" - "-D_GNU_SOURCE") - set(CMAKE_REQUIRED_LIBRARIES "pthread") - check_symbol_exists(pthread_mutex_clocklock "pthread.h" ECAL_HAS_CLOCKLOCK_MUTEX) - check_symbol_exists(pthread_mutexattr_setrobust "pthread.h" ECAL_HAS_ROBUST_MUTEX) - unset(CMAKE_REQUIRED_DEFINITIONS) - unset(CMAKE_REQUIRED_LIBRARIES) - if(NOT ECAL_HAS_ROBUST_MUTEX) - if(NOT ECAL_HAS_CLOCKLOCK_MUTEX) - message(WARNING "This OS does not support robust mutexes with monotonic clock which can cause dead locks under certain circumstances. (e.g. memfile monitoring).") - else() - message(WARNING "This OS does not support robust mutexes which can cause dead locks under certain circumstances (e.g. memfile monitoring).") - endif() - endif() -endif() - - -if(ECAL_NPCAP_SUPPORT) -set(ecal_io_cpp_src_npcap - src/io/udp_receiver_npcap.cpp -) -endif() - -set(ecal_custom_tclap_cpp_src - src/custom_tclap/advanced_tclap_output.cpp -) - -set(ecal_io_cpp_src - src/io/rcv_sample.cpp - src/io/snd_raw_buffer.cpp - src/io/snd_sample.cpp - src/io/udp_configurations.cpp - src/io/udp_init.cpp - src/io/udp_receiver.cpp - src/io/udp_receiver_asio.cpp - src/io/udp_sender.cpp - ${ecal_io_cpp_src_npcap} -) - -if(NOT ECAL_LAYER_ICEORYX) - set(ecal_io_mem_cpp_src - src/io/ecal_memfile.cpp - src/io/ecal_memfile_db.cpp - src/io/ecal_memfile_broadcast.cpp - src/io/ecal_memfile_broadcast_reader.cpp - src/io/ecal_memfile_broadcast_writer.cpp - src/io/ecal_memfile_naming.cpp - src/io/ecal_memfile_pool.cpp - src/io/ecal_memfile_sync.cpp - src/io/ecal_named_mutex.cpp - ) - if(UNIX) - set(ecal_io_mem_cpp_linux_src - src/io/linux/ecal_memfile_os.cpp - src/io/linux/ecal_named_mutex_impl.cpp - $<$,$>:src/io/linux/ecal_named_mutex_robust_clocklock_impl.cpp> - ) - endif() - - if(WIN32) - set(ecal_io_mem_cpp_win_src - src/io/win32/ecal_memfile_os.cpp - src/io/win32/ecal_named_mutex_impl.cpp - ) - endif() -endif(NOT ECAL_LAYER_ICEORYX) - -set(ecal_mon_cpp_src - src/mon/ecal_monitoring_def.cpp - src/mon/ecal_monitoring_impl.cpp - src/mon/ecal_monitoring_threads.cpp -) - -set(ecal_pubsub_cpp_src - src/pubsub/ecal_proto_dyn_json_sub.cpp - src/pubsub/ecal_pubgate.cpp - src/pubsub/ecal_publisher.cpp - src/pubsub/ecal_subgate.cpp - src/pubsub/ecal_subscriber.cpp -) - -set(ecal_readwrite_cpp_src - src/readwrite/ecal_reader.cpp - src/readwrite/ecal_reader_udp_mc.cpp - src/readwrite/ecal_reader_tcp.cpp - src/readwrite/ecal_writer.cpp - src/readwrite/ecal_writer_inproc.cpp - src/readwrite/ecal_writer_udp_mc.cpp - src/readwrite/ecal_writer_tcp.cpp -) - -if(ECAL_LAYER_ICEORYX) - set(ecal_readwrite_shm_cpp_src - src/readwrite/ecal_reader_iceoryx.cpp - src/readwrite/ecal_writer_iceoryx.cpp - ) -else(ECAL_LAYER_ICEORYX) - set(ecal_readwrite_shm_cpp_src - src/readwrite/ecal_reader_shm.cpp - src/readwrite/ecal_writer_shm.cpp - ) -endif(ECAL_LAYER_ICEORYX) - -set(ecal_service_cpp_src - src/service/ecal_clientgate.cpp - src/service/ecal_service_client.cpp - src/service/ecal_service_client_impl.cpp - src/service/ecal_service_server.cpp - src/service/ecal_service_server_impl.cpp - src/service/ecal_tcpclient.cpp - src/service/ecal_servicegate.cpp - src/service/ecal_tcpserver.cpp -) - -set(ecal_cmn_cpp_src - src/convert_utf.cpp - src/ecal.cpp - src/ecal_clang.cpp - src/ecal_config.cpp - src/ecal_config_reader.cpp - src/ecal_descgate.cpp - src/ecal_event.cpp - src/ecal_global_accessors.cpp - src/ecal_globals.cpp - src/ecal_log.cpp - src/ecal_log_impl.cpp - src/ecal_process.cpp - src/ecal_registration_provider.cpp - src/ecal_registration_receiver.cpp - src/ecal_thread.cpp - src/ecal_time.cpp - src/ecal_timegate.cpp - src/ecal_timer.cpp - src/ecal_util.cpp - src/ecalc.cpp - src/sys_usage.cpp -) - -if(ECAL_NPCAP_SUPPORT) -set(ecal_io_header_src_npcap - src/io/udp_receiver_npcap.h -) -endif() - -set(ecal_custom_tclap_header_src - src/custom_tclap/advanced_tclap_output.h -) - -set(ecal_io_header_src - src/io/ecal_memfile_header.h - src/io/ecal_receiver.h - src/io/ecal_sender.h - src/io/msg_type.h - src/io/rcv_sample.h - src/io/snd_raw_buffer.h - src/io/snd_sample.h - src/io/udp_configurations.h - src/io/udp_init.h - src/io/udp_receiver.h - src/io/udp_receiver_base.h - src/io/udp_receiver_asio.h - src/io/udp_sender.h - ${ecal_io_header_src_npcap} -) - -if (UNIX) - list (APPEND - ecal_io_header_src - "src/io/linux/ecal_socket_option_linux.h") -endif() - -if(NOT ECAL_LAYER_ICEORYX) - set(ecal_io_mem_header_src - src/io/ecal_memfile.h - src/io/ecal_memfile_broadcast.h - src/io/ecal_memfile_broadcast_reader.h - src/io/ecal_memfile_broadcast_writer.h - src/io/ecal_memfile_db.h - src/io/ecal_memfile_info.h - src/io/ecal_memfile_naming.h - src/io/ecal_memfile_os.h - src/io/ecal_memfile_pool.h - src/io/ecal_memfile_sync.h - src/io/ecal_named_mutex.h - src/io/ecal_named_mutex_base.h - ) - - if(WIN32) - set(ecal_io_mem_header_win_src - src/io/win32/ecal_named_mutex_impl.h - ) - endif() - - if(UNIX) - set(ecal_io_mem_header_linux_src - src/io/linux/ecal_named_mutex_impl.h - $<$,$>:src/io/linux/ecal_named_mutex_robust_clocklock_impl.h> - ) - endif() -endif(NOT ECAL_LAYER_ICEORYX) - -set(ecal_mon_header_src - src/mon/ecal_monitoring_def.h - src/mon/ecal_monitoring_impl.h - src/mon/ecal_monitoring_threads.h -) - -set(ecal_pubsub_header_src - src/pubsub/ecal_pubgate.h - src/pubsub/ecal_subgate.h -) - -set(ecal_readwrite_header_src - src/readwrite/ecal_reader.h - src/readwrite/ecal_reader_layer.h - src/readwrite/ecal_reader_tcp.h - src/readwrite/ecal_reader_udp_mc.h - src/readwrite/ecal_writer.h - src/readwrite/ecal_writer_base.h - src/readwrite/ecal_writer_data.h - src/readwrite/ecal_writer_info.h - src/readwrite/ecal_writer_inproc.h - src/readwrite/ecal_writer_tcp.h - src/readwrite/ecal_writer_udp_mc.h - src/readwrite/ecal_tcp_pubsub_logger.h -) - -if(ECAL_LAYER_ICEORYX) - set(ecal_readwrite_shm_header_src - src/readwrite/ecal_reader_iceoryx.h - src/readwrite/ecal_writer_iceoryx.h - ) -else(ECAL_LAYER_ICEORYX) - set(ecal_readwrite_shm_header_src - src/readwrite/ecal_reader_shm.h - src/readwrite/ecal_writer_shm.h - ) -endif(ECAL_LAYER_ICEORYX) - -set(ecal_service_header_src - src/service/asio_server.h - src/service/ecal_clientgate.h - src/service/ecal_service_client_impl.h - src/service/ecal_service_server_impl.h - src/service/ecal_servicegate.h - src/service/ecal_tcpclient.h - src/service/ecal_tcpserver.h -) - -set(ecal_cmn_header_src - src/convert_utf.h - src/ecal_config_reader.h - src/ecal_config_reader_hlp.h - src/ecal_def.h - src/ecal_def_ini.h - src/ecal_descgate.h - src/ecal_expmap.h - src/ecal_global_accessors.h - src/ecal_globals.h - src/ecal_log_impl.h - src/ecal_registration_provider.h - src/ecal_registration_receiver.h - src/ecal_thread.h - src/ecal_timegate.h - $<$:src/ecal_win_main.h> - $<$:src/ecal_win_socket.h> - src/getenvvar.h - src/sys_usage.h - src/topic2mcast.h -) - -set(ecal_c_src - src/ecalc.cpp -) - -if(WIN32) - set(ecal_c_win_src - src/win32/dll/dllmain.cpp - src/win32/dll/ecal.rc - ) -endif() - -set(ecal_header_cmn - include/ecal/ecal.h - include/ecal/ecal_callback.h - include/ecal/ecal_clang.h - include/ecal/ecal_config.h - include/ecal/ecal_client.h - include/ecal/ecal_config.h - include/ecal/ecal_core.h - include/ecal/ecal_event.h - include/ecal/ecal_eventhandle.h - include/ecal/ecal_init.h - include/ecal/ecal_log.h - include/ecal/ecal_log_level.h - include/ecal/ecal_monitoring.h - include/ecal/ecal_os.h - include/ecal/ecal_process.h - include/ecal/ecal_process_mode.h - include/ecal/ecal_process_severity.h - include/ecal/ecal_publisher.h - include/ecal/ecal_qos.h - include/ecal/ecal_server.h - include/ecal/ecal_service.h - include/ecal/ecal_service_info.h - include/ecal/ecal_subscriber.h - include/ecal/ecal_time.h - include/ecal/ecal_timed_cb.h - include/ecal/ecal_timer.h - include/ecal/ecal_tlayer.h - include/ecal/ecal_util.h - include/ecal/ecalc.h - include/ecal/ecalc_types.h -) - -set(ecal_header_cimpl - include/ecal/cimpl/ecal_callback_cimpl.h - include/ecal/cimpl/ecal_client_cimpl.h - include/ecal/cimpl/ecal_core_cimpl.h - include/ecal/cimpl/ecal_event_cimpl.h - include/ecal/cimpl/ecal_init_cimpl.h - include/ecal/cimpl/ecal_log_cimpl.h - include/ecal/cimpl/ecal_monitoring_cimpl.h - include/ecal/cimpl/ecal_process_cimpl.h - include/ecal/cimpl/ecal_proto_dyn_json_subscriber_cimpl.h - include/ecal/cimpl/ecal_publisher_cimpl.h - include/ecal/cimpl/ecal_qos_cimpl.h - include/ecal/cimpl/ecal_server_cimpl.h - include/ecal/cimpl/ecal_service_cimpl.h - include/ecal/cimpl/ecal_service_info_cimpl.h - include/ecal/cimpl/ecal_subscriber_cimpl.h - include/ecal/cimpl/ecal_time_cimpl.h - include/ecal/cimpl/ecal_timer_cimpl.h - include/ecal/cimpl/ecal_tlayer_cimpl.h - include/ecal/cimpl/ecal_util_cimpl.h -) - -set(ecal_header_msg - include/ecal/msg/capnproto/dynamic.h - include/ecal/msg/capnproto/helper.h - include/ecal/msg/capnproto/publisher.h - include/ecal/msg/capnproto/subscriber.h - include/ecal/msg/flatbuffers/publisher.h - include/ecal/msg/flatbuffers/subscriber.h - include/ecal/msg/messagepack/publisher.h - include/ecal/msg/messagepack/subscriber.h - include/ecal/msg/protobuf/client.h - include/ecal/msg/protobuf/dynamic_json_subscriber.h - include/ecal/msg/protobuf/dynamic_publisher.h - include/ecal/msg/protobuf/dynamic_subscriber.h - include/ecal/msg/protobuf/publisher.h - include/ecal/msg/protobuf/server.h - include/ecal/msg/protobuf/subscriber.h - include/ecal/msg/string/publisher.h - include/ecal/msg/string/subscriber.h - include/ecal/msg/dynamic.h - include/ecal/msg/publisher.h - include/ecal/msg/subscriber.h -) - -set(ecal_header_base - ${ecal_header_cmn} - ${ecal_header_cimpl} - ${ecal_header_msg} -) - -ecal_add_ecal_shared_library(${PROJECT_NAME} - ${ecal_custom_tclap_cpp_src} - ${ecal_io_cpp_src} - ${ecal_io_mem_cpp_src} - ${ecal_io_mem_cpp_win_src} - ${ecal_io_mem_cpp_linux_src} - ${ecal_mon_cpp_src} - ${ecal_pubsub_cpp_src} - ${ecal_readwrite_cpp_src} - ${ecal_readwrite_shm_cpp_src} - ${ecal_service_cpp_src} - ${ecal_cmn_cpp_src} - - ${ecal_custom_tclap_header_src} - ${ecal_cmn_header_src} - ${ecal_io_header_src} - ${ecal_io_mem_header_src} - ${ecal_io_mem_header_win_src} - ${ecal_io_mem_header_linux_src} - ${ecal_mon_header_src} - ${ecal_pubsub_header_src} - ${ecal_readwrite_header_src} - ${ecal_readwrite_shm_header_src} - ${ecal_service_header_src} - - ${ecal_header_base} - ${ecal_header_cimpl} - ${ecal_header_msg} - - ${CMAKE_CURRENT_BINARY_DIR}/include/ecal/ecal_defs.h -) - -if(UNIX) - set_source_files_properties(src/convert_utf.cpp PROPERTIES COMPILE_FLAGS -Wno-implicit-fallthrough) -endif() - -ecal_add_ecal_shared_library(${PROJECT_NAME}_c ${ecal_c_src} ${ecal_c_win_src}) - -add_library(eCAL::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -add_library(eCAL::${PROJECT_NAME}_c ALIAS ${PROJECT_NAME}_c) - -target_link_libraries(${PROJECT_NAME}_c ${PROJECT_NAME}) - -target_compile_definitions(${PROJECT_NAME}_c - INTERFACE ECAL_C_DLL - PUBLIC - ASIO_STANDALONE - ASIO_DISABLE_VISIBILITY - PRIVATE eCAL_EXPORTS) - -target_compile_definitions(${PROJECT_NAME} - PUBLIC - ASIO_STANDALONE - ASIO_DISABLE_VISIBILITY - PRIVATE - eCAL_EXPORTS - $<$:ECAL_HAS_CLOCKLOCK_MUTEX> - $<$:ECAL_HAS_ROBUST_MUTEX> - $<$:ECAL_USE_CLOCKLOCK_MUTEX>) - -if(ECAL_LAYER_ICEORYX) - find_package(iceoryx_posh REQUIRED) - target_link_libraries(${PROJECT_NAME} PRIVATE iceoryx_posh::iceoryx_posh) - target_compile_definitions(${PROJECT_NAME} PUBLIC ECAL_LAYER_ICEORYX) -endif() - -if(ECAL_NPCAP_SUPPORT) - target_compile_definitions(${PROJECT_NAME} - PRIVATE ECAL_NPCAP_SUPPORT) - target_link_libraries(${PROJECT_NAME} - PRIVATE - udpcap::udpcap - ) -endif(ECAL_NPCAP_SUPPORT) - -target_include_directories(${PROJECT_NAME} - PRIVATE - $ - PUBLIC - $ - $ - $ -) - -target_link_libraries(${PROJECT_NAME} - PUBLIC - eCAL::proto - PRIVATE - $<$,$>>:dl> - $<$,$>,$>>:rt> - $<$:util> - $<$:iphlpapi> - $<$:psapi> - $<$:shlwapi.lib> - $<$:winmm> - $<$:ws2_32> - $<$:wsock32> - $<$:socket> - asio::asio - tclap::tclap - simpleini::simpleini - eCAL::core_pb - Threads::Threads - eCAL::ecal-utils - tcp_pubsub::tcp_pubsub -) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER ecal/core) -set_property(TARGET ${PROJECT_NAME}_c PROPERTY FOLDER ecal/core) - -ecal_install_ecal_shared_library(${PROJECT_NAME}_c) -ecal_install_ecal_shared_library(${PROJECT_NAME}) - -install(DIRECTORY - "include/" DESTINATION "${INSTALL_INCLUDE_DIR}" COMPONENT sdk - FILES_MATCHING PATTERN "*.h") - -#Install generated ecal_defs.h file -install(DIRECTORY - "${CMAKE_CURRENT_BINARY_DIR}/include/" DESTINATION "${INSTALL_INCLUDE_DIR}" COMPONENT sdk - FILES_MATCHING PATTERN "*.h") - - -if(NOT ${CMAKE_VERSION} VERSION_LESS "3.8.0") - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES - ${ecal_custom_tclap_cpp_src} - ${ecal_io_cpp_src} - ${ecal_io_mem_cpp_src} - ${ecal_io_mem_cpp_linux_src} - ${ecal_io_mem_cpp_win_src} - ${ecal_io_cpp_linux_src} - ${ecal_io_cpp_win32_src} - ${ecal_mon_cpp_src} - ${ecal_pubsub_cpp_src} - ${ecal_readwrite_cpp_src} - ${ecal_readwrite_shm_cpp_src} - ${ecal_service_cpp_src} - ${ecal_cmn_cpp_src} - ${ecal_c_src} - ${ecal_c_win_src} - ) - - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Header Files\\internal" FILES - ${ecal_custom_tclap_header_src} - ${ecal_cmn_header_src} - ${ecal_io_header_src} - ${ecal_io_mem_header_src} - ${ecal_io_mem_header_win_src} - ${ecal_io_header_linux_src} - ${ecal_io_header_win_src} - ${ecal_mon_header_src} - ${ecal_pubsub_header_src} - ${ecal_readwrite_header_src} - ${ecal_readwrite_shm_header_src} - ${ecal_service_header_src} - ) - - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include" PREFIX "Header Files\\public" FILES - ${ecal_header_cmn} - ${ecal_header_cimpl} - ${ecal_header_msg} - ) -endif() - -# eCAL Process stub -if(UNIX) - set(PROJECT_NAME_PROCESS_STUB process_stub) - - ecal_add_app_console(${PROJECT_NAME_PROCESS_STUB} - src/ecal_process_stub.cpp - src/ecal_process_stub.h - ) - - ecal_install_app(${PROJECT_NAME_PROCESS_STUB}) - - set_property(TARGET ${PROJECT_NAME_PROCESS_STUB} PROPERTY FOLDER ecal/core) -endif(UNIX) \ No newline at end of file diff --git a/ecal/core/cfg/ecal.ini b/ecal/core/cfg/ecal.ini deleted file mode 100644 index b38ea11..0000000 --- a/ecal/core/cfg/ecal.ini +++ /dev/null @@ -1,194 +0,0 @@ -; -------------------------------------------------- -; NETWORK SETTINGS -; -------------------------------------------------- -; network_enabled = true / false true = all eCAL components communicate over network boundaries -; false = local host only communication -; -; multicast_config_version = v1 / v2 UDP configuration version (Since eCAL 5.12.) -; v1: default behavior -; v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups -; multicast_group = 239.0.0.1 UDP multicast group base -; All registration and logging is sent on this address -; multicast_mask = 0.0.0.1-0.0.0.255 v1: Mask maximum number of dynamic multicast group -; 255.0.0.0-255.255.255.255 v2: masks are now considered like routes masking -; -; multicast_port = 14000 + x UDP multicast port number (eCAL will use at least the 2 following port -; numbers too, so please modify in steps of 10 (e.g. 1010, 1020 ...) -; -; multicast_ttl = 0 + x UDP ttl value, also known as hop limit, is used in determining -; the intermediate routers being traversed towards the destination -; -; multicast_sndbuf = 1024 * x UDP send buffer in bytes -; -; multicast_rcvbuf = 1024 * x UDP receive buffer in bytes -; -; multicast_join_all_if = false Linux specific setting to enable joining multicast groups on all network interfacs -; independent of their link state. Enabling this makes sure that eCAL processes -; receive data if they are started before network devices are up and running. -; -; bandwidth_max_udp = -1 UDP bandwidth limit for eCAL udp layer (-1 == unlimited) -; -; inproc_rec_enabled = true Enable to receive on eCAL inner process layer -; shm_rec_enabled = true Enable to receive on eCAL shared memory layer -; udp_mc_rec_enabled = true Enable to receive on eCAL udp multicast layer -; -; npcap_enabled = false Enable to receive UDP traffic with the Npcap based receiver -; -; tcp_pubsub_num_executor_reader = 4 Tcp_pubsub reader amount of threads that shall execute workload -; tcp_pubsub_num_executor_writer = 4 Tcp_pubsub writer amount of threads that shall execute workload -; tcp_pubsub_max_reconnections = 5 Tcp_pubsub reconnection attemps the session will try to reconnect in -; case of an issue (a negative value means infinite reconnection attemps) -; -------------------------------------------------- - -[network] -network_enabled = false -multicast_config_version = v1 -multicast_group = 239.0.0.1 -multicast_mask = 0.0.0.15 -multicast_port = 14000 -multicast_ttl = 2 -multicast_sndbuf = 5242880 -multicast_rcvbuf = 5242880 - -multicast_join_all_if = false - -bandwidth_max_udp = -1 - -inproc_rec_enabled = true -shm_rec_enabled = true -tcp_rec_enabled = true -udp_mc_rec_enabled = true - -npcap_enabled = false - -tcp_pubsub_num_executor_reader = 4 -tcp_pubsub_num_executor_writer = 4 -tcp_pubsub_max_reconnections = 5 - -; -------------------------------------------------- -; COMMON SETTINGS -; -------------------------------------------------- -; registration_timeout = 60000 Timeout for topic registration in ms (internal) -; registration_refresh = 1000 Topic registration refresh cylce (has to be smaller then registration timeout !) -; -------------------------------------------------- -[common] -registration_timeout = 60000 -registration_refresh = 1000 - -; -------------------------------------------------- -; TIME SETTINGS -; -------------------------------------------------- -; timesync_module_rt = "ecaltime-localtime" Time synchronisation interface name (dynamic library) -; The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so) -; -; Available modules are: -; - ecaltime-localtime local system time without synchronization -; - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux -; (device configuration in ecaltime.ini) -; - ecaltime-simtime Simulation time as published by the eCAL Player. -; -------------------------------------------------- -[time] -timesync_module_rt = "ecaltime-localtime" - -; -------------------------------------------------- -; ICEORYX SETTINGS -; -------------------------------------------------- -; service = "eCAL" Default Iceoryx service name -; instance = "" Default Iceoryx service instance name -; -------------------------------------------------- - -; --------------------------------------------- -; PROCESS SETTINGS -; --------------------------------------------- -; -; terminal_emulator = /usr/bin/x-terminal-emulator -e command for starting applications with an external terminal emulator. If empty, the command will be ignored. Ignored on Windows. -; e.g. /usr/bin/x-terminal-emulator -e -; /usr/bin/gnome-terminal -x -; /usr/bin/xterm -e -; -; --------------------------------------------- -[process] -terminal_emulator = - -[iceoryx] -service = "eCAL" -instance = "" - -; -------------------------------------------------- -; PUBLISHER SETTINGS -; -------------------------------------------------- -; use_inproc = 0, 1, 2 Use inner process transport layer (0 = off, 1 = on, 2 = auto, default = 0) -; use_shm = 0, 1, 2 Use shared memory transport layer (0 = off, 1 = on, 2 = auto, default = 2) -; use_tcp = 0, 1, 2 Use tcp transport layer (0 = off, 1 = on, 2 = auto, default = 0) -; use_udp_mc = 0, 1, 2 Use udp multicast transport layer (0 = off, 1 = on, 2 = auto, default = 2) -; -; memfile_minsize = x * 4096 kB Default memory file size for new publisher -; -; memfile_reserve = 50 .. x % Dynamic file size reserve before recreating memory file if topic size changes -; -; memfile_ack_timeout = 0 .. x ms Publisher timeout for ack event from subscriber that memory file content is processed -; -; memfile_buffer_count = 1 .. x Number of parallel used memory file buffers for 1:n publish/subscribe ipc connections (default = 1) -; memfile_zero_copy = 0, 1 Allow matching subscriber to access memory file without copying its content in advance (blocking mode) -; -; share_ttype = 0, 1 Share topic type via registration layer -; share_tdesc = 0, 1 Share topic description via registration layer (switch off to disable reflection) -; -------------------------------------------------- -[publisher] -use_inproc = 0 -use_shm = 2 -use_tcp = 0 -use_udp_mc = 2 - -memfile_minsize = 4096 -memfile_reserve = 50 -memfile_ack_timeout = 0 -memfile_buffer_count = 1 -memfile_zero_copy = 0 - -share_ttype = 1 -share_tdesc = 1 - -; -------------------------------------------------- -; MONITORING SETTINGS -; -------------------------------------------------- -; timeout = 1000 + (x * 1000) Timeout for topic monitoring in ms -; filter_excl = __.* Topics blacklist as regular expression (will not be monitored) -; filter_incl = Topics whitelist as regular expression (will be monitored only) -; filter_log_con = warning, error Log messages logged to console (all, info, warning, error, fatal, debug1, debug2, debug3, debug4) -; filter_log_file = Log messages to logged into file system -; filter_log_udp = warning, error, fatal Log messages logged via udp network -; -------------------------------------------------- -[monitoring] -timeout = 5000 -filter_excl = __.* -filter_incl = -filter_log_con = error, fatal, warning -filter_log_file = -filter_log_udp = info, warning, error, fatal - -; -------------------------------------------------- -; SYS SETTINGS -; -------------------------------------------------- -; filter_excl = App1,App2 Apps blacklist to be excluded when importing tasks from cloud -; -------------------------------------------------- -[sys] -filter_excl = ^eCALSysClient$|^eCALSysGUI$|^eCALSys$ - -; -------------------------------------------------- -; EXPERIMENTAL SETTINGS -; -------------------------------------------------- -; shm_monitoring_enabled = false Enable distribution of monitoring/registration information via shared memory -; shm_monitoring_domain = ecal_monitoring Domain name for shared memory based monitoring/registration -; shm_monitoring_queue_size = 1024 Queue size of monitoring/registration events -; network_monitoring_disabled = false Disable distribution of monitoring/registration information via network (default) -; -; drop_out_of_order_messages = false Enable dropping of payload messages that arrive out of order -; -------------------------------------------------- -[experimental] -shm_monitoring_enabled = false -shm_monitoring_domain = ecal_monitoring -shm_monitoring_queue_size = 1024 -network_monitoring_disabled = false - -drop_out_of_order_messages = false diff --git a/ecal/core/include/ecal/cimpl/ecal_event_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_event_cimpl.h deleted file mode 100644 index 6cc35b6..0000000 --- a/ecal/core/include/ecal/cimpl/ecal_event_cimpl.h +++ /dev/null @@ -1,84 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_event_cimpl.h - * @brief eCAL event c interface -**/ - -#ifndef ecal_event_cimpl_h_included -#define ecal_event_cimpl_h_included - -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif /*__cplusplus*/ - /** - * @brief Open a named or unnamed event. - * - * @param event_name_ Event name ("" == unnamed). - * - * @return Handle to opened event or NULL if failed. - **/ - ECALC_API ECAL_HANDLE eCAL_Event_gOpenEvent(const char* event_name_); - - /** - * @brief Close an event. - * - * @param handle_ Event handle. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Event_gCloseEvent(ECAL_HANDLE handle_); - - /** - * @brief Set an event active. - * - * @param handle_ Event handle. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Event_gSetEvent(ECAL_HANDLE handle_); - - /** - * @brief Wait for an event with timeout. - * - * @param handle_ Event handle. - * @param timeout_ Timeout in ms (-1 == infinite). - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Event_gWaitForEvent(ECAL_HANDLE handle_, long timeout_); - - /** - * @brief Check whether an event is valid or not. - * - * @param handle_ Event handle. - * - * @return None zero if event is valid. - **/ - ECALC_API int eCAL_Event_gEventIsValid(ECAL_HANDLE handle_); -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -#endif /*ecal_event_cimpl_h_included*/ diff --git a/ecal/core/include/ecal/cimpl/ecal_proto_dyn_json_subscriber_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_proto_dyn_json_subscriber_cimpl.h deleted file mode 100644 index 054f60e..0000000 --- a/ecal/core/include/ecal/cimpl/ecal_proto_dyn_json_subscriber_cimpl.h +++ /dev/null @@ -1,92 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_proto_dyn_json_subscriber_cimpl.h - * @brief eCAL subscriber c interface -**/ - -#ifndef ecal_proto_dyn_json_subscriber_cimpl_h_included -#define ecal_proto_dyn_json_subscriber_cimpl_h_included - -#include -#include - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif /*__cplusplus*/ - /** - * @brief Create a subscriber. - * - * @param topic_name_ Unique topic name. - * - * @return Handle to created subscriber or NULL if failed. - **/ - ECALC_API ECAL_HANDLE eCAL_Proto_Dyn_JSON_Sub_Create(const char* topic_name_); - - /** - * @brief Destroy a subscriber. - * - * @param handle_ Subscriber handle. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Proto_Dyn_JSON_Sub_Destroy(ECAL_HANDLE handle_); - - /** - * @brief Add callback function for incoming receives. - * @since eCAL 5.10.0 - * - * @param handle_ Subscriber handle. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Proto_Dyn_JSON_Sub_AddReceiveCallback(ECAL_HANDLE handle_, ReceiveCallbackCT callback_, void* par_); - - /** - * @deprecated Please use eCAL_Proto_Dyn_JSON_Sub_AddReceiveCallback instead - * @brief Add callback function for incoming receives. - * - * @param handle_ Subscriber handle. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return None zero if succeeded. - **/ - ECALC_API_DEPRECATED int eCAL_Proto_Dyn_JSON_Sub_AddReceiveCallbackC(ECAL_HANDLE handle_, ReceiveCallbackCT callback_, void* par_); - - /** - * @brief Remove callback function for incoming receives. - * - * @param handle_ Subscriber handle. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Proto_Dyn_JSON_Sub_RemReceiveCallback(ECAL_HANDLE handle_); - -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -#endif /*ecal_proto_dyn_json_subscriber_cimpl_h_included*/ diff --git a/ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h deleted file mode 100644 index 9d92453..0000000 --- a/ecal/core/include/ecal/cimpl/ecal_publisher_cimpl.h +++ /dev/null @@ -1,297 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_publisher_cimpl.h - * @brief eCAL publisher c interface -**/ - -#ifndef ecal_publisher_cimpl_h_included -#define ecal_publisher_cimpl_h_included - -#include -#include - -#include - -#include "ecal_qos_cimpl.h" -#include "ecal_tlayer_cimpl.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /*__cplusplus*/ - /** - * @brief Instance a publisher. - * - * @return Handle to new publisher or NULL if failed. - **/ - ECALC_API ECAL_HANDLE eCAL_Pub_New(); - - /** - * @brief Create a publisher. - * - * @param handle_ Publisher handle. - * @param topic_name_ Unique topic name. - * @param topic_type_ Topic type name. - * @param topic_desc_ Topic type description. - * @param topic_desc_len_ Topic type description length. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_, const char* topic_desc_, int topic_desc_len_); - - /** - * @brief Destroy a publisher. - * - * @param handle_ Publisher handle. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_Destroy(ECAL_HANDLE handle_); - - /** - * @brief Setup topic type name. - * - * @param handle_ Publisher handle. - * @param topic_type_name_ Topic type name. - * @param topic_type_name_len_ Topic type name length. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_SetTypeName(ECAL_HANDLE handle_, const char* topic_type_name_, int topic_type_name_len_); - - /** - * @brief Setup topic type description. - * - * @param handle_ Publisher handle. - * @param topic_desc_ Topic type description. - * @param topic_desc_len_ Topic type description length. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_SetDescription(ECAL_HANDLE handle_, const char* topic_desc_, int topic_desc_len_); - - /** - * @brief Sets publisher attribute. - * - * @param handle_ Publisher handle. - * @param attr_name_ Attribute name. - * @param attr_name_len_ Attribute name length. - * @param attr_value_ Attribute value. - * @param attr_value_len_ Attribute value length. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_SetAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_, const char* attr_value_, int attr_value_len_); - - /** - * @brief Removes publisher attribute. - * - * @param handle_ Publisher handle. - * @param attr_name_ Attribute name. - * @param attr_name_len_ Attribute name length. - * - * @return None zero if succeeded. - * @experimental - **/ - ECALC_API int eCAL_Pub_ClearAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_); - - /** - * @brief Share topic type. - * - * @param handle_ Publisher handle. - * @param state_ Set type share mode (none zero == share type). - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_ShareType(ECAL_HANDLE handle_, int state_); - - /** - * @brief Share topic description. - * - * @param handle_ Publisher handle. - * @param state_ Set description share mode (none zero == share description). - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_ShareDescription(ECAL_HANDLE handle_, int state_); - - /** - * @brief Set publisher quality of service attributes. - * - * @param handle_ Publisher handle. - * @param qos_ Quality of service policies. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_SetQOS(ECAL_HANDLE handle_, struct SWriterQOSC qos_); - - /** - * @brief Get publisher quality of service attributes. - * - * @param handle_ Publisher handle. - * @param qos_ Quality of service policies. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_GetQOS(ECAL_HANDLE handle_, struct SWriterQOSC* qos_); - - /** - * @brief Set publisher send mode for specific transport layer. - * - * @param handle_ Publisher handle. - * @param layer_ Transport layer. - * @param mode_ Send mode. - * - * @return True if it succeeds, false if it fails. - **/ - ECALC_API int eCAL_Pub_SetLayerMode(ECAL_HANDLE handle_, enum eTransportLayerC layer_, enum eSendModeC mode_); - - /** - * @brief Set publisher maximum transmit bandwidth for the udp layer. - * - * @param handle_ Publisher handle. - * @param bandwidth_ Maximum bandwidth in bytes/s (-1 == unlimited). - * - * @return True if it succeeds, false if it fails. - **/ - ECALC_API int eCAL_Pub_SetMaxBandwidthUDP(ECAL_HANDLE handle_, long bandwidth_); - - /** - * @brief Set publisher maximum number of used shared memory buffers. - * - * @param buffering_ Maximum number of used buffers (needs to be greater than 1, default = 1). - * - * @return True if it succeeds, false if it fails. - **/ - ECALC_API int eCAL_Pub_ShmSetBufferCount(ECAL_HANDLE handle_, long buffering_); - - /** - * @brief Enable zero copy shared memory trasnport mode. - * - * By default, the builtin shared memory layer is configured to make one memory copy - * on the receiver side. That means the payload is copied by the internal eCAL memory pool manager - * out of the memory file and the file is closed immediately after this. - * The intention of this implementation is to free the file as fast as possible after reading - * its content to allow other subscribing processes to access the content with minimal latency. - * The different reading subscribers are fully decoupled and can access their memory copy - * independently. - * - * If ShmEnableZeroCopy is switched on no memory will be copied at all. The user message callback is - * called right after opening the memory file. A direct pointer to the memory payload is forwarded - * and can be processed with no latency. The memory file will be closed after the user callback function - * returned. The advantage of this configuration is a much higher performance for large payloads (> 1024 kB). - * The disadvantage of this configuration is that in the time when the callback is executed the memory file - * is blocked for other subscribers and for writing publishers too. Maybe this can be eliminated - * by a better memory file read/write access implementation (lock free read) in future releases. - * - * Today, for specific scenarios (1:1 pub/sub connections with large payloads for example) this feature - * can increase the performance remarkable. But please keep in mind to return from the message callback function - * as fast as possible to not delay subsequent read/write access operations. - * - * @state_ Set type zero copy mode for shared memory trasnport layer (true == zero copy enabled). - * - * @return True if it succeeds, false if it fails. - **/ - ECALC_API int eCAL_Pub_ShmEnableZeroCopy(ECAL_HANDLE handle_, int state_); - - /** - * @brief Set publisher maximum transmit bandwidth for the udp layer. - * - * @param handle_ Publisher handle. - * @param id_ The topic id for subscriber side filtering (0 == no id). - * - * @return True if it succeeds, false if it fails. - **/ - ECALC_API int eCAL_Pub_SetID(ECAL_HANDLE handle_, long long id_); - - /** - * @brief Query if the publisher is subscribed. - * - * @param handle_ Publisher handle. - * - * @return None zero if subscribed. - **/ - ECALC_API int eCAL_Pub_IsSubscribed(ECAL_HANDLE handle_); - - /** - * @brief Send a message to all subscribers. - * - * @param handle_ Publisher handle. - * @param buf_ Buffer that contains content to send. - * @param buf_len_ Send buffer length. - * @param time_ Send time (-1 = use eCAL system time in us, default = -1). - * - * @return Number of bytes sent. - **/ - ECALC_API int eCAL_Pub_Send(ECAL_HANDLE handle_, const void* const buf_, int buf_len_, long long time_); - - /** - * @brief Add callback function for publisher events. - * @since eCAL 5.10.0 - * - * @param handle_ Publisher handle. - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_AddEventCallback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_, PubEventCallbackCT callback_, void* par_); - - /** - * @brief Add callback function for publisher events. - * @deprecated Please use eCAL_Pub_AddEventCallback instead - * - * @param handle_ Publisher handle. - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return None zero if succeeded. - **/ - ECALC_API_DEPRECATED int eCAL_Pub_AddEventCallbackC(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_, PubEventCallbackCT callback_, void* par_); - - /** - * @brief Remove callback function for publisher events. - * - * @param handle_ Publisher handle. - * @param type_ The event type to remove. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Pub_RemEventCallback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_); - - /** - * @brief Dump the whole class state into a string buffer. - * - * @param handle_ Publisher handle. - * @param [out] buf_ Pointer to store the monitoring information. - * @param buf_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Dump buffer length or zero if failed. - **/ - ECALC_API int eCAL_Pub_Dump(ECAL_HANDLE handle_, void* buf_, int buf_len_); -#ifdef __cplusplus -} -#endif /*__cplusplus*/ - -#endif /*ecal_publisher_cimpl_h_included*/ diff --git a/ecal/core/include/ecal/cimpl/ecal_qos_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_qos_cimpl.h deleted file mode 100644 index 01acc0c..0000000 --- a/ecal/core/include/ecal/cimpl/ecal_qos_cimpl.h +++ /dev/null @@ -1,66 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_qos_cimpl.h - * @brief eCAL quality of service settings -**/ - -#ifndef ecal_qos_cimpl_h_included -#define ecal_qos_cimpl_h_included - -/** - * @brief eCAL QOS history kind mode. -**/ -enum eQOSPolicy_HistoryKindC -{ - keep_last_history_qos, /*!< Keep only a number of samples, default value. */ - keep_all_history_qos, /*!< Keep all samples until the ResourceLimitsQosPolicy are exhausted. */ -}; - -/** - * @brief eCAL QOS reliability mode. -**/ -enum eQOSPolicy_ReliabilityC -{ - best_effort_reliability_qos, /*!< Best Effort reliability (default for Subscribers). */ - reliable_reliability_qos, /*!< Reliable reliability (default for Publishers). */ -}; - -/** - * @brief eCAL data writer QOS settings. -**/ -struct SWriterQOSC -{ - enum eQOSPolicy_HistoryKindC history_kind; /*!< qos history kind mode */ - int history_kind_depth; /*!< qos history kind mode depth */ - enum eQOSPolicy_ReliabilityC reliability; /*!< qos reliability mode */ -}; - -/** - * @brief eCAL data reader QOS settings. -**/ -struct SReaderQOSC -{ - enum eQOSPolicy_HistoryKindC history_kind; /*!< qos history kind mode */ - int history_kind_depth; /*!< qos history kind mode depth */ - enum eQOSPolicy_ReliabilityC reliability; /*!< qos reliability mode */ -}; - -#endif /*ecal_qos_cimpl_h_included*/ diff --git a/ecal/core/include/ecal/ecal_clang.h b/ecal/core/include/ecal/ecal_clang.h deleted file mode 100644 index 9594f74..0000000 --- a/ecal/core/include/ecal/ecal_clang.h +++ /dev/null @@ -1,685 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_clang.h - * @brief eCAL C language interface (to wrap ecal into other languages easily) -**/ - -#ifndef ECAL_CLANG_H_INCLUDED -#define ECAL_CLANG_H_INCLUDED - -#include - -/*************************************************************************/ -/* common */ -/*************************************************************************/ -/** - * @brief Get eCAL version string. - * - * @return Full eCAL version string. -**/ -ECAL_API const char* ecal_getversion(); - -/** - * @brief Get eCAL version date. - * - * @return Full eCAL version date string. -**/ -ECAL_API const char* ecal_getdate(); - -/** - * @brief Initialize eCAL API. - * - * @param argc_ Number of command line arguments. - * @param argv_ Array of command line arguments. - * @param unit_name_ Defines the name of the eCAL unit. - * - * @return Zero if succeeded, 1 if already initialized, -1 if failed. -**/ -ECAL_API int ecal_initialize(int argc_, char **argv_, const char* unit_name_); - -/** - * @brief Finalize eCAL API. - * - * @return Zero if succeeded, 1 if already initialized, -1 if failed. -**/ -ECAL_API int ecal_finalize(); - -/** - * @brief Set process state info. - * - * @param severity_ Severity. - * @param level_ Severity level. - * @param info_ Info message. - * -**/ -ECAL_API void ecal_set_process_state(const int severity_, const int level_, const char* info_); - -/** - * @brief Return the eCAL process state. - * - * @return True if eCAL is in proper state. -**/ -ECAL_API bool ecal_ok(); - -/** - * @brief Free an eCAL memory block allocated by functions like - * mon_get_monitoring, mon_get_logging, - * sub_receive, that let eCAL allocate - * the memory internally. -**/ -/** - * @code - * // let eCAL allocate memory for the subscriber buffer and return the pointer to 'rcv_buf' - * const char* rcv_buf = nullptr; - * int rcv_buf_len = 0; - * long long rcv_time = 0; - * int timeout = 0; - * int ret = sub_receive(subscriber_handle, &rcv_buf, &rcv_buf_len, &rcv_time, timeout); - * if(rcv_buf_len > 0) - * { - * ... - * // PROCESS THE BUFFER CONTENT HERE - * ... - * // finally free the allocated memory - * ecal_free_mem((void*)rcv_buf); - * } - * @endcode -**/ -ECAL_API void ecal_free_mem(void* mem_); - -/** - * @brief Sleep current thread. - * - * @param time_ms_ Time to sleep in ms. -**/ -ECAL_API void ecal_sleep_ms(const long time_ms_); - -/** - * @brief Send shutdown event to specified local user process using it's unit name. - * - * @param unit_name_ Process unit name. -**/ -ECAL_API void ecal_shutdown_process_uname(const char* unit_name_); - -/** - * @brief Send shutdown event to specified local user process using it's process id. - * - * @param process_id_ Process id. -**/ -ECAL_API void ecal_shutdown_process_id(const int process_id_); - -/** - * @brief Send shutdown event to all local user processes. -**/ -ECAL_API void ecal_shutdown_processes(); - -/** - * @brief Send shutdown event to all local core components. -**/ -ECAL_API void ecal_shutdown_core(); - -/** - * @brief Enable eCAL message loop back, - * that means subscriber will receive messages from - * publishers of the same process (default == false). - * - * @param state_ Switch on message loop back.. -**/ -ECAL_API void ecal_enable_loopback(const 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 [out] topic_type_len_ Length of allocated buffer, - * eCAL is allocating the buffer for you, use ecal_free_mem to free the buffer finally. - * - * @return True if succeeded. -**/ -ECAL_API bool ecal_get_type_name(const char* topic_name_, const char** topic_type_, int* topic_type_len_); - -/** - * @brief Gets type description of the specified topic. - * - * @param topic_name_ Topic name. - * @param [out] topic_desc_ Pointer to store the type description information. - * @param [out] topic_desc_len_ Length of allocated buffer, - * eCAL is allocating the buffer for you, use ecal_free_mem to free the buffer finally. - * - * @return True if succeeded. -**/ -ECAL_API bool ecal_get_description(const char* topic_name_, const char** topic_desc_, int* topic_desc_len_); - -/*************************************************************************/ -/* logging */ -/*************************************************************************/ -/** - * @brief Sets the log level. - * - * @param level_ The level. -**/ -ECAL_API void log_setlevel(const int level_); - -/** - * @brief Set the current measured core time in s (for user implemented measuring). - * - * @param time_ The core time. -**/ -ECAL_API void log_setcoretime(const double time_); - -/** - * @brief Log a message (with current log level). - * - * @param message_ The log message string. -**/ -ECAL_API void log_message(const char* message_); - -/*************************************************************************/ -/* publisher */ -/*************************************************************************/ -/** - * @brief Create a publisher. - * - * @param topic_name_ Unique topic name. - * @param topic_type_ Topic type name. - * - * @return Handle of the created publisher or NULL if failed. -**/ -ECAL_API ECAL_HANDLE pub_create(const char* topic_name_, const char* topic_type_); - -/** - * @brief Destroy a publisher. - * - * @param handle_ Publisher handle. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_destroy(ECAL_HANDLE handle_); - -/** - * @brief Setup topic type name. - * - * @param handle_ Publisher handle. - * @param topic_type_name_ Topic type name. - * @param topic_type_name_length_ Topic type name length. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_set_type_name(ECAL_HANDLE handle_, const char* topic_type_name_, const int topic_type_name_length_); - -/** - * @brief Setup topic type description. - * - * @param handle_ Publisher handle. - * @param topic_desc_ Topic type description. - * @param topic_desc_length_ Topic type description length. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_set_description(ECAL_HANDLE handle_, const char* topic_desc_, const int topic_desc_length_); - -/** - * @brief Set publisher quality of service attributes. - * - * @param handle_ Publisher handle. - * @param qos_ Quality of service policies. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_set_qos(ECAL_HANDLE handle_, struct SWriterQOSC qos_); - -/** - * @brief Get publisher quality of service attributes. - * - * @param handle_ Publisher handle. - * @param qos_ Quality of service policies. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_get_qos(ECAL_HANDLE handle_, struct SWriterQOSC* qos_); - -/** - * @brief Set publisher send mode for specific transport layer. - * - * @param handle_ Publisher handle. - * @param layer_ Transport layer. - * @param mode_ Send mode. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_set_layer_mode(ECAL_HANDLE handle_, const int layer_, const int mode_); - -/** - * @brief Set publisher maximum transmit bandwidth for the udp layer. - * - * @param handle_ Publisher handle. - * @param bandwidth_ Maximum bandwidth in bytes/s (-1 == unlimited). - * - * @return True if succeeded. -**/ -ECAL_API bool pub_set_max_bandwidth_udp(ECAL_HANDLE handle_, long bandwidth_); - -/** - * @brief Send a message to all subscribers. - * - * @param handle_ Publisher handle. - * @param payload_ Buffer that contains content to send. - * @param length_ Send buffer length. - * @param time_ Send time (-1 = use eCAL system time in us, default = -1). - * - * @return Number of bytes sent. -**/ -ECAL_API int pub_send(ECAL_HANDLE handle_, const char* payload_, const int length_, const long long time_); - -/** - * @brief Send a message to all subscribers synchronized with acknowledge timeout. - * - * This synchronized mode is currently implemented for local interprocess communication (shm-ecal layer) only. - * - * @param handle_ Publisher handle. - * @param payload_ Buffer that contains content to send. - * @param length_ Length of buffer. - * @param time_ Send time (-1 = use eCAL system time in us). - * @param acknowledge_timeout_ms_ Maximum time to wait for all subscribers acknowledge feedback in ms (content received and processed). - * - * @return Number of bytes sent. -**/ -ECAL_API int pub_send_sync(ECAL_HANDLE handle_, const char* payload_, const int length_, const long long time_, const long long acknowledge_timeout_ms_); - -/** - * @brief Add callback function for publisher events. - * - * @param handle_ Publisher handle. - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_add_event_callback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_, const PubEventCallbackCT callback_, void* par_); - -/** - * @brief Remove callback function for publisher events. - * - * @param handle_ Publisher handle. - * @param type_ The event type to remove. - * - * @return True if succeeded. -**/ -ECAL_API bool pub_rem_event_callback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_); - -/*************************************************************************/ -/* subscriber */ -/*************************************************************************/ -/** -* @brief Create a subscriber. - * - * @param topic_name_ Unique topic name. - * @param topic_type_ Topic type name. - * - * @return Handle of the created subscriber or NULL if failed. -**/ -ECAL_API ECAL_HANDLE sub_create(const char* topic_name_, const char* topic_type_); - -/** - * @brief Destroy a subscriber. - * - * @param handle_ Subscriber handle. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_destroy(ECAL_HANDLE handle_); - -/** - * @brief Set subscriber quality of service attributes. - * - * @param handle_ Subscriber handle. - * @param qos_ Quality of service policies. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_set_qos(ECAL_HANDLE handle_, struct SReaderQOSC qos_); - -/** - * @brief Get subscriber quality of service attributes. - * - * @param handle_ Subscriber handle. - * @param qos_ Quality of service policies. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_get_qos(ECAL_HANDLE handle_, struct SReaderQOSC* qos_); - -/** - * @brief Receive a message from the publisher. - * - * @param handle_ Subscriber handle. - * @param [out] rcv_buf_ Buffer to store the received message content. - * @param [out] rcv_buf_len_ Length of allocated buffer, - * eCAL is allocating the buffer for you, use ecal_free_mem to free the buffer finally. - * @param [out] rcv_time_ Time from publisher in us. - * @param timeout_ Maximum time before receive operation returns (in milliseconds, -1 means infinite). - * - * @return Length of received buffer. -**/ -ECAL_API int sub_receive(ECAL_HANDLE handle_, const char** rcv_buf_, int* rcv_buf_len_, long long* rcv_time_, const int timeout_); - -/** - * @brief Receive a message from the publisher (able to process zero length buffer). - * - * @param handle_ Subscriber handle. - * @param [out] rcv_buf_ Buffer to store the received message content. - * @param [out] rcv_buf_len_ Length of allocated buffer, - * eCAL is allocating the buffer for you, use ecal_free_mem to free the buffer finally. - * @param [out] rcv_time_ Time from publisher in us. - * @param timeout_ Maximum time before receive operation returns (in milliseconds, -1 means infinite). - * - * @return True if succeeded. -**/ -ECAL_API bool sub_receive_buffer(ECAL_HANDLE handle_, const char** rcv_buf_, int* rcv_buf_len_, long long* rcv_time_, const int timeout_); - -/** - * @brief Add callback function for incoming receives. - * - * @param handle_ Subscriber handle. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_add_receive_callback(ECAL_HANDLE handle_, const ReceiveCallbackCT callback_, void* par_); - -/** - * @brief Remove callback function for incoming receives. - * - * @param handle_ Subscriber handle. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_rem_receive_callback(ECAL_HANDLE handle_); - -/** - * @brief Add callback function for subscriber events. - * - * @param handle_ Subscriber handle. - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_add_event_callback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_, const SubEventCallbackCT callback_, void* par_); - -/** - * @brief Remove callback function for subscriber events. - * - * @param handle_ Subscriber handle. - * @param type_ The event type to remove. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_rem_event_callback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_); - -/** - * @brief Set the timeout parameter for triggering - * the timeout callback. - * - * @param handle_ Subscriber handle. - * @param timeout_ The timeout in milliseconds. - * - * @return True if succeeded. -**/ -ECAL_API bool sub_set_timeout(ECAL_HANDLE handle_, int timeout_); - -/*************************************************************************/ -/* dyn_json_subscriber */ -/*************************************************************************/ -/** - * @brief Create a subscriber. - * - * @param topic_name_ Unique topic name. - * - * @return Handle to created subscriber or NULL if failed. -**/ -ECAL_API ECAL_HANDLE dyn_json_sub_create(const char* topic_name_); - -/** - * @brief Destroy a subscriber. - * - * @param handle_ Subscriber handle. - * - * @return True if succeeded. -**/ -ECAL_API bool dyn_json_sub_destroy(ECAL_HANDLE handle_); - -/** - * @brief Add callback function for incoming receives. - * - * @param handle_ Subscriber handle. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return True if succeeded. -**/ -ECAL_API bool dyn_json_sub_add_receive_callback(ECAL_HANDLE handle_, const ReceiveCallbackCT callback_, void* par_); - -/** - * @brief Remove callback function for incoming receives. - * - * @param handle_ Subscriber handle. - * - * @return True if succeeded. -**/ -ECAL_API bool dyn_json_sub_rem_receive_callback(ECAL_HANDLE handle_); - -/* TODO: not implemented and not used for now */ -//ECAL_API bool dyn_json_sub_add_event_callback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_, const EventCallbackCT callback_, void* par_); -//ECAL_API bool dyn_json_sub_rem_event_callback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_); -//ECAL_API bool dyn_json_sub_set_timeout(ECAL_HANDLE handle_, int timeout_); - -/*************************************************************************/ -/* service */ -/*************************************************************************/ -/** - * @brief Create a server. - * - * @param service_name_ Service name. - * - * @return Handle to created server or NULL if failed. -**/ -ECAL_API ECAL_HANDLE server_create(const char* service_name_); - -/** - * @brief Destroy a server. - * - * @param handle_ Server handle. - * - * @return True if succeeded. -**/ -ECAL_API bool server_destroy(ECAL_HANDLE handle_); - -/** - * @brief Add server method callback. - * - * @param handle_ Server handle. - * @param method_name_ Service method name. - * @param req_type_ Method request type (default = ""). - * @param resp_type_ Method response type (default = ""). - * @param callback_ Callback function for server request. - * @param par_ User defined context that will be forwarded to the request function. - * - * @return True if succeeded. -**/ -ECAL_API bool server_add_method_callback(ECAL_HANDLE handle_, const char* method_name_, const char* req_type_, const char* resp_type_, const MethodCallbackCT callback_, void* par_); - -/** - * @brief Remove server method callback. - * - * @param handle_ Server handle. - * @param method_name_ Service method name. - * - * @return True if succeeded. -**/ -ECAL_API bool server_rem_method_callback(ECAL_HANDLE handle_, const char* method_name_); - -/** - * @brief Create a client. - * - * @param service_name_ Service name. - * - * @return Handle to created client or NULL if failed. -**/ -ECAL_API ECAL_HANDLE client_create(const char* service_name_); - -/** - * @brief Destroy a client. - * - * @param handle_ Client handle. - * - * @return True if succeeded. -**/ -ECAL_API bool client_destroy(ECAL_HANDLE handle_); - -/** - * @brief Change the host name filter for that client instance - * - * @param handle_ Client handle. - * @param host_name_ Host name filter (empty or "*" == all hosts) - * - * @return True if succeeded. -**/ -ECAL_API bool client_set_hostname(ECAL_HANDLE handle_, const char* host_name_); - -/** - * @brief Call method of this service (none blocking variant with callback). - * - * @param handle_ Client handle. - * @param method_name_ Method name. - * @param request_ Request message buffer. - * @param request_len_ Request message length. - * @param timeout_ Maximum time before operation returns (in milliseconds, -1 means infinite). - * - * @return True if succeeded. -**/ -ECAL_API bool client_call_method(ECAL_HANDLE handle_, const char* method_name_, const char* request_, const int request_len_, const int timeout_); - -/** - * @brief Call method of this service (asynchronously with callback). - * - * @param handle_ Client handle. - * @param method_name_ Method name. - * @param request_ Request message buffer. - * @param request_len_ Request message length. - * @param timeout_ Maximum time before operation returns (in milliseconds, -1 means infinite). - * - * @return True if succeeded. -**/ -ECAL_API bool client_call_method_async(ECAL_HANDLE handle_, const char* method_name_, const char* request_, const int request_len_, const int timeout_); - -/* TODO: not implemented and not used for now */ -//ECAL_API client_add_response_callback -//ECAL_API client_rem_response_callback - - -/*************************************************************************/ -/* monitoring */ -/*************************************************************************/ -/** - * @brief Initialize eCAL monitoring API. - * - * @return Zero if succeeded, 1 if already initialized, -1 if failed. -**/ -ECAL_API int mon_initialize(); - -/** - * @brief Finalize eCAL monitoring API. - * - * @return Zero if succeeded, 1 if already initialized, -1 if failed. -**/ -ECAL_API int mon_finalize(); - -/** - * @brief Set topics filter blacklist regular expression. - * - * @param filter_ Topic filter as regular expression. - * - * @return Zero if succeeded. -**/ -ECAL_API int mon_set_excl_filter(const char* filter_); - -/** - * @brief Set topics filter whitelist regular expression. - * - * @param filter_ Topic filter as regular expression. - * - * @return Zero if succeeded. -**/ -ECAL_API int mon_set_incl_filter(const char* filter_); - -/** - * @brief Switch topics filter using regular expression on/off. - * - * @param state_ Filter on / off state. - * - * @return Zero if succeeded. -**/ -ECAL_API int mon_set_filter_state(const bool state_); - -/** - * @brief Get monitoring protobuf string. - * - * @param [out] mon_buf_ Pointer to store the monitoring information. - * @param [out] mon_buf_len_ Length of allocated buffer, - * eCAL is allocating the buffer for you, use ecal_free_mem to free the buffer finally. - * - * @return Monitoring buffer length or zero if failed. -**/ -/** - * @code - * // let eCAL allocate memory for the monitoring buffer and return the pointer to 'buf' - * const char* mon_buf_ = NULL; - * int mon_buf_len_ = 0; - * mon_get_monitoring(subscriber_handle, &mon_buf_, &mon_buf_len_); - * if(mon_buf_len_ > 0) - * { - * ... - * // PROCESS THE BUFFER CONTENT HERE - * ... - * // finally free the allocated memory - * ecal_free_mem(((void*)rcv_buf);); - * } - * @endcode -**/ -ECAL_API int mon_get_monitoring(const char** mon_buf_, int* mon_buf_len_); - -/** - * @brief Get logging string. - * - * @param [out] log_buf_ Pointer to store the monitoring information. - * @param [out] log_buf_len_ Length of allocated buffer, - * eCAL is allocating the buffer for you, use ecal_free_mem to free the buffer finally. - * - * @return Logging buffer length or zero if failed. -**/ -ECAL_API int mon_get_logging(const char** log_buf_, int* log_buf_len_); - -#endif /* ECAL_CLANG_H_INCLUDED */ diff --git a/ecal/core/include/ecal/ecal_publisher.h b/ecal/core/include/ecal/ecal_publisher.h deleted file mode 100644 index 5b3f4a9..0000000 --- a/ecal/core/include/ecal/ecal_publisher.h +++ /dev/null @@ -1,456 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_publisher.h - * @brief eCAL publisher interface -**/ - -#pragma once - -#include -#include -#include -#include - -#include -#include - -namespace eCAL -{ - class CDataWriter; - - /** - * @brief eCAL publisher class. - * - * The CPublisher class is used to send topics to matching eCAL subscribers. The topic is created automatically by the constructor - * or by the Create member function. - *
- *
- * For sending the topic payload the publisher class provides an overloaded Send method. The first one is sending the payload as - * a std::string. The second needs a preallocated buffer described by a buffer address and a buffer length. The publisher is not - * taking the ownership for the allocated memory buffer. - *
- *
- * An optional time stamp can be attached to the topic payload. - * - **/ - /** - * @code - * // create publisher, topic name "A" - * eCAL::CPublisher pub("A"); - * - * // send string - * std::string send_s = "Hello World "; - * - * // send content - * size_t snd_len = pub.Send(send_s); - * @endcode - **/ - class ECAL_API CPublisher - { - public: - /** - * @brief Constructor. - **/ - CPublisher(); - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional). - * @param topic_desc_ Type description (optional). - **/ - CPublisher(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = ""); - - /** - * @brief Destructor. - **/ - virtual ~CPublisher(); - - /** - * @brief CPublishers are non-copyable - **/ - CPublisher(const CPublisher&) = delete; - - /** - * @brief CPublishers are non-copyable - **/ - CPublisher& operator=(const CPublisher&) = delete; - - /** - * @brief CPublishers are move-enabled - **/ - CPublisher(CPublisher&& rhs) noexcept; - - /** - * @brief CPublishers are move-enabled - **/ - CPublisher& operator=(CPublisher&& rhs) noexcept; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional). - * @param topic_desc_ Type description (optional). - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = ""); - - /** - * @brief Destroys this object. - * - * @return True if it succeeds, false if it fails. - **/ - bool Destroy(); - - /** - * @brief Setup topic type name. - * - * @param topic_type_name_ Topic type name. - * - * @return True if it succeeds, false if it fails. - **/ - bool SetTypeName(const std::string& topic_type_name_); - - /** - * @brief Setup topic description. - * - * @param topic_desc_ Description string. - * - * @return True if it succeeds, false if it fails. - **/ - bool SetDescription(const std::string& topic_desc_); - - /** - * @brief Sets publisher attribute. - * - * @param attr_name_ Attribute name. - * @param attr_value_ Attribute value. - * - * @return True if it succeeds, false if it fails. - * @experimental - **/ - bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); - - /** - * @brief Removes publisher attribute. - * - * @param attr_name_ Attribute name. - * - * @return True if it succeeds, false if it fails. - * @experimental - **/ - bool ClearAttribute(const std::string& attr_name_); - - /** - * @brief Share topic type. - * - * @param state_ Set type share mode (true == share type). - * - * @return True if it succeeds, false if it fails. - **/ - bool ShareType(bool state_ = true); - - /** - * @brief Share topic description. - * - * @param state_ Set description share mode (true == share description). - * - * @return True if it succeeds, false if it fails. - **/ - bool ShareDescription(bool state_ = true); - - /** - * @brief Set publisher quality of service attributes. - * - * @param qos_ Quality of service policies. - * - * @return True if it succeeds, false if it fails. - **/ - bool SetQOS(const QOS::SWriterQOS& qos_); - - /** - * @brief Get current publisher quality of service attributes. - * - * @return Quality of service attributes. - **/ - QOS::SWriterQOS GetQOS(); - - /** - * @brief Set publisher send mode for specific transport layer. - * - * @param layer_ Transport layer. - * @param mode_ Send mode. - * - * @return True if it succeeds, false if it fails. - **/ - bool SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_); - - /** - * @brief Set publisher maximum transmit bandwidth for the udp layer. - * - * @param bandwidth_ Maximum bandwidth in bytes/s (-1 == unlimited). - * - * @return True if it succeeds, false if it fails. - **/ - bool SetMaxBandwidthUDP(long bandwidth_); - - /** - * @brief Set publisher maximum number of used shared memory buffers. - * - * @param buffering_ Maximum number of used buffers (needs to be greater than 1, default = 1). - * - * @return True if it succeeds, false if it fails. - **/ - bool ShmSetBufferCount(long buffering_); - - /** - * @brief Enable zero copy shared memory transport mode. - * - * By default, the built-in shared memory layer is configured to make one memory copy - * on the receiver side. That means the payload is copied by the internal eCAL memory pool manager - * out of the memory file and the file is closed immediately after this. - * The intention of this implementation is to free the file as fast as possible after reading - * its content to allow other subscribing processes to access the content with minimal latency. - * The different reading subscribers are fully decoupled and can access their memory copy - * independently. - * - * If ShmEnableZeroCopy is switched on no memory will be copied at all. The user message callback is - * called right after opening the memory file. A direct pointer to the memory payload is forwarded - * and can be processed with no latency. The memory file will be closed after the user callback function - * returned. The advantage of this configuration is a much higher performance for large payloads (> 1024 kB). - * The disadvantage of this configuration is that in the time when the callback is executed the memory file - * is blocked for other subscribers and for writing publishers too. Maybe this can be eliminated - * by a better memory file read/write access implementation (lock free read) in future releases. - * - * Today, for specific scenarios (1:1 pub/sub connections with large payloads for example) this feature - * can increase the performance remarkable. But please keep in mind to return from the message callback function - * as fast as possible to not delay subsequent read/write access operations. - * - * @param state_ Set type zero copy mode for shared memory transport layer (true == zero copy enabled). - * - * @return True if it succeeds, false if it fails. - **/ - bool ShmEnableZeroCopy(bool state_); - - /** - * @brief Force connected subscribers to send acknowledge event after processing the message and - * block publisher send call on this event with a timeout. - * - * Most applications perform very well with the default behavior. If subscribers are too slow - * to process incoming messages then the overall software architecture needs to be checked, software components - * need to be optimized or parallelized. - * - * There may still be cases where it could make sense to synchronize the transfer of the payload from a publisher - * to a subscriber by using an additional handshake event. This event is signaled by a subscriber back to the - * sending publisher to confirm the complete payload transmission and the processed subscriber callback. - * - * The publisher will wait up to the specified timeout for the acknowledge signals of all connected subscribers - * before sending new content. Finally that means the publishers CPublisher::Send API function call is now blocked - * and will not return until all subscriber have read and processed their content or the timeout has been reached. - * - * @param acknowledge_timeout_ms_ timeout to wait for acknowledge signal from connected subscriber in ms (0 == no handshake). - * - * @return True if it succeeds, false if it fails. - **/ - bool ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_); - - /** - * @brief Force connected subscribers to send acknowledge event after processing the message and - * block publisher send call on this event with a timeout. - * - * See ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_) - * - * @param acknowledge_timeout_ timeout to wait for acknowledge signal from connected subscriber (0 == no handshake). - * - * @return True if it succeeds, false if it fails. - **/ - template - bool ShmSetAcknowledgeTimeout(std::chrono::duration acknowledge_timeout_) - { - auto acknowledge_timeout_ms = std::chrono::duration_cast(acknowledge_timeout_).count(); - return ShmSetAcknowledgeTimeout(static_cast(acknowledge_timeout_ms)); - } - - /** - * @brief Set the specific topic id. - * - * @param id_ The topic id for subscriber side filtering (0 == no id). - * - * @return True if it succeeds, false if it fails. - **/ - bool SetID(long long id_); - - /** - * @brief Send a message to all subscribers. - * - * @param buf_ Pointer to content buffer. - * @param len_ Length of buffer. - * @param time_ Send time (-1 = use eCAL system time in us, default = -1). - * - * @return Number of bytes sent. - **/ - size_t Send(const void* const buf_, size_t len_, long long time_ = -1) const; - - /** - * @brief Send a message to all subscribers synchronized with acknowledge timeout (see also ShmSetAcknowledgeTimeout). - * - * This synchronized mode is currently implemented for local interprocess communication (shm-ecal layer) only. - * - * @param buf_ Pointer to content buffer. - * @param len_ Length of buffer. - * @param time_ Send time (-1 = use eCAL system time in us). - * @param acknowledge_timeout_ms_ Maximum time to wait for all subscribers acknowledge feedback in ms (buffer received and processed). - * - * @return Number of bytes sent. - **/ - size_t Send(const void* const buf_, size_t len_, long long time_, long long acknowledge_timeout_ms_) const; - - /** - * @brief Send a message to all subscribers synchronized with acknowledge timeout (see also ShmSetAcknowledgeTimeout). - * - * This synchronized mode is currently implemented for local interprocess communication (shm-ecal layer) only. - * - * @param buf_ Pointer to content buffer. - * @param len_ Length of buffer. - * @param time_ Send time (-1 = use eCAL system time in us). - * @param acknowledge_timeout_ms_ Maximum time to wait for all subscribers acknowledge feedback in ms (buffer received and processed). - * - * @return Number of bytes sent. - **/ - [[deprecated]] - size_t SendSynchronized(const void* const buf_, size_t len_, long long time_, long long acknowledge_timeout_ms_) const - { - return Send(buf_, len_, time_, acknowledge_timeout_ms_); - } - - /** - * @brief Send a message to all subscribers. - * - * @param s_ String that contains content to send. - * @param time_ Send time (-1 = use eCAL system time in us, default = -1). - * - * @return Number of bytes sent. - **/ - size_t Send(const std::string& s_, long long time_ = -1) const - { - return(Send(s_.data(), s_.size(), time_)); - } - - /** - * @brief Send a message to all subscribers synchronized. - * - * @param s_ String that contains content to send. - * @param time_ Send time (-1 = use eCAL system time in us). - * @param acknowledge_timeout_ms_ Maximum time to wait for all subscribers acknowledge feedback in ms (buffer received and processed). - * - * @return Number of bytes sent. - **/ - size_t Send(const std::string& s_, long long time_, long long acknowledge_timeout_ms_) const - { - return(Send(s_.data(), s_.size(), time_, acknowledge_timeout_ms_)); - } - - /** - * @brief Add callback function for publisher events. - * - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_); - - /** - * @brief Remove callback function for publisher events. - * - * @param type_ The event type to remove. - * - * @return True if succeeded, false if not. - **/ - bool RemEventCallback(eCAL_Publisher_Event type_); - - /** - * @brief Query if the publisher is created. - * - * @return True if created, false if not. - **/ - bool IsCreated() const {return(m_created);} - - /** - * @brief Query if the publisher is subscribed. - * - * @return true if subscribed, false if not. - **/ - bool IsSubscribed() const; - - /** - * @brief Query the number of subscribers. - * - * @return Number of subscribers. - **/ - size_t GetSubscriberCount() const; - - /** - * @brief Gets name of the connected topic. - * - * @return The topic name. - **/ - std::string GetTopicName() const; - - /** - * @brief Gets type of the connected topic. - * - * @return The type name. - **/ - std::string GetTypeName() const; - - /** - * @brief Gets description of the connected topic. - * - * @return The description. - **/ - std::string GetDescription() const; - - /** - * @brief Dump the whole class state into a string. - * - * @param indent_ Indentation used for dump. - * - * @return The dump string. - **/ - std::string Dump(const std::string& indent_ = "") const; - - protected: - void InitializeQOS(); - void InitializeTLayer(); - - // class members - CDataWriter* m_datawriter; - struct ECAL_API QOS::SWriterQOS m_qos; - struct ECAL_API TLayer::STLayer m_tlayer; - long long m_id; - bool m_created; - bool m_initialized; - }; -}; diff --git a/ecal/core/include/ecal/ecal_qos.h b/ecal/core/include/ecal/ecal_qos.h deleted file mode 100644 index 520d421..0000000 --- a/ecal/core/include/ecal/ecal_qos.h +++ /dev/null @@ -1,83 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_qos.h - * @brief eCAL quality of service settings -**/ - -#pragma once - -#include "ecal_os.h" - -namespace eCAL -{ - namespace QOS - { - /** - * @brief eCAL QOS history kind mode. - **/ - enum eQOSPolicy_HistoryKind - { - keep_last_history_qos, //!< Keep only a number of samples, default value. - keep_all_history_qos, //!< Keep all samples until the ResourceLimitsQosPolicy are exhausted. - }; - - /** - * @brief eCAL QOS reliability mode. - **/ - enum eQOSPolicy_Reliability - { - best_effort_reliability_qos, //!< Best Effort reliability (default for Subscribers). - reliable_reliability_qos, //!< Reliable reliability (default for Publishers). - }; - - /** - * @brief eCAL data writer QOS settings. - **/ - struct ECAL_API SWriterQOS - { - SWriterQOS() - { - history_kind = keep_last_history_qos; - history_kind_depth = 8; - reliability = reliable_reliability_qos; - } - eQOSPolicy_HistoryKind history_kind; //!< qos history kind mode - int history_kind_depth; //!< qos history kind mode depth - eQOSPolicy_Reliability reliability; //!< qos reliability mode - }; - - /** - * @brief eCAL data reader QOS settings. - **/ - struct ECAL_API SReaderQOS - { - SReaderQOS() - { - history_kind = keep_last_history_qos; - history_kind_depth = 8; - reliability = best_effort_reliability_qos; - } - eQOSPolicy_HistoryKind history_kind; //!< qos history kind mode - int history_kind_depth; //!< qos history kind mode depth - eQOSPolicy_Reliability reliability; //!< qos reliability mode - }; - } -} diff --git a/ecal/core/include/ecal/ecal_service.h b/ecal/core/include/ecal/ecal_service.h deleted file mode 100644 index e909310..0000000 --- a/ecal/core/include/ecal/ecal_service.h +++ /dev/null @@ -1,35 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_service.h - * @brief eCAL service interface -**/ - -#pragma once - -#ifdef _MSC_VER -#pragma message("WARNING: This header file is deprecated. It will be removed in future eCAL versions. Please include and / or instead") -#endif /*_MSC_VER*/ -#ifdef __GNUC__ -#pragma message "WARNING: This header file is deprecated. It will be removed in future eCAL versions. Please include and / or instead" -#endif /* __GNUC__ */ - -#include -#include \ No newline at end of file diff --git a/ecal/core/include/ecal/ecal_timed_cb.h b/ecal/core/include/ecal/ecal_timed_cb.h deleted file mode 100644 index 5581b6d..0000000 --- a/ecal/core/include/ecal/ecal_timed_cb.h +++ /dev/null @@ -1,134 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_timed_cb.h - * @brief eCAL timer callback class (for internal use only !!) -**/ - -#pragma once - -#include -#include -#include -#include -#include - -#include "ecal_process.h" - -namespace eCAL -{ - class CTimedCB; - - /** - * @brief Timer callback function type. - **/ - typedef std::function TimerCallbackT; - - /** - * @brief eCAL timer callback class. - * - * The CTimedCB class is used to realize simple time triggered callbacks. - **/ - class CTimedCB - { - public: - /** - * @brief Constructor. - **/ - CTimedCB() : m_stop(false), m_running(false) {} - - /** - * @brief Constructor. - * - * @param timeout_ Timer callback loop time in ms. - * @param callback_ The callback function. - * @param delay_ Timer callback delay for first call in ms. - **/ - CTimedCB(int timeout_, TimerCallbackT callback_, int delay_ = 0) : m_stop(false), m_running(false) { Start(timeout_, callback_, delay_); } - - /** - * @brief Destructor. - **/ - virtual ~CTimedCB() { Stop(); } - - /** - * @brief Start the timer. - * - * @param timeout_ Timer callback loop time in ms. - * @param callback_ The callback function. - * @param delay_ Timer callback delay for first call in ms. - * - * @return True if timer could be started. - **/ - bool Start(const int timeout_, TimerCallbackT callback_, const int delay_ = 0) - { - assert(m_running == false); - if (m_running) return(false); - if (timeout_ < 0) return(false); - m_stop = false; - m_thread = std::thread(&CTimedCB::Thread, this, callback_, timeout_, delay_); - m_running = true; - return(true); - } - - /** - * @brief Stop the timer. - * - * @return True if timer could be stopped. - **/ - bool Stop() - { - if (!m_running) return(false); - m_stop = true; - m_thread.join(); - m_running = false; - return(true); - } - - private: - // this object must not be copied. - CTimedCB(const CTimedCB&); - CTimedCB& operator=(const CTimedCB&); - - void Thread(TimerCallbackT callback_, int timeout_, int delay_) - { - assert(callback_ != nullptr); - if (callback_ == nullptr) return; - if (delay_ > 0) eCAL::Process::SleepFor(std::chrono::milliseconds(delay_)); - while (!m_stop) - { - auto start = std::chrono::steady_clock::now(); - (callback_)(); - if (timeout_ > 0) - { - auto now = std::chrono::steady_clock::now(); - auto elapsed_time = std::chrono::duration_cast(now - start).count(); - auto sleep_duration = timeout_ - elapsed_time; - eCAL::Process::SleepFor(std::chrono::milliseconds(sleep_duration)); - } - } - m_stop = false; - } - - std::atomic m_stop; - std::atomic m_running; - std::thread m_thread; - }; -} diff --git a/ecal/core/include/ecal/ecalc_types.h b/ecal/core/include/ecal/ecalc_types.h deleted file mode 100644 index e1d6847..0000000 --- a/ecal/core/include/ecal/ecalc_types.h +++ /dev/null @@ -1,21 +0,0 @@ - -/** - * @file ecalc_types.h - * @brief File including shared types for eCAL C API -**/ - -#ifndef ecalc_types_h_included -#define ecalc_types_h_included - -/** - * @brief Flag to indicate eCAL to allocate/deallocate memory. -**/ -#define ECAL_ALLOCATE_4ME 0 - -/** - * @brief Common handle for eCAL C API function calls. -**/ -typedef void* ECAL_HANDLE; - -#endif //ecalc_types_h_included - diff --git a/ecal/core/include/ecal/msg/capnproto/dynamic.h b/ecal/core/include/ecal/msg/capnproto/dynamic.h deleted file mode 100644 index 3f0b28a..0000000 --- a/ecal/core/include/ecal/msg/capnproto/dynamic.h +++ /dev/null @@ -1,202 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file dynamic.h - * @brief eCAL dynamic subscriber interface for Cap'n Proto message definitions -**/ - -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif /*_MSC_VER*/ -#include -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif /*_MSC_VER*/ - -#include -#include - -namespace eCAL -{ - - namespace capnproto - { - class CDynamicSubscriber - { - public: - /** - * @brief Constructor. - **/ - CDynamicSubscriber() - : subscriber() - , builder() - , initialized(false) - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CDynamicSubscriber(const std::string& topic_name_) - : subscriber(topic_name_, GetTypeName(), GetDescription()) - , builder() - , initialized(false) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CDynamicSubscriber(const CDynamicSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CDynamicSubscriber& operator=(const CDynamicSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CDynamicSubscriber(CDynamicSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CDynamicSubscriber& operator=(CDynamicSubscriber&&) = default; - - /** - * @brief eCAL protobuf message receive callback function - * - * @param topic_name_ Topic name of the data source (publisher). - * @param msg_ Protobuf message content. - * @param time_ Message time stamp. - **/ - typedef std::function CapnpDynamicMsgCallbackT; - - /** - * @brief Add callback function for incoming receives. - * - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddReceiveCallback(CapnpDynamicMsgCallbackT callback_) - { - msg_callback = callback_; - return subscriber.AddReceiveCallback(std::bind(&CDynamicSubscriber::OnReceive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - } - - bool RemReceiveCallback() - { - auto ret = subscriber.RemReceiveCallback(); - msg_callback = nullptr; - return ret; - } - - void OnReceive(const char* topic_name_, const capnp::MallocMessageBuilder& msg_, long long time_, long long clock_, long long id_) - { - if (!initialized) - { - std::string topic_desc = eCAL::Util::GetDescription(topic_name_); - if (!topic_desc.empty()) - { - // We initialize the builder from the string - schema = eCAL::capnproto::SchemaFromDescriptor(topic_desc, loader); - initialized = true; - } - else - { - return; - } - } - - auto root = const_cast(msg_).getRoot(schema.asStruct()); - msg_callback(topic_name_, root.asReader(), time_, clock_, id_); - } - - /** - * @brief get a Pointer to a temporary message that can be passed to receive - **/ - typename capnp::DynamicStruct::Reader getReader() - { - return root_builder.asReader(); - } - - /** - * @brief Manually receive the next sample - **/ - bool Receive(long long* time_ = nullptr, int rcv_timeout_ = 0) - { - return subscriber.Receive(builder, time_, rcv_timeout_); - } - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(subscriber.Create(topic_name_, "", "")); - } - - /** - * @brief Get type name of the capnp message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return (""); - } - - private: - - CBuilderSubscriber subscriber; - capnp::MallocMessageBuilder builder; - capnp::DynamicStruct::Builder root_builder; - CapnpDynamicMsgCallbackT msg_callback; - - capnp::schema::Node::Reader reader; - capnp::SchemaLoader loader; - capnp::Schema schema; - - bool initialized; - - /** - * @brief Get file descriptor string of the capnp message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return(""); - } - }; - - } - -} \ No newline at end of file diff --git a/ecal/core/include/ecal/msg/capnproto/helper.h b/ecal/core/include/ecal/msg/capnproto/helper.h deleted file mode 100644 index 390e143..0000000 --- a/ecal/core/include/ecal/msg/capnproto/helper.h +++ /dev/null @@ -1,120 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif /*_MSC_VER*/ -#include -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif /*_MSC_VER*/ - -#include -#include - -namespace eCAL -{ - namespace capnproto - { - const std::string message_prefix{ "capnp:" }; - - inline void appendMessageToString(capnp::MallocMessageBuilder& builder_, std::string& output_) - { - auto message_words = capnp::messageToFlatArray(builder_); - auto message_bytes = message_words.asBytes(); - std::copy(message_bytes.begin(), message_bytes.end(), std::back_inserter(output_)); - } - - // From an abritrary type T, create a schema that contains all dependencies and - // return it as a std::string - // The first word (aka 8 bytes) is the id of T in Big Endian (Network) byte order. - // Following is a serialized list of T and all its dependencies schemas. - template - std::string SchemaAsString() - { - // Create a loader and load T and all its dependencies, - capnp::SchemaLoader loader; - loader.loadCompiledTypeAndDependencies(); - - // Retrieve id information and save as big endian / network order - // then save it to the descriptor string as first 8 bytes - uint64_t id = capnp::typeId(); - - std::string descriptor_string; - - capnp::MallocMessageBuilder id_builder; - auto type_builder = id_builder.getRoot(); - type_builder.setUint64(id); - appendMessageToString(id_builder, descriptor_string); - - // Serialize schema & all dependencies into string - auto loaded = loader.getAllLoaded(); - for (auto schema : loaded) - { - capnp::MallocMessageBuilder builder; - builder.setRoot(schema.getProto()); - appendMessageToString(builder, descriptor_string); - } - return descriptor_string; - } - - template - inline std::string TypeAsString() - { - auto schema = capnp::Schema::from(); - auto name = schema.getShortDisplayName(); - return(message_prefix + std::string(name.cStr())); - } - - // Creates a Schema from a given descriptor, needs to be passed a Schema loader. - inline capnp::Schema SchemaFromDescriptor(const std::string descriptor_string, capnp::SchemaLoader& loader) - { - kj::ArrayPtr schema_words = kj::arrayPtr(reinterpret_cast(descriptor_string.data()), descriptor_string.size() / sizeof(capnp::word)); - - // First, read out the id of the root message - capnp::MallocMessageBuilder value_builder; - schema_words = capnp::initMessageBuilderFromFlatArrayCopy(schema_words, value_builder); - auto value_reader = value_builder.getRoot(); - uint64_t id = value_reader.getUint64(); - - // Read all messages into a builder, and load that into the loader - while (schema_words.begin() != schema_words.end()) - { - capnp::MallocMessageBuilder schema_builder; - schema_words = capnp::initMessageBuilderFromFlatArrayCopy(schema_words, schema_builder); - // Get builder root as a Node - capnp::schema::Node::Reader reader = schema_builder.getRoot(); - loader.load(reader); - } - // how to get the "root" schema here? So the AddressBookSchema? - return loader.get(id); - } - - - } -} \ No newline at end of file diff --git a/ecal/core/include/ecal/msg/capnproto/publisher.h b/ecal/core/include/ecal/msg/capnproto/publisher.h deleted file mode 100644 index 3781a65..0000000 --- a/ecal/core/include/ecal/msg/capnproto/publisher.h +++ /dev/null @@ -1,263 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file publisher.h - * @brief eCAL publisher interface for Cap'n Proto message definitions -**/ - -#pragma once - -#include -#include - -// capnp includes -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif /*_MSC_VER*/ -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif /*_MSC_VER*/ - - -namespace eCAL -{ - namespace capnproto - { - /** - * @brief eCAL capnp publisher class. - * - * Publisher template class for capnp messages. For details see documentation of CPublisher class. - * - **/ - class CBuilderPublisher : public CMsgPublisher - { - public: - /** - * @brief Constructor. - **/ - CBuilderPublisher() : CMsgPublisher() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CBuilderPublisher(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_) : CMsgPublisher(topic_name_, topic_type_, topic_desc_) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CBuilderPublisher(const CBuilderPublisher&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CBuilderPublisher& operator=(const CBuilderPublisher&) = delete; - - /** - * @brief Move Constructor - **/ - CBuilderPublisher(CBuilderPublisher&&) = default; - - /** - * @brief Move assignment - **/ - CBuilderPublisher& operator=(CBuilderPublisher&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_) - { - return(CMsgPublisher::Create(topic_name_, topic_type_, topic_desc_)); - } - - /** - * @brief Get type name of the capnp message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return("capnp:"); - } - - private: - /** - * @brief Get file descriptor string of the capnp message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - //auto schema = capnp::Schema::from(); - return(""); - } - - /** - * @brief Get size for serialized message object. - * - * @param msg_ The message object. - * - * @return Message size. - **/ - size_t GetSize(const capnp::MallocMessageBuilder& msg_) const - { - return(capnp::computeSerializedSizeInWords(const_cast(msg_)) * sizeof(capnp::word)); - } - - /** - * @brief Serialize the message object into a preallocated char buffer. - * - * @param msg_ The message object. - * @param [out] buffer_ Target buffer. - * @param size_ Target buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Serialize(const capnp::MallocMessageBuilder& msg_, char* buffer_, size_t size_) const - { - kj::Array words = capnp::messageToFlatArray(const_cast(msg_)); - kj::ArrayPtr bytes = words.asBytes(); - if (size_ < bytes.size()) return(false); - memcpy(buffer_, bytes.begin(), bytes.size()); - return(true); - } - }; - /** @example addressbook_snd.cpp - * This is an example how to use eCAL::CPublisher to send capnp data with eCAL. To receive the data, see @ref addressbook_rec.cpp . - */ - - /** - * @brief eCAL capnp publisher class. - * - * Publisher template class for capnp messages. For details see documentation of CPublisher class. - * - **/ - template - class CPublisher - { - public: - /** - * @brief Constructor. - **/ - CPublisher() - : publisher() - , builder() - , root_builder(builder.initRoot()) - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CPublisher(const std::string& topic_name_) - : publisher(topic_name_, GetTypeName(), GetDescription()) - , builder() - , root_builder(builder.initRoot()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher(const CPublisher&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher& operator=(const CPublisher&) = delete; - - /** - * @brief Move Constructor - **/ - CPublisher(CPublisher&&) = default; - - /** - * @brief Move assignment - **/ - CPublisher& operator=(CPublisher&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(publisher.Create(topic_name_, GetTypeName(), GetDescription())); - } - - typename message_type::Builder GetBuilder() - { - return root_builder; - } - - void Send() - { - publisher.Send(builder); - } - - /** - * @brief Get type name of the capnp message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return eCAL::capnproto::TypeAsString(); - } - - private: - - CBuilderPublisher publisher; - capnp::MallocMessageBuilder builder; - typename message_type::Builder root_builder; - - /** - * @brief Get file descriptor string of the capnp message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return eCAL::capnproto::SchemaAsString(); - } - - }; - /** @example addressbook_snd.cpp - * This is an example how to use eCAL::CPublisher to send capnp data with eCAL. To receive the data, see @ref addressbook_rec.cpp . - */ - } -} diff --git a/ecal/core/include/ecal/msg/capnproto/subscriber.h b/ecal/core/include/ecal/msg/capnproto/subscriber.h deleted file mode 100644 index bf1164d..0000000 --- a/ecal/core/include/ecal/msg/capnproto/subscriber.h +++ /dev/null @@ -1,290 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file subscriber.h - * @brief eCAL subscriber interface for Cap'n Proto message definitions -**/ - -#pragma once - -#include -#include - -// capnp includes -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif /*_MSC_VER*/ -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif /*_MSC_VER*/ - - -namespace eCAL -{ - namespace capnproto - { - /** - * @brief eCAL capnp subscriber class. - * - * Subscriber template class for capnp messages. For details see documentation of CSubscriber class. - * - **/ - class CBuilderSubscriber : public CMsgSubscriber - { - public: - - /** - * @brief Constructor. - **/ - CBuilderSubscriber() : CMsgSubscriber() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CBuilderSubscriber(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_) : CMsgSubscriber(topic_name_, topic_type_, topic_desc_) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CBuilderSubscriber(const CBuilderSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CBuilderSubscriber& operator=(const CBuilderSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CBuilderSubscriber(CBuilderSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CBuilderSubscriber& operator=(CBuilderSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_) - { - return(CMsgSubscriber::Create(topic_name_, topic_type_, topic_desc_)); - } - - /** - * @brief Get type name of the capnp message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return("capnp:"); - } - - private: - /** - * @brief Get file descriptor string of the capnp message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return(""); - } - - /** - * @brief Deserialize the message object from a message buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Source buffer. - * @param size_ Source buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(capnp::MallocMessageBuilder& msg_, const void* buffer_, size_t size_) const - { - kj::ArrayPtr words = kj::arrayPtr(reinterpret_cast(buffer_), size_ / sizeof(capnp::word)); - kj::ArrayPtr rest = initMessageBuilderFromFlatArrayCopy(words, msg_); - return(rest.size() == 0); - } - }; - /** @example addressbook_rec.cpp - * This is an example how to use eCAL::CCapnpSubscriber to receive capnp data with eCAL. To send the data, see @ref addressbook_snd.cpp . - */ - - /** - * @brief eCAL capnp subscriber class. - * - * Subscriber template class for capnp messages. For details see documentation of CSubscriber class. - * - **/ - template - class CSubscriber - { - public: - /** - * @brief Constructor. - **/ - CSubscriber() - : subscriber() - , builder() - , root_builder(builder.getRoot()) - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CSubscriber(const std::string& topic_name_) - : subscriber(topic_name_, GetTypeName(), GetDescription()) - , builder() - , root_builder(builder.getRoot()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief eCAL protobuf message receive callback function - * - * @param topic_name_ Topic name of the data source (publisher). - * @param msg_ Protobuf message content. - * @param time_ Message time stamp. - **/ - typedef std::function MsgCallbackT; - - /** - * @brief Add callback function for incoming receives. - * - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddReceiveCallback(MsgCallbackT callback_) - { - msg_callback = callback_; - return subscriber.AddReceiveCallback(std::bind(&CSubscriber::OnReceive, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - } - - bool RemReceiveCallback() - { - auto ret{subscriber.RemReceiveCallback()}; - msg_callback = nullptr; - return ret; - } - - void OnReceive(const char* topic_name_, const capnp::MallocMessageBuilder& msg_, long long time_, long long clock_, long long id_) - { - auto root = const_cast(msg_).getRoot(); - msg_callback(topic_name_, root, time_, clock_, id_); - } - - /** - * @brief get a Pointer to a temporary message that can be passed to receive - **/ - typename message_type::Reader getReader() - { - return root_builder.asReader(); - } - - /** - * @brief Manually receive the next sample - **/ - bool Receive(long long* time_ = nullptr, int rcv_timeout_ = 0) - { - bool success = subscriber.Receive(builder, time_, rcv_timeout_); - // Update the Reader - root_builder = typename message_type::Builder(builder.getRoot()); - return success; - } - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(subscriber.Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name of the capnp message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return eCAL::capnproto::TypeAsString(); - } - - private: - - CBuilderSubscriber subscriber; - capnp::MallocMessageBuilder builder; - typename message_type::Builder root_builder; - MsgCallbackT msg_callback; - - /** - * @brief Get file descriptor string of the capnp message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return eCAL::capnproto::SchemaAsString(); - } - }; - - } -} diff --git a/ecal/core/include/ecal/msg/flatbuffers/publisher.h b/ecal/core/include/ecal/msg/flatbuffers/publisher.h deleted file mode 100644 index 87b268c..0000000 --- a/ecal/core/include/ecal/msg/flatbuffers/publisher.h +++ /dev/null @@ -1,144 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file publisher.h - * @brief eCAL publisher interface for google::flatbuffers message definitions -**/ - -#pragma once - -#include - -namespace eCAL -{ - namespace flatbuffers - { - /** - * @brief eCAL google::flatbuffers publisher class. - * - * Publisher template class for goggle::flatbuffers messages. For details see documentation of CPublisher class. - * - **/ - template - class CPublisher : public CMsgPublisher - { - public: - /** - * @brief Constructor. - **/ - CPublisher() : CMsgPublisher() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, GetTypeName(), GetDescription()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher(const CPublisher&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher& operator=(const CPublisher&) = delete; - - /** - * @brief Move Constructor - **/ - CPublisher(CPublisher&&) = default; - - /** - * @brief Move assignment - **/ - CPublisher& operator=(CPublisher&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgPublisher::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name of the flatbuffers message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return("flatb:"); - } - - private: - /** - * @brief Get file descriptor string of the flatbuffers message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return(""); - } - - /** - * @brief Get size for serialized message object. - * - * @param msg_ The message object. - * - * @return Message size. - **/ - size_t GetSize(const T& msg_) const - { - return((size_t)msg_.GetSize()); - } - - /** - * @brief Serialize the message object into a preallocated char buffer. - * - * @param msg_ The message object. - * @param [out] buffer_ Target buffer. - * @param size_ Target buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Serialize(const T& msg_, char* buffer_, size_t size_) const - { - if (size_ < msg_.GetSize()) return(false); - memcpy(buffer_, msg_.GetBufferPointer(), msg_.GetSize()); - return(true); - } - }; - /** @example monster_snd.cpp - * This is an example how to use eCAL::CPublisher to send goggle::flatbuffers data with eCAL. To receive the data, see @ref monster_rec.cpp . - */ - } -} diff --git a/ecal/core/include/ecal/msg/flatbuffers/subscriber.h b/ecal/core/include/ecal/msg/flatbuffers/subscriber.h deleted file mode 100644 index 39fd499..0000000 --- a/ecal/core/include/ecal/msg/flatbuffers/subscriber.h +++ /dev/null @@ -1,131 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file subscriber.h - * @brief eCAL subscriber interface for google::flatbuffers message definitions -**/ - -#pragma once - -#include - -namespace eCAL -{ - namespace flatbuffers - { - /** - * @brief eCAL google::flatbuffers subscriber class. - * - * Subscriber template class for google::flatbuffers messages. For details see documentation of CSubscriber class. - * - **/ - template - class CSubscriber : public CMsgSubscriber - { - public: - /** - * @brief Constructor. - **/ - CSubscriber() : CMsgSubscriber() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, GetTypeName(), GetDescription()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgSubscriber::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name of the flatbuffers message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return("flatb:"); - } - - private: - /** - * @brief Get file descriptor string of the flatbuffers message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return(""); - } - - /** - * @brief Deserialize the message object from a message buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Source buffer. - * @param size_ Source buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(T& msg_, const void* buffer_, size_t size_) const - { - msg_.PushFlatBuffer(static_cast(buffer_), static_cast(size_)); - return(true); - } - }; - /** @example monster_rec.cpp - * This is an example how to use eCAL::CSubscriber to receive goggle::flatbuffers data with eCAL. To send the data, see @ref monster_snd.cpp . - */ - } -} diff --git a/ecal/core/include/ecal/msg/messagepack/publisher.h b/ecal/core/include/ecal/msg/messagepack/publisher.h deleted file mode 100644 index c7c6e34..0000000 --- a/ecal/core/include/ecal/msg/messagepack/publisher.h +++ /dev/null @@ -1,154 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file publisher.h - * @brief eCAL publisher interface for messagepack message definitions -**/ - -#pragma once - -#include - -#include -#include - -namespace eCAL -{ - namespace messagepack - { - /** - * @brief eCAL msgpack publisher class. - * - * Publisher template class for msgpack messages. For details see documentation of CPublisher class. - * - **/ - template - class CPublisher : public CMsgPublisher - { - public: - /** - * @brief Constructor. - **/ - CPublisher() : CMsgPublisher() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, GetTypeName(), GetDescription()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher(const CPublisher&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher& operator=(const CPublisher&) = delete; - - /** - * @brief Move Constructor - **/ - CPublisher(CPublisher&&) = default; - - /** - * @brief Move assignment - **/ - CPublisher& operator=(CPublisher&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgPublisher::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name of the msgpack message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return("mpack:"); - } - - private: - /** - * @brief Get file descriptor string of the msgpack message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return(""); - } - - /** - * @brief Get size for serialized message object. - * - * @param msg_ The message object. - * - * @return Message size. - **/ - size_t GetSize(const T& msg_) const - { - msgpack::sbuffer buf; - msgpack::pack(&buf, msg_); - return(buf.size()); - } - - /** - * @brief Serialize the message object into a preallocated char buffer. - * - * @param msg_ The message object. - * @param [out] buffer_ Target buffer. - * @param size_ Target buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Serialize(const T& msg_, char* buffer_, size_t size_) const - { - std::stringstream buffer; - msgpack::pack(buffer, msg_); - buffer.seekg(0, std::ios::end); - size_t size = static_cast(buffer.tellg()); - if (size > size_) return(false); - buffer.seekg(0, std::ios::beg); - buffer.read(buffer_, size_); - return(true); - } - }; - /** @example address_snd.cpp - * This is an example how to use eCAL::CPublisher to send msgpack data with eCAL. To receive the data, see @ref address_rec.cpp . - */ - } -} diff --git a/ecal/core/include/ecal/msg/messagepack/subscriber.h b/ecal/core/include/ecal/msg/messagepack/subscriber.h deleted file mode 100644 index 7e452fd..0000000 --- a/ecal/core/include/ecal/msg/messagepack/subscriber.h +++ /dev/null @@ -1,138 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file subscriber.h - * @brief eCAL subscriber interface for messagepack message definitions -**/ - -#pragma once - -#include - -#include -#include - -namespace eCAL -{ - namespace messagepack - { - /** - * @brief eCAL msgpack subscriber class. - * - * Subscriber template class for msgpack messages. For details see documentation of CSubscriber class. - * - **/ - template - class CSubscriber : public CMsgSubscriber - { - public: - /** - * @brief Constructor. - **/ - CSubscriber() : CMsgSubscriber() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, GetTypeName(), GetDescription()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber(const CSubscriber&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CSubscriber& operator=(const CSubscriber&) = delete; - - /** - * @brief Move Constructor - **/ - CSubscriber(CSubscriber&&) = default; - - /** - * @brief Move assignment - **/ - CSubscriber& operator=(CSubscriber&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgSubscriber::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name of the msgpack message. - * - * @return Type name. - **/ - std::string GetTypeName() const - { - return("mpack:"); - } - - private: - /** - * @brief Get file descriptor string of the msgpack message. - * - * @return Description string. - **/ - std::string GetDescription() const - { - return(""); - } - - /** - * @brief Deserialize the message object from a message buffer. - * - * @param [out] msg_ The message object. - * @param buffer_ Source buffer. - * @param size_ Source buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Deserialize(T& msg_, const void* buffer_, size_t size_) const - { - msgpack::unpacked ubuffer; - msgpack::unpack(ubuffer, static_cast(buffer_), size_); - msgpack::object deserialized = ubuffer.get(); - deserialized.convert(msg_); - return(true); - } - - }; - /** @example address_rec.cpp - * This is an example how to use eCAL::CSubscriber to receive msgpack data with eCAL. To send the data, see @ref address_snd.cpp . - */ - } -} diff --git a/ecal/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h b/ecal/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h deleted file mode 100644 index 7319140..0000000 --- a/ecal/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h +++ /dev/null @@ -1,111 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file ecal_proto_dyn_json_sub.h - * @brief dynamic protobuf message to json decoder -**/ - -#pragma once - -#include - -namespace eCAL -{ - namespace protobuf - { - - class CDynamicJSONSubscriberImpl; - - /** - * @brief eCAL dynamic protobuf to json subscriber. - **/ - class ECAL_API CDynamicJSONSubscriber - { - public: - /** - * @brief Constructor. - **/ - CDynamicJSONSubscriber(); - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - CDynamicJSONSubscriber(const std::string& topic_name_); - - /** - * @brief Destructor. - **/ - ~CDynamicJSONSubscriber(); - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return true if it succeeds, false if it fails. - **/ - void Create(const std::string& topic_name_); - - /** - * @brief Destroys this object. - * - * @return true if it succeeds, false if it fails. - **/ - void Destroy(); - - /** - * @brief Query if this object is created. - * - * @return true if created, false if not. - **/ - bool IsCreated() { return(created); } - - /** - * @brief Add callback function for incoming receives. - * - * @param callback_ The callback function to add. - * - * @return True if succeeded, false if not. - **/ - bool AddReceiveCallback(ReceiveCallbackT callback_); - - /** - * @brief Remove callback function for incoming receives. - * - * @return True if succeeded, false if not. - **/ - bool RemReceiveCallback(); - - protected: - bool created; - CDynamicJSONSubscriberImpl* proto_dyn_sub_impl; - - private: - // this object must not be copied. - CDynamicJSONSubscriber(const CDynamicJSONSubscriber&); - CDynamicJSONSubscriber& operator=(const CDynamicJSONSubscriber&); - }; - /** @example proto_dyn_json.cpp - * This is an example how to use CDynamicJSONSubscriber to receive dynamic google::protobuf data as a JSON string with eCAL. - */ - } -} diff --git a/ecal/core/include/ecal/msg/protobuf/publisher.h b/ecal/core/include/ecal/msg/protobuf/publisher.h deleted file mode 100644 index 8dc59e2..0000000 --- a/ecal/core/include/ecal/msg/protobuf/publisher.h +++ /dev/null @@ -1,169 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @file publisher.h - * @brief eCAL publisher interface for google::protobuf message definitions -**/ - -#pragma once - -#include -#include - -// protobuf includes -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// stl includes -#include -#include -#include - -namespace eCAL -{ - namespace protobuf - { - /** - * @brief eCAL google::protobuf publisher class. - * - * Publisher template class for google::protobuf messages. For details see documentation of CPublisher class. - * - **/ - template - class CPublisher : public CMsgPublisher - { - public: - /** - * @brief Constructor. - **/ - CPublisher() : CMsgPublisher() - { - } - - /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - **/ - - // call the function via its class becase it's a virtual function that is called in constructor/destructor,- - // where the vtable is not created yet or it's destructed. - CPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, CPublisher::GetTypeName(), CPublisher::GetDescription()) - { - } - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher(const CPublisher&) = delete; - - /** - * @brief Copy Constructor is not available. - **/ - CPublisher& operator=(const CPublisher&) = delete; - - /** - * @brief Move Constructor - **/ - CPublisher(CPublisher&&) = default; - - /** - * @brief Move assignment - **/ - CPublisher& operator=(CPublisher&&) = default; - - /** - * @brief Creates this object. - * - * @param topic_name_ Unique topic name. - * - * @return True if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_) - { - return(CMsgPublisher::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name of the protobuf message. - * - * @return Type name. - **/ - std::string GetTypeName() const override - { - static T msg; - return("proto:" + msg.GetTypeName()); - } - - private: - /** - * @brief Get file descriptor string of the protobuf message. - * - * @return Description string. - **/ - std::string GetDescription() const override - { - static T msg; - return(protobuf::GetProtoMessageDescription(msg)); - } - - /** - * @brief Get size for serialized message object. - * - * @param msg_ The message object. - * - * @return Message size. - **/ - size_t GetSize(const T& msg_) const override - { - size_t size(0); -#if GOOGLE_PROTOBUF_VERSION >= 3001000 - size = (size_t)msg_.ByteSizeLong(); -#else - size = (size_t)msg_.ByteSize(); -#endif - return(size); - } - - /** - * @brief Serialize the message object into a preallocated char buffer. - * - * @param msg_ The message object. - * @param [out] buffer_ Target buffer. - * @param size_ Target buffer size. - * - * @return True if it succeeds, false if it fails. - **/ - bool Serialize(const T& msg_, char* buffer_, size_t size_) const override - { - return(msg_.SerializeToArray((void*)buffer_, (int)size_)); - } - - }; - /** @example person_snd.cpp - * This is an example how to use eCAL::CPublisher to send google::protobuf data with eCAL. To receive the data, see @ref person_rec.cpp . - */ - } -} diff --git a/ecal/core/src/_code.cppcheck b/ecal/core/src/_code.cppcheck deleted file mode 100644 index 493dff9..0000000 --- a/ecal/core/src/_code.cppcheck +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/ecal/core/src/ecal_clang.cpp b/ecal/core/src/ecal_clang.cpp deleted file mode 100644 index fc6f828..0000000 --- a/ecal/core/src/ecal_clang.cpp +++ /dev/null @@ -1,935 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL C language interface -**/ - -#include -#include -#include - -#include -#include -#include -#include -#include - -static char* str_malloc(const std::string& buf_s_) -{ - void* cbuf = malloc(buf_s_.size()); - if(cbuf != nullptr) - { - memcpy(cbuf, buf_s_.data(), buf_s_.size()); - } - return(static_cast(cbuf)); -} - -/****************************************/ -/* ecal_getversion */ -/****************************************/ -ECAL_API const char* ecal_getversion() -{ - return(ECAL_VERSION); -} - -/****************************************/ -/* ecal_getdate */ -/****************************************/ -ECAL_API const char* ecal_getdate() -{ - return(ECAL_DATE); -} - -/****************************************/ -/* ecal_initialize */ -/****************************************/ -ECAL_API int ecal_initialize(int argc_, char **argv_, const char* unit_name_) -{ - return(eCAL::Initialize(argc_, argv_, unit_name_)); -} - -/****************************************/ -/* ecal_finalize */ -/****************************************/ -ECAL_API int ecal_finalize() -{ - //* @return Zero if succeeded, 1 if still initialized, -1 if failed. - return(eCAL::Finalize()); -} - -/****************************************/ -/* ecal_set_process_state */ -/****************************************/ -ECAL_API void ecal_set_process_state(const int severity_, const int level_, const char* info_) -{ - return(eCAL::Process::SetState(eCAL_Process_eSeverity(severity_), eCAL_Process_eSeverity_Level(level_), info_)); -} - -/****************************************/ -/* ecal_ok */ -/****************************************/ -ECAL_API bool ecal_ok() -{ - return(eCAL::Ok()); -} - -/****************************************/ -/* ecal_free_mem */ -/****************************************/ -ECAL_API void ecal_free_mem(void* mem_) -{ - free(mem_); -} - -/****************************************/ -/* ecal_sleep_ms */ -/****************************************/ -ECAL_API void ecal_sleep_ms(const long time_ms_) -{ - eCAL::Process::SleepMS(time_ms_); -} - -/****************************************/ -/* ecal_shutdown_process_uname */ -/****************************************/ -ECAL_API void ecal_shutdown_process_uname(const char* unit_name_) -{ - eCAL::Util::ShutdownProcess(unit_name_); -} - -/****************************************/ -/* ecal_shutdown_process_id */ -/****************************************/ -ECAL_API void ecal_shutdown_process_id(const int process_id_) -{ - eCAL::Util::ShutdownProcess(process_id_); -} - -/****************************************/ -/* ecal_shutdown_processes */ -/****************************************/ -ECAL_API void ecal_shutdown_processes() -{ - eCAL::Util::ShutdownProcesses(); -} - -/****************************************/ -/* ecal_shutdown_core */ -/****************************************/ -ECAL_API void ecal_shutdown_core() -{ - eCAL::Util::ShutdownCore(); -} - -/****************************************/ -/* ecal_enable_loopback */ -/****************************************/ -ECAL_API void ecal_enable_loopback(const int state_) -{ - eCAL::Util::EnableLoopback(state_ != 0); -} - -/****************************************/ -/* get_type_name */ -/****************************************/ -ECAL_API bool ecal_get_type_name(const char* topic_name_, const char** topic_type_, int* topic_type_len_) -{ - std::string topic_type_s; - bool ret = eCAL::Util::GetTopicTypeName(topic_name_, topic_type_s); - if(ret) - { - // this has to be freed by caller (ecal_free_mem) - char* cbuf = str_malloc(topic_type_s); - if(cbuf == nullptr) return(false); - - if (topic_type_) { - *topic_type_ = cbuf; - if(topic_type_len_) *topic_type_len_ = static_cast(topic_type_s.size()); - } - else { - // free allocated memory: - ecal_free_mem(cbuf); - if (topic_type_len_) *topic_type_len_ = 0; - ret = false; - } - } - return(ret); -} - -/****************************************/ -/* get_description */ -/****************************************/ -ECAL_API bool ecal_get_description(const char* topic_name_, const char** topic_desc_, int* topic_desc_len_) -{ - std::string topic_desc_s; - bool ret = eCAL::Util::GetTopicDescription(topic_name_, topic_desc_s); - if(ret) - { - // this has to be freed by caller (ecal_free_mem) - char* cbuf = str_malloc(topic_desc_s); - if(cbuf == nullptr) return(false); - - if (topic_desc_) { - *topic_desc_ = cbuf; - if (topic_desc_len_) *topic_desc_len_ = static_cast(topic_desc_s.size()); - } - else { - // free allocated memory: - ecal_free_mem(cbuf); - if (topic_desc_len_) *topic_desc_len_ = 0; - ret = false; - } - } - return(ret); -} - -/****************************************/ -/* log_setlevel */ -/****************************************/ -ECAL_API void log_setlevel(const int level_) -{ - eCAL::Logging::SetLogLevel(eCAL_Logging_eLogLevel(level_)); -} - -/****************************************/ -/* log_setcoretime */ -/****************************************/ -ECAL_API void log_setcoretime(const double time_) -{ - eCAL::Logging::SetCoreTime(time_); -} - -/****************************************/ -/* log_message */ -/****************************************/ -ECAL_API void log_message(const char* message_) -{ - eCAL::Logging::Log(message_); -} - - -/****************************************/ -/* pub_create */ -/****************************************/ -ECAL_API ECAL_HANDLE pub_create(const char* topic_name_, const char* topic_type_) -{ - eCAL::CPublisher* pub = new eCAL::CPublisher; - if (!pub->Create(topic_name_, topic_type_)) - { - delete pub; - return(nullptr); - } - return(pub); -} - -/****************************************/ -/* pub_destroy */ -/****************************************/ -ECAL_API bool pub_destroy(ECAL_HANDLE handle_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - if(pub) - { - delete pub; - pub = nullptr; - return(true); - } - else - { - return(false); - } -} - -/****************************************/ -/* pub_set_type_name */ -/****************************************/ -ECAL_API bool pub_set_type_name(ECAL_HANDLE handle_, const char* topic_type_name_, const int topic_type_name_length_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - if (pub) - { - return(pub->SetTypeName(std::string(topic_type_name_, static_cast(topic_type_name_length_)))); - } - return(false); -} - -/****************************************/ -/* pub_set_description */ -/****************************************/ -ECAL_API bool pub_set_description(ECAL_HANDLE handle_, const char* topic_desc_, const int topic_desc_length_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - if(pub) - { - return(pub->SetDescription(std::string(topic_desc_, static_cast(topic_desc_length_)))); - } - return(false); -} - -/****************************************/ -/* pub_set_qos */ -/****************************************/ -ECAL_API bool pub_set_qos(ECAL_HANDLE handle_, struct SWriterQOSC qos_) //-V813 -{ - int ret = eCAL_Pub_SetQOS(handle_, qos_); - return(ret == 0); -} - -/****************************************/ -/* pub_get_qos */ -/****************************************/ -ECAL_API bool pub_get_qos(ECAL_HANDLE handle_, struct SWriterQOSC* qos_) -{ - int ret = eCAL_Pub_GetQOS(handle_, qos_); - return(ret == 0); -} - -/****************************************/ -/* pub_set_layer_mode */ -/****************************************/ -ECAL_API bool pub_set_layer_mode(ECAL_HANDLE handle_, const int layer_, const int mode_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - if(pub) - { - return(pub->SetLayerMode(eCAL::TLayer::eTransportLayer(layer_), eCAL::TLayer::eSendMode(mode_))); - } - return(false); -} - -/****************************************/ -/* pub_set_max_bandwidth_udp */ -/****************************************/ -ECAL_API bool pub_set_max_bandwidth_udp(ECAL_HANDLE handle_, long bandwidth_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - if (pub) - { - return(pub->SetMaxBandwidthUDP(bandwidth_)); - } - return(false); -} - -/****************************************/ -/* pub_send */ -/****************************************/ -ECAL_API int pub_send(ECAL_HANDLE handle_, const char* payload_, const int length_, const long long time_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - if(pub) - { - size_t ret = pub->Send(payload_, static_cast(length_), time_); - if(static_cast(ret) == length_) - { - return(length_); - } - } - return(0); -} - -/****************************************/ -/* pub_send_sync */ -/****************************************/ -ECAL_API int pub_send_sync(ECAL_HANDLE handle_, const char* payload_, const int length_, const long long time_, const long long acknowledge_timeout_ms_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - if (pub) - { - size_t ret = pub->Send(payload_, static_cast(length_), time_, acknowledge_timeout_ms_); - if (static_cast(ret) == length_) - { - return(length_); - } - } - return(0); -} - -/****************************************/ -/* pub_add_event_callback */ -/****************************************/ -static std::mutex g_pub_event_callback_mtx; -static void g_pub_event_callback(const char* topic_name_, const struct eCAL::SPubEventCallbackData* data_, const PubEventCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_pub_event_callback_mtx); - SPubEventCallbackDataC data; - data.type = data_->type; - data.time = data_->time; - data.clock = data_->clock; - data.tid = data_->tid.c_str(); - data.ttype = data_->ttype.c_str(); - data.tdesc = data_->tdesc.c_str(); - callback_(topic_name_, &data, par_); -} - -ECAL_API bool pub_add_event_callback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_, const PubEventCallbackCT callback_, void* par_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - - auto callback = std::bind(g_pub_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - return(pub->AddEventCallback(type_, callback)); -} - -/****************************************/ -/* pub_rem_event_callback */ -/****************************************/ -ECAL_API bool pub_rem_event_callback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_) -{ - eCAL::CPublisher* pub = static_cast(handle_); - - return(pub->RemEventCallback(type_)); -} - - -/****************************************/ -/* sub_create */ -/****************************************/ -ECAL_API ECAL_HANDLE sub_create(const char* topic_name_, const char* topic_type_) -{ - eCAL::CSubscriber* sub = new eCAL::CSubscriber; - if (!sub->Create(topic_name_, topic_type_)) - { - delete sub; - return(nullptr); - } - return(sub); -} - -/****************************************/ -/* sub_destroy */ -/****************************************/ -ECAL_API bool sub_destroy(ECAL_HANDLE handle_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - if(sub) - { - delete sub; - sub = nullptr; - return(true); - } - return(false); -} - -/****************************************/ -/* sub_set_qos */ -/****************************************/ -ECAL_API bool sub_set_qos(ECAL_HANDLE handle_, struct SReaderQOSC qos_) //-V813 -{ - int ret = eCAL_Sub_SetQOS(handle_, qos_); - return(ret == 0); -} - -/****************************************/ -/* sub_get_qos */ -/****************************************/ -ECAL_API bool sub_get_qos(ECAL_HANDLE handle_, struct SReaderQOSC* qos_) -{ - int ret = eCAL_Sub_GetQOS(handle_, qos_); - return(ret == 0); -} - -/****************************************/ -/* sub_receive */ -/****************************************/ -ECAL_API int sub_receive(ECAL_HANDLE handle_, const char** rcv_buf_, int* rcv_buf_len_, long long* rcv_time_, const int timeout_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - if(sub) - { - std::string rcv_buf; - long long rcv_time = 0; - sub->ReceiveBuffer(rcv_buf, &rcv_time, timeout_); - - if(!rcv_buf.empty()) - { - // this has to be freed by caller (ecal_free_mem) - char* cbuf = str_malloc(rcv_buf); - if(cbuf == nullptr) return(0); - - if (rcv_buf_) { - *rcv_buf_ = cbuf; - if (rcv_buf_len_) *rcv_buf_len_ = static_cast(rcv_buf.size()); - } - else { - // free allocated memory: - ecal_free_mem(cbuf); - if (rcv_buf_len_) *rcv_buf_len_ = 0; - // operation could't be completed successfully - return(0); - } - - if(rcv_time_) *rcv_time_ = rcv_time; - - return(static_cast(rcv_buf.size())); - } - } - return(0); -} - -/****************************************/ -/* sub_receive_buffer */ -/****************************************/ -ECAL_API bool sub_receive_buffer(ECAL_HANDLE handle_, const char** rcv_buf_, int* rcv_buf_len_, long long* rcv_time_, const int timeout_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - if (sub) - { - std::string rcv_buf; - long long rcv_time = 0; - - if (sub->ReceiveBuffer(rcv_buf, &rcv_time, timeout_)) - { - // this has to be freed by caller (ecal_free_mem) - char* cbuf = str_malloc(rcv_buf); - if (cbuf == nullptr) return(0); - - if (rcv_buf_) { - *rcv_buf_ = cbuf; - if (rcv_buf_len_) *rcv_buf_len_ = static_cast(rcv_buf.size()); - } - else { - // free allocated memory: - ecal_free_mem(cbuf); - if (rcv_buf_len_) *rcv_buf_len_ = 0; - // operation couldn't be completed successfullly. - return(false); - } - if (rcv_time_) *rcv_time_ = rcv_time; - - return(true); - } - } - return(false); -} - -/****************************************/ -/* sub_add_receive_callback */ -/****************************************/ -static std::mutex g_sub_receive_callback_mtx; -static void g_sub_receive_callback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_, const ReceiveCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_sub_receive_callback_mtx); - SReceiveCallbackDataC data; - data.buf = data_->buf; - data.size = data_->size; - data.id = data_->id; - data.time = data_->time; - data.clock = data_->clock; - callback_(topic_name_, &data, par_); -} - -ECAL_API bool sub_add_receive_callback(ECAL_HANDLE handle_, const ReceiveCallbackCT callback_, void* par_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - - auto callback = std::bind(g_sub_receive_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - return(sub->AddReceiveCallback(callback)); -} - -/****************************************/ -/* sub_rem_receive_callback */ -/****************************************/ -ECAL_API bool sub_rem_receive_callback(ECAL_HANDLE handle_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - - return(sub->RemReceiveCallback()); -} - -/****************************************/ -/* sub_add_event_callback */ -/****************************************/ -static std::mutex g_sub_event_callback_mtx; -static void g_sub_event_callback(const char* topic_name_, const struct eCAL::SSubEventCallbackData* data_, const SubEventCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_sub_event_callback_mtx); - SSubEventCallbackDataC data; - data.type = data_->type; - data.time = data_->time; - data.clock = data_->clock; - data.tid = data_->tid.c_str(); - data.ttype = data_->ttype.c_str(); - data.tdesc = data_->tdesc.c_str(); - callback_(topic_name_, &data, par_); -} - -ECAL_API bool sub_add_event_callback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_, const SubEventCallbackCT callback_, void* par_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - - auto callback = std::bind(g_sub_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - return(sub->AddEventCallback(type_, callback)); -} - -/****************************************/ -/* sub_rem_event_callback */ -/****************************************/ -ECAL_API bool sub_rem_event_callback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - - return(sub->RemEventCallback(type_)); -} - -/****************************************/ -/* sub_set_timeout */ -/****************************************/ -ECAL_API bool sub_set_timeout(ECAL_HANDLE handle_, int timeout_) -{ - eCAL::CSubscriber* sub = static_cast(handle_); - - return(sub->SetTimeout(timeout_)); -} - -/****************************************/ -/* dyn_json_sub_create */ -/****************************************/ -ECAL_API ECAL_HANDLE dyn_json_sub_create(const char* topic_name_) -{ - eCAL::protobuf::CDynamicJSONSubscriber* sub = new eCAL::protobuf::CDynamicJSONSubscriber(topic_name_); - if (!sub->IsCreated()) - { - delete sub; - return(nullptr); - } - return(sub); -} - -/****************************************/ -/* dyn_json_sub_destroy */ -/****************************************/ -ECAL_API bool dyn_json_sub_destroy(ECAL_HANDLE handle_) -{ - eCAL::protobuf::CDynamicJSONSubscriber* sub = static_cast(handle_); - if (sub) - { - delete sub; - sub = nullptr; - return(true); - } - return(false); -} - -/****************************************/ -/* dyn_json_sub_add_receive_callback */ -/****************************************/ -static std::mutex g_dyn_json_sub_receive_callback_mtx; -static void g_dyn_json_sub_receive_callback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_, const ReceiveCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_dyn_json_sub_receive_callback_mtx); - SReceiveCallbackDataC data; - data.buf = data_->buf; - data.size = data_->size; - data.id = data_->id; - data.time = data_->time; - data.clock = data_->clock; - callback_(topic_name_, &data, par_); -} - -ECAL_API bool dyn_json_sub_add_receive_callback(ECAL_HANDLE handle_, const ReceiveCallbackCT callback_, void* par_) -{ - eCAL::protobuf::CDynamicJSONSubscriber* sub = static_cast(handle_); - - auto callback = std::bind(g_dyn_json_sub_receive_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - return(sub->AddReceiveCallback(callback)); -} - -/****************************************/ -/* dyn_json_sub_rem_receive_callback */ -/****************************************/ -ECAL_API bool dyn_json_sub_rem_receive_callback(ECAL_HANDLE handle_) -{ - eCAL::protobuf::CDynamicJSONSubscriber* sub = static_cast(handle_); - - return(sub->RemReceiveCallback()); -} - - -/****************************************/ -/* server_create */ -/****************************************/ -ECAL_API ECAL_HANDLE server_create(const char* service_name_) -{ - eCAL::CServiceServer* server = new eCAL::CServiceServer; - if (!server->Create(service_name_)) - { - delete server; - return(nullptr); - } - return(server); -} - -/****************************************/ -/* server_destroy */ -/****************************************/ -ECAL_API bool server_destroy(ECAL_HANDLE handle_) -{ - eCAL::CServiceServer* server = static_cast(handle_); - if (server) - { - delete server; - server = nullptr; - return(true); - } - else - { - return(false); - } -} - -/****************************************/ -/* server_add_method_callback */ -/****************************************/ -static std::mutex g_server_add_method_callback_mtx; -static int g_server_method_callback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_, const MethodCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_server_add_method_callback_mtx); - void* response(nullptr); - int response_len(0); - int ret = callback_(method_.data(), req_type_.data(), resp_type_.data(), request_.data(), static_cast(request_.size()), &response, &response_len, par_); - response_ = std::string(static_cast(response), static_cast(response_len)); - return ret; -} - -ECAL_API bool server_add_method_callback(ECAL_HANDLE handle_, const char* method_name_, const char* req_type_, const char* resp_type_, const MethodCallbackCT callback_, void* par_) -{ - eCAL::CServiceServer* server = static_cast(handle_); - if (server) - { - auto callback = std::bind(g_server_method_callback, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, callback_, par_); - return(server->AddMethodCallback(method_name_, req_type_, resp_type_, callback)); - } - else - { - return(false); - } -} - -/****************************************/ -/* server_rem_method_callback */ -/****************************************/ -ECAL_API bool server_rem_method_callback(ECAL_HANDLE handle_, const char* method_name_) -{ - eCAL::CServiceServer* server = static_cast(handle_); - if (server) - { - return(server->RemMethodCallback(method_name_)); - } - else - { - return(false); - } -} - - -/****************************************/ -/* client_create */ -/****************************************/ -ECAL_API ECAL_HANDLE client_create(const char* service_name_) -{ - eCAL::CServiceClient* client = new eCAL::CServiceClient; - if (!client->Create(service_name_)) - { - delete client; - return(nullptr); - } - return(client); -} - -/****************************************/ -/* client_destroy */ -/****************************************/ -ECAL_API bool client_destroy(ECAL_HANDLE handle_) -{ - eCAL::CServiceClient* client = static_cast(handle_); - if (client) - { - delete client; - client = nullptr; - return(true); - } - else - { - return(false); - } -} - -/****************************************/ -/* client_set_hostname */ -/****************************************/ -ECAL_API bool client_set_hostname(ECAL_HANDLE handle_, const char* host_name_) -{ - eCAL::CServiceClient* client = static_cast(handle_); - if (client) - { - return(client->SetHostName(host_name_)); - } - else - { - return(false); - } -} - -/****************************************/ -/* client_call_method */ -/****************************************/ -ECAL_API bool client_call_method(ECAL_HANDLE handle_, const char* method_name_, const char* request_, const int request_len_, const int timeout_) -{ - eCAL::CServiceClient* client = static_cast(handle_); - if (client) - { - std::string request(request_, request_len_); - return(client->Call(method_name_, request, timeout_)); - } - else - { - return(false); - } -} - -/****************************************/ -/* client_call_method_async */ -/****************************************/ -ECAL_API bool client_call_method_async(ECAL_HANDLE handle_, const char* method_name_, const char* request_, const int request_len_, const int timeout_) -{ - eCAL::CServiceClient* client = static_cast(handle_); - if (client) - { - std::string request(request_, request_len_); - return(client->CallAsync(method_name_, request, timeout_)); - } - else - { - return(false); - } -} - -/****************************************/ -/* client_add_response_callback */ -/****************************************/ - -/****************************************/ -/* client_rem_response_callback */ -/****************************************/ - - -/****************************************/ -/* mon_initialize */ -/****************************************/ -ECAL_API int mon_initialize() -{ - return(eCAL::Initialize(0, nullptr, "", eCAL::Init::Monitoring)); -} - -/****************************************/ -/* mon_finalize */ -/****************************************/ -ECAL_API int mon_finalize() -{ - return(ecal_finalize()); -} - -/****************************************/ -/* mon_set_excl_filter */ -/****************************************/ -ECAL_API int mon_set_excl_filter(const char* filter_) -{ - return(eCAL::Monitoring::SetExclFilter(filter_)); -} - -/****************************************/ -/* mon_set_incl_filter */ -/****************************************/ -ECAL_API int mon_set_incl_filter(const char* filter_) -{ - return(eCAL::Monitoring::SetInclFilter(filter_)); -} - -/****************************************/ -/* mon_set_filter_state */ -/****************************************/ -ECAL_API int mon_set_filter_state(const bool state_) -{ - return(eCAL::Monitoring::SetFilterState(state_)); -} - -/****************************************/ -/* mon_get_monitoring */ -/****************************************/ -ECAL_API int mon_get_monitoring(const char** mon_buf_, int* mon_buf_len_) -{ - std::string mon_s; - int size = eCAL::Monitoring::GetMonitoring(mon_s); - if(size > 0) - { - // this has to be freed by caller (ecal_free_mem) - char* cbuf = str_malloc(mon_s); - if(cbuf == nullptr) return(0); - - if (mon_buf_) { - *mon_buf_ = cbuf; - if (mon_buf_len_) *mon_buf_len_ = static_cast(mon_s.size()); - } - else - { - // free allocated memory: - ecal_free_mem(cbuf); - if (mon_buf_len_) *mon_buf_len_ = 0; - // operation could't be completed successfully - return(0); - } - return(static_cast(mon_s.size())); - } - else - { - return(0); - } -} - -/****************************************/ -/* mon_get_logging */ -/****************************************/ -ECAL_API int mon_get_logging(const char** log_buf_, int* log_buf_len_) -{ - std::string log_s; - int size = eCAL::Monitoring::GetLogging(log_s); - if(size > 0) - { - // this has to be freed by caller (ecal_free_mem) - char* cbuf = str_malloc(log_s); - if(cbuf == nullptr) return(0); - - if (log_buf_) { - *log_buf_ = cbuf; - if (log_buf_len_) *log_buf_len_ = static_cast(log_s.size()); - } - else { - // free allocated memory: - ecal_free_mem(cbuf); - if (log_buf_len_) *log_buf_len_ = 0; - // operation couldn't be completed successfullly. - return(0); - } - return(static_cast(log_s.size())); - } - else - { - return(0); - } -} diff --git a/ecal/core/src/ecal_def_ini.h b/ecal/core/src/ecal_def_ini.h deleted file mode 100644 index 2000c3e..0000000 --- a/ecal/core/src/ecal_def_ini.h +++ /dev/null @@ -1,131 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL ini file keys -**/ - -#pragma once - -///////////////////////////////////// -// common -///////////////////////////////////// -#define CMN_SECTION_S "common" -#define CMN_REGISTRATION_TO_S "registration_timeout" -#define CMN_REGISTRATION_REFRESH_S "registration_refresh" - -///////////////////////////////////// -// network -///////////////////////////////////// -#define NET_SECTION_S "network" - -#define NET_ENABLED_S "network_enabled" - -#define NET_UDP_MULTICAST_CONFIG_VERSION_S "multicast_config_version" -#define NET_UDP_MULTICAST_GROUP_S "multicast_group" -#define NET_UDP_MULTICAST_MASK_S "multicast_mask" -#define NET_UDP_MULTICAST_PORT_S "multicast_port" -#define NET_UDP_MULTICAST_TTL_S "multicast_ttl" - -#define NET_UDP_MULTICAST_SNDBUF_S "multicast_sndbuf" -#define NET_UDP_MULTICAST_RCVBUF_S "multicast_rcvbuf" - -#define NET_UDP_MULTICAST_JOIN_ALL_IF_ENABLED_S "multicast_join_all_if" - -#define NET_BANDWIDTH_MAX_UDP_S "bandwidth_max_udp" - -#define NET_UDP_MC_REC_ENABLED_S "udp_mc_rec_enabled" -#define NET_SHM_REC_ENABLED_S "shm_rec_enabled" -#define NET_TCP_REC_ENABLED_S "tcp_rec_enabled" -#define NET_INPROC_REC_ENABLED_S "inproc_rec_enabled" - -#define NET_NPCAP_ENABLED_S "npcap_enabled" - -#define NET_TCP_PUBSUB_NUM_EXECUTOR_READER_S "tcp_pubsub_num_executor_reader" -#define NET_TCP_PUBSUB_NUM_EXECUTOR_WRITER_S "tcp_pubsub_num_executor_writer" -#define NET_TCP_PUBSUB_MAX_RECONNECTIONS_S "tcp_pubsub_max_reconnections" - -///////////////////////////////////// -// time -///////////////////////////////////// -#define TIME_SECTION_S "time" -#define TIME_SYNC_MOD_RT_S "timesync_module_rt" -#define TIME_SYNC_MOD_REPLAY_S "timesync_module_replay" - -///////////////////////////////////// -// process -///////////////////////////////////// -#define PROCESS_SECTION_S "process" -#define PROCESS_TERMINAL_EMULATOR_S "terminal_emulator" - -///////////////////////////////////// -// monitoring -///////////////////////////////////// -#define MON_SECTION_S "monitoring" - -#define MON_TIMEOUT_S "timeout" -#define MON_FILTER_EXCL_S "filter_excl" -#define MON_FILTER_INCL_S "filter_incl" - -#define MON_LOG_FILTER_CON_S "filter_log_con" -#define MON_LOG_FILTER_FILE_S "filter_log_file" -#define MON_LOG_FILTER_UDP_S "filter_log_udp" - -///////////////////////////////////// -// sys -///////////////////////////////////// -#define SYS_SECTION_S "sys" -#define SYS_FILTER_EXCL_S "filter_excl" - -///////////////////////////////////// -// iceoryx -///////////////////////////////////// -#define ICEORYX_SECTION_S "iceoryx" -#define ICEORYX_SERVICE_S "service" -#define ICEORYX_INSTANCE_S "instance" - -///////////////////////////////////// -// publisher -///////////////////////////////////// -#define PUB_SECTION_S "publisher" - -#define PUB_USE_UDP_MC_S "use_udp_mc" -#define PUB_USE_SHM_S "use_shm" -#define PUB_USE_TCP_S "use_tcp" -#define PUB_USE_INPROC_S "use_inproc" - -#define PUB_MEMFILE_MINSIZE_S "memfile_minsize" -#define PUB_MEMFILE_RESERVE_S "memfile_reserve" -#define PUB_MEMFILE_ACK_TO_S "memfile_ack_timeout" -#define PUB_MEMFILE_ZERO_COPY_S "memfile_zero_copy" -#define PUB_MEMFILE_BUF_COUNT_S "memfile_buffer_count" - -#define PUB_SHARE_TTYPE_S "share_ttype" -#define PUB_SHARE_TDESC_S "share_tdesc" - -///////////////////////////////////// -// experimental -///////////////////////////////////// -#define EXP_SECTION_S "experimental" - -#define EXP_SHM_MONITORING_ENABLED_S "shm_monitoring_enabled" -#define EXP_NETWORK_MONITORING_DISABLED_S "network_monitoring_disabled" -#define EXP_SHM_MONITORING_QUEUE_SIZE_S "shm_monitoring_queue_size" -#define EXP_SHM_MONITORING_DOMAIN_S "shm_monitoring_domain" -#define EXP_DROP_OUT_OF_ORDER_MESSAGES_S "drop_out_of_order_messages" diff --git a/ecal/core/src/ecal_defs.h.in b/ecal/core/src/ecal_defs.h.in deleted file mode 100644 index bbe4ac9..0000000 --- a/ecal/core/src/ecal_defs.h.in +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ecal_defs_h_included -#define ecal_defs_h_included -#define ECAL_VERSION_MAJOR (@GIT_REVISION_MAJOR@) -#define ECAL_VERSION_MINOR (@GIT_REVISION_MINOR@) -#define ECAL_VERSION_PATCH (@GIT_REVISION_PATCH@) -#define ECAL_VERSION "@GIT_DESCRIBE_TAG@" -#define ECAL_DATE "@GIT_REVISION_DATE@" -#define ECAL_PLATFORMTOOLSET "@eCAL_PLATFORM_TOOLSET@" - -#define ECAL_INSTALL_APP_DIR "@eCAL_install_app_dir@" -#define ECAL_INSTALL_SAMPLES_DIR "@eCAL_install_samples_dir@" -#define ECAL_INSTALL_LIB_DIR "@eCAL_install_lib_dir@" -#define ECAL_INSTALL_CONFIG_DIR "@eCAL_install_config_dir@" -#define ECAL_INSTALL_INCLUDE_DIR "@eCAL_install_include_dir@" -#define ECAL_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@" - -#endif // ecal_defs_h_included diff --git a/ecal/core/src/ecal_descgate.cpp b/ecal/core/src/ecal_descgate.cpp deleted file mode 100644 index 056d928..0000000 --- a/ecal/core/src/ecal_descgate.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL description gateway class -**/ - -#include -#include - -#include "ecal_descgate.h" -#include -#include -#include - -namespace eCAL -{ - CDescGate::CDescGate() : - m_topic_info_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_service_info_map(std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())) - { - } - CDescGate::~CDescGate() = default; - - void CDescGate::Create() - { - } - - void CDescGate::Destroy() - { - } - - void CDescGate::ApplyTopicDescription(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_, const QualityFlags description_quality_) - { - std::unique_lock lock(m_topic_info_map.sync); - m_topic_info_map.map->remove_deprecated(); - - const auto topic_info_it = m_topic_info_map.map->find(topic_name_); - - // new element (no need to check anything, just add it) - if(topic_info_it == m_topic_info_map.map->end()) - { - // TODO: Maybe we should copy the description from another topic with the same type, if it is empty? - STopicInfoQuality& topic_info = (*m_topic_info_map.map)[topic_name_]; - topic_info.info.type_name = topic_type_; - topic_info.info.type_description = topic_desc_; - topic_info.description_quality = description_quality_; - } - else - { - // existing type for the same topic name should be equal !! - // we log the error only one time - STopicInfoQuality& topic_info = (*m_topic_info_map.map)[topic_name_]; - - if( !topic_info.type_missmatch_logged - && !topic_type_.empty() - && !topic_info.info.type_name.empty() - && (topic_info.info.type_name != topic_type_) - ) - { - std::string ttype1 = topic_info.info.type_name; - std::string ttype2 = topic_type_; - std::replace(ttype1.begin(), ttype1.end(), '\0', '?'); - std::replace(ttype1.begin(), ttype1.end(), '\t', '?'); - std::replace(ttype2.begin(), ttype2.end(), '\0', '?'); - std::replace(ttype2.begin(), ttype2.end(), '\t', '?'); - std::string msg = "eCAL Pub/Sub type mismatch for topic "; - msg += topic_name_; - msg += " (\'"; - msg += ttype1; - msg += "\' <> \'"; - msg += ttype2; - msg += "\')"; - eCAL::Logging::Log(log_level_warning, msg); - - topic_info.type_missmatch_logged = true; - } - - // existing description for the same topic name should be equal !! - // we log the warning only one time - if( !topic_info.type_missmatch_logged - && !topic_desc_.empty() - && !topic_info.info.type_description.empty() - && (topic_info.info.type_description != topic_desc_) - ) - { - std::string msg = "eCAL Pub/Sub description mismatch for topic "; - msg += topic_name_; - eCAL::Logging::Log(log_level_warning, msg); - - topic_info.type_missmatch_logged = true; - } - - // Now let's check whether the current information has a higher quality. - // If it has a higher quality, we overwrite it. - if (description_quality_ > topic_info.description_quality) - { - topic_info.info.type_name = topic_type_; - topic_info.info.type_description = topic_desc_; - topic_info.description_quality = description_quality_; - } - } - } - - void CDescGate::GetTopics(std::unordered_map& topic_info_map_) - { - std::unordered_map map; - - std::shared_lock lock(m_topic_info_map.sync); - m_topic_info_map.map->remove_deprecated(); - map.reserve(m_topic_info_map.map->size()); - - for (const auto& topic_info : (*m_topic_info_map.map)) - { - map.emplace(topic_info.first, topic_info.second.info); - } - topic_info_map_.swap(map); - } - - void CDescGate::GetTopicNames(std::vector& topic_names_) - { - topic_names_.clear(); - - std::shared_lock lock(m_topic_info_map.sync); - m_topic_info_map.map->remove_deprecated(); - topic_names_.reserve(m_topic_info_map.map->size()); - - for (const auto& topic_info : (*m_topic_info_map.map)) - { - topic_names_.emplace_back(topic_info.first); - } - } - - bool CDescGate::GetTopicTypeName(const std::string& topic_name_, std::string& topic_type_) - { - if(topic_name_.empty()) return(false); - - std::shared_lock lock(m_topic_info_map.sync); - const auto topic_info_it = m_topic_info_map.map->find(topic_name_); - - if(topic_info_it == m_topic_info_map.map->end()) return(false); - topic_type_ = (*topic_info_it).second.info.type_name; - return(true); - } - - bool CDescGate::GetTopicDescription(const std::string& topic_name_, std::string& topic_desc_) - { - if (topic_name_.empty()) return(false); - - std::shared_lock lock(m_topic_info_map.sync); - const auto topic_info_it = m_topic_info_map.map->find(topic_name_); - - if (topic_info_it == m_topic_info_map.map->end()) return(false); - topic_desc_ = (*topic_info_it).second.info.type_description; - return(true); - } - - void CDescGate::ApplyServiceDescription(const std::string& service_name_ - , const std::string& method_name_ - , const std::string& req_type_name_ - , const std::string& req_type_desc_ - , const std::string& resp_type_name_ - , const std::string& resp_type_desc_ - , const QualityFlags info_quality_) - { - std::tuple service_method_tuple = std::make_tuple(service_name_, method_name_); - - std::unique_lock lock(m_service_info_map.sync); - m_service_info_map.map->remove_deprecated(); - - auto service_info_map_it = m_service_info_map.map->find(service_method_tuple); - if (service_info_map_it == m_service_info_map.map->end()) - { - SServiceMethodInfoQuality& service_info = (*m_service_info_map.map)[service_method_tuple]; - service_info.info.request_type_name = req_type_name_; - service_info.info.request_type_description = req_type_desc_; - service_info.info.response_type_name = resp_type_name_; - service_info.info.response_type_description = resp_type_desc_; - service_info.info_quality = info_quality_; - } - else - { - // do we need to check consistency ? - SServiceMethodInfoQuality& service_info = (*m_service_info_map.map)[service_method_tuple]; - if (info_quality_ > service_info.info_quality) - { - service_info.info.request_type_name = req_type_name_; - service_info.info.request_type_description = req_type_desc_; - service_info.info.response_type_name = resp_type_name_; - service_info.info.response_type_description = resp_type_desc_; - service_info.info_quality = info_quality_; - } - } - } - - void CDescGate::GetServices(std::map, Util::SServiceMethodInfo>& service_info_map_) - { - std::map, Util::SServiceMethodInfo> map; - - std::shared_lock lock(m_service_info_map.sync); - m_service_info_map.map->remove_deprecated(); - - for (const auto& service_info : (*m_service_info_map.map)) - { - map.emplace(service_info.first, service_info.second.info); - } - service_info_map_.swap(map); - } - - void CDescGate::GetServiceNames(std::vector>& service_method_names_) - { - service_method_names_.clear(); - - std::shared_lock lock(m_service_info_map.sync); - m_service_info_map.map->remove_deprecated(); - service_method_names_.reserve(m_service_info_map.map->size()); - - for (const auto& service_info : (*m_service_info_map.map)) - { - service_method_names_.emplace_back(service_info.first); - } - } - - bool CDescGate::GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_name_, std::string& resp_type_name_) - { - std::tuple service_method_tuple = std::make_tuple(service_name_, method_name_); - - std::shared_lock lock(m_service_info_map.sync); - auto service_info_map_it = m_service_info_map.map->find(service_method_tuple); - - if (service_info_map_it == m_service_info_map.map->end()) return false; - req_type_name_ = (*service_info_map_it).second.info.request_type_name; - resp_type_name_ = (*service_info_map_it).second.info.response_type_name; - return true; - } - - bool CDescGate::GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_type_desc_, std::string& resp_type_desc_) - { - std::tuple service_method_tuple = std::make_tuple(service_name_, method_name_); - - std::shared_lock lock(m_service_info_map.sync); - auto service_info_map_it = m_service_info_map.map->find(service_method_tuple); - - if (service_info_map_it == m_service_info_map.map->end()) return false; - req_type_desc_ = (*service_info_map_it).second.info.request_type_description; - resp_type_desc_ = (*service_info_map_it).second.info.response_type_description; - return true; - } -}; diff --git a/ecal/core/src/ecal_registration_provider.cpp b/ecal/core/src/ecal_registration_provider.cpp deleted file mode 100644 index 1dbf7a3..0000000 --- a/ecal/core/src/ecal_registration_provider.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL registration provider - * - * All process internal publisher/subscriber, server/clients register here with all their attributes. - * - * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. - * -**/ - -#include - -#include "ecal_def.h" -#include "ecal_globals.h" -#include "ecal_registration_provider.h" - -#include "io/udp_configurations.h" -#include "io/snd_sample.h" - -namespace eCAL -{ - extern eCAL_Process_eSeverity g_process_severity; - extern std::string g_process_info; - - extern std::atomic g_process_wbytes; - extern std::atomic g_process_wbytes_sum; - - extern std::atomic g_process_rbytes; - extern std::atomic g_process_rbytes_sum; - - std::atomic CRegistrationProvider::m_created; - - CRegistrationProvider::CRegistrationProvider() : - m_multicast_group(NET_UDP_MULTICAST_GROUP), - m_reg_refresh(CMN_REGISTRATION_REFRESH), - m_reg_topics(false), - m_reg_services(false), - m_reg_process(false), - m_use_network_monitoring(false), - m_use_shm_monitoring(false) - - { - }; - - CRegistrationProvider::~CRegistrationProvider() - { - Destroy(); - } - - void CRegistrationProvider::Create(bool topics_, bool services_, bool process_) - { - if(m_created) return; - - m_reg_refresh = Config::GetRegistrationRefreshMs(); - - m_reg_topics = topics_; - m_reg_services = services_; - m_reg_process = process_; - - m_use_shm_monitoring = Config::Experimental::IsShmMonitoringEnabled(); - m_use_network_monitoring = !Config::Experimental::IsNetworkMonitoringDisabled(); - - if (m_use_network_monitoring) - { - SSenderAttr attr; - bool local_only = !Config::IsNetworkEnabled(); - // for local only communication we switch to local broadcasting to bypass vpn's or firewalls - if (local_only) - { - attr.broadcast = true; - } - else - { - attr.broadcast = false; - } - attr.ipaddr = UDP::GetRegistrationMulticastAddress(); - attr.port = Config::GetUdpMulticastPort() + NET_UDP_MULTICAST_PORT_REG_OFF; - attr.loopback = true; - attr.ttl = Config::GetUdpMulticastTtl(); - attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); - - m_multicast_group = attr.ipaddr; - - m_reg_snd.Create(attr); - } - else - { - std::cout << "Network monitoring is disabled" << std::endl; - } - -#ifndef ECAL_LAYER_ICEORYX - if (m_use_shm_monitoring) - { - std::cout << "Shared memory monitoring is enabled (domain: " << Config::Experimental::GetShmMonitoringDomain() << " - queue size: " << Config::Experimental::GetShmMonitoringQueueSize() << ")" << std::endl; - m_memfile_broadcast.Create(Config::Experimental::GetShmMonitoringDomain(), Config::Experimental::GetShmMonitoringQueueSize()); - m_memfile_broadcast_writer.Bind(&m_memfile_broadcast); - } -#endif - - m_reg_snd_thread.Start(Config::GetRegistrationRefreshMs(), std::bind(&CRegistrationProvider::RegisterSendThread, this)); - - m_created = true; - } - - void CRegistrationProvider::Destroy() - { - if(!m_created) return; - - m_reg_snd_thread.Stop(); - -#ifndef ECAL_LAYER_ICEORYX - if(m_use_shm_monitoring) - { - m_memfile_broadcast_writer.Unbind(); - m_memfile_broadcast.Destroy(); - } -#endif - - m_created = false; - } - - bool CRegistrationProvider::RegisterTopic(const std::string& topic_name_, const std::string& topic_id_, const eCAL::pb::Sample& ecal_sample_, const bool force_) - { - if(!m_created) return(false); - if(!m_reg_topics) return (false); - - std::lock_guard lock(m_topics_map_sync); - m_topics_map[topic_name_ + topic_id_] = ecal_sample_; - if(force_) - { - RegisterProcess(); - RegisterSample(topic_name_, ecal_sample_); -#ifndef ECAL_LAYER_ICEORYX - SendSampleList(false); -#endif - } - - return(true); - } - - bool CRegistrationProvider::UnregisterTopic(const std::string& topic_name_, const std::string& topic_id_) - { - if(!m_created) return(false); - - SampleMapT::iterator iter; - std::lock_guard lock(m_topics_map_sync); - iter = m_topics_map.find(topic_name_ + topic_id_); - if(iter != m_topics_map.end()) - { - m_topics_map.erase(iter); - return(true); - } - - return(false); - } - - bool CRegistrationProvider::RegisterServer(const std::string& service_name_, const std::string& service_id_, const eCAL::pb::Sample& ecal_sample_, const bool force_) - { - if(!m_created) return(false); - if(!m_reg_services) return(false); - - std::lock_guard lock(m_server_map_sync); - m_server_map[service_name_ + service_id_] = ecal_sample_; - if(force_) - { - RegisterProcess(); - RegisterSample(service_name_, ecal_sample_); -#ifndef ECAL_LAYER_ICEORYX - SendSampleList(false); -#endif - } - - return(true); - } - - bool CRegistrationProvider::UnregisterServer(const std::string& service_name_, const std::string& service_id_) - { - if(!m_created) return(false); - - SampleMapT::iterator iter; - std::lock_guard lock(m_server_map_sync); - iter = m_server_map.find(service_name_ + service_id_); - if(iter != m_server_map.end()) - { - m_server_map.erase(iter); - return(true); - } - - return(false); - } - - bool CRegistrationProvider::RegisterClient(const std::string& client_name_, const std::string& client_id_, const eCAL::pb::Sample& ecal_sample_, const bool force_) - { - if (!m_created) return(false); - if (!m_reg_services) return(false); - - std::lock_guard lock(m_client_map_sync); - m_client_map[client_name_ + client_id_] = ecal_sample_; - if (force_) - { - RegisterProcess(); - RegisterSample(client_name_, ecal_sample_); -#ifndef ECAL_LAYER_ICEORYX - SendSampleList(false); -#endif - } - - return(true); - } - - bool CRegistrationProvider::UnregisterClient(const std::string& client_name_, const std::string& client_id_) - { - if (!m_created) return(false); - - SampleMapT::iterator iter; - std::lock_guard lock(m_client_map_sync); - iter = m_client_map.find(client_name_ + client_id_); - if (iter != m_client_map.end()) - { - m_client_map.erase(iter); - return(true); - } - - return(false); - } - - bool CRegistrationProvider::RegisterProcess() - { - if(!m_created) return(0); - if(!m_reg_process) return(0); - - eCAL::pb::Sample process_sample; - process_sample.set_cmd_type(eCAL::pb::bct_reg_process); - auto process_sample_mutable_process = process_sample.mutable_process(); - process_sample_mutable_process->set_hname(Process::GetHostName()); - process_sample_mutable_process->set_pid(Process::GetProcessID()); - process_sample_mutable_process->set_pname(Process::GetProcessName()); - process_sample_mutable_process->set_uname(Process::GetUnitName()); - process_sample_mutable_process->set_pparam(Process::GetProcessParameter()); - process_sample_mutable_process->set_pmemory(Process::GetProcessMemory()); - process_sample_mutable_process->set_pcpu(Process::GetProcessCpuUsage()); - process_sample_mutable_process->set_usrptime(static_cast(Logging::GetCoreTime())); - process_sample_mutable_process->set_datawrite(google::protobuf::int64(Process::GetWBytes())); - process_sample_mutable_process->set_dataread(google::protobuf::int64(Process::GetRBytes())); - process_sample_mutable_process->mutable_state()->set_severity(eCAL::pb::eProcessSeverity(g_process_severity)); - process_sample_mutable_process->mutable_state()->set_info(g_process_info); - if (!g_timegate()) - { - process_sample_mutable_process->set_tsync_state(eCAL::pb::eTSyncState::tsync_none); - } - else - { - if (!g_timegate()->IsSynchronized()) - { - process_sample_mutable_process->set_tsync_state(eCAL::pb::eTSyncState::tsync_none); - } - else - { - switch (g_timegate()->GetSyncMode()) - { - case CTimeGate::eTimeSyncMode::realtime: - process_sample_mutable_process->set_tsync_state(eCAL::pb::eTSyncState::tsync_realtime); - break; - case CTimeGate::eTimeSyncMode::replay: - process_sample_mutable_process->set_tsync_state(eCAL::pb::eTSyncState::tsync_replay); - break; - default: - process_sample_mutable_process->set_tsync_state(eCAL::pb::eTSyncState::tsync_none); - break; - } - } - process_sample_mutable_process->set_tsync_mod_name(g_timegate()->GetName()); - } - - // eCAL initialization state - unsigned int comp_state(g_globals()->GetComponents()); - process_sample_mutable_process->set_component_init_state(google::protobuf::int32(comp_state)); - std::string component_info; - if (comp_state & Init::Publisher) component_info += "|pub"; - if (comp_state & Init::Subscriber) component_info += "|sub"; - if (comp_state & Init::Service) component_info += "|srv"; - if (comp_state & Init::Monitoring) component_info += "|mon"; - if (comp_state & Init::Logging) component_info += "|log"; - if (comp_state & Init::TimeSync) component_info += "|time"; - if (!component_info.empty()) component_info = component_info.substr(1); - process_sample_mutable_process->set_component_init_info(component_info); - - process_sample_mutable_process->set_ecal_runtime_version(eCAL::GetVersionString()); - - // register sample - bool return_value = RegisterSample(Process::GetHostName(), process_sample); - - return return_value; - } - - bool CRegistrationProvider::RegisterServer() - { - if(!m_created) return(0); - if(!m_reg_services) return(0); - - bool return_value {true}; - std::lock_guard lock(m_server_map_sync); - for(SampleMapT::const_iterator iter = m_server_map.begin(); iter != m_server_map.end(); ++iter) - { - // register sample - return_value &= RegisterSample(iter->second.service().sname(), iter->second); - } - - return return_value; - } - - bool CRegistrationProvider::RegisterClient() - { - if (!m_created) return(0); - if (!m_reg_services) return(0); - - bool return_value {true}; - std::lock_guard lock(m_client_map_sync); - for (SampleMapT::const_iterator iter = m_client_map.begin(); iter != m_client_map.end(); ++iter) - { - // register sample - return_value &= RegisterSample(iter->second.client().sname(), iter->second); - } - - return return_value; - } - - bool CRegistrationProvider::RegisterTopics() - { - if(!m_created) return(0); - if(!m_reg_topics) return(0); - - bool return_value {true}; - std::lock_guard lock(m_topics_map_sync); - for(SampleMapT::const_iterator iter = m_topics_map.begin(); iter != m_topics_map.end(); ++iter) - { - return_value &= RegisterSample(iter->second.topic().tname(), iter->second); - } - - return return_value; - } - - bool CRegistrationProvider::RegisterSample(const std::string& sample_name_, const eCAL::pb::Sample& sample_) - { - if(!m_created) return(0); - - bool return_value {true}; - - if(m_use_network_monitoring) - return_value &= (SendSample(&m_reg_snd, sample_name_, sample_, m_multicast_group, -1) != 0); - -#ifndef ECAL_LAYER_ICEORYX - if(m_use_shm_monitoring) - { - std::lock_guard lock(m_sample_list_sync); - m_sample_list.mutable_samples()->Add()->CopyFrom(sample_); - } -#endif - - return return_value; - } - -#ifndef ECAL_LAYER_ICEORYX - bool CRegistrationProvider::SendSampleList(bool reset_sample_list_) - { - if(!m_created) return(false); - bool return_value {true}; - - if(m_use_shm_monitoring) - { - { - std::lock_guard lock(m_sample_list_sync); - m_sample_list.SerializeToString(&m_sample_list_buffer); - if(reset_sample_list_) - m_sample_list.clear_samples(); - } - - if(m_sample_list_buffer.size()) - return_value &=m_memfile_broadcast_writer.Write(m_sample_list_buffer.data(), m_sample_list_buffer.size()); - } - - return return_value; - } -#endif - - int CRegistrationProvider::RegisterSendThread() - { - if(!m_created) return(0); - - // calculate average receive bytes - g_process_rbytes = static_cast(((double)g_process_rbytes_sum / m_reg_refresh)*1000.0); - g_process_rbytes_sum = 0; - - // calculate average write bytes - g_process_wbytes = static_cast(((double)g_process_wbytes_sum / m_reg_refresh)*1000.0); - g_process_wbytes_sum = 0; - - // refresh subscriber registration - if (g_subgate()) g_subgate()->RefreshRegistrations(); - - // refresh publisher registration - if (g_pubgate()) g_pubgate()->RefreshRegistrations(); - - // refresh server registration - if (g_servicegate()) g_servicegate()->RefreshRegistrations(); - - // refresh client registration - if (g_clientgate()) g_clientgate()->RefreshRegistrations(); - - // overall registration send status for debugging - /*bool registration_successful {true};*/ - - // register process - /*registration_successful &= */RegisterProcess(); - - // register server - /*registration_successful &= */RegisterServer(); - - // register clients - /*registration_successful &= */RegisterClient(); - - // register topics - /*registration_successful &= */RegisterTopics(); - -#ifndef ECAL_LAYER_ICEORYX - // write sample list to shared memory - /*registration_successful &= */SendSampleList(); -#endif - - return(0); - }; -}; diff --git a/ecal/core/src/ecal_registration_provider.h b/ecal/core/src/ecal_registration_provider.h deleted file mode 100644 index 00db33f..0000000 --- a/ecal/core/src/ecal_registration_provider.h +++ /dev/null @@ -1,118 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL registration provider - * - * All process internal publisher/subscriber, server/clients register here with all their attributes. - * - * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. - * -**/ - -#pragma once - -#include "ecal_thread.h" -#include "io/udp_sender.h" - -#ifndef ECAL_LAYER_ICEORYX -#include "io/ecal_memfile_broadcast.h" -#include "io/ecal_memfile_broadcast_writer.h" -#endif - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -namespace eCAL -{ - class CRegistrationProvider - { - public: - CRegistrationProvider(); - ~CRegistrationProvider(); - - void Create(bool topics_, bool services_, bool process_); - void Destroy(); - - bool RegisterTopic(const std::string& topic_name_, const std::string& topic_id_, const eCAL::pb::Sample& ecal_sample_, const bool force_); - bool UnregisterTopic(const std::string& topic_name_, const std::string& topic_id_); - - bool RegisterServer(const std::string& service_name_, const std::string& service_id_, const eCAL::pb::Sample& ecal_sample_, const bool force_); - bool UnregisterServer(const std::string& service_name_, const std::string& service_id_); - - bool RegisterClient(const std::string& client_name_, const std::string& client_id_, const eCAL::pb::Sample& ecal_sample_, const bool force_); - bool UnregisterClient(const std::string& client_name_, const std::string& client_id_); - - protected: - bool RegisterProcess(); - bool RegisterServer(); - bool RegisterClient(); - bool RegisterTopics(); - bool RegisterSample(const std::string& sample_name_, const eCAL::pb::Sample& sample_); - - int RegisterSendThread(); - -#ifndef ECAL_LAYER_ICEORYX - bool SendSampleList(bool reset_sample_list_ = true); -#endif - - static std::atomic m_created; - std::string m_multicast_group; - int m_reg_refresh; - bool m_reg_topics; - bool m_reg_services; - bool m_reg_process; - - CUDPSender m_reg_snd; - CThread m_reg_snd_thread; - - typedef std::unordered_map SampleMapT; - std::mutex m_topics_map_sync; - SampleMapT m_topics_map; - - std::mutex m_server_map_sync; - SampleMapT m_server_map; - - std::mutex m_client_map_sync; - SampleMapT m_client_map; - -#ifndef ECAL_LAYER_ICEORYX - std::mutex m_sample_list_sync; - eCAL::pb::SampleList m_sample_list; - std::string m_sample_list_buffer; - - eCAL::CMemoryFileBroadcast m_memfile_broadcast; - eCAL::CMemoryFileBroadcastWriter m_memfile_broadcast_writer; -#endif - - bool m_use_network_monitoring; - bool m_use_shm_monitoring; - }; -}; diff --git a/ecal/core/src/ecal_registration_receiver.cpp b/ecal/core/src/ecal_registration_receiver.cpp deleted file mode 100644 index 21c8c69..0000000 --- a/ecal/core/src/ecal_registration_receiver.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL registration receiver - * - * Receives registration information from external eCAL processes and forwards them to - * the internal publisher/subscriber, server/clients. - * -**/ - -#include "ecal_registration_receiver.h" - -#include "pubsub/ecal_subgate.h" -#include "pubsub/ecal_pubgate.h" -#include "service/ecal_clientgate.h" -#include "service/ecal_servicegate.h" - -#include "io/udp_configurations.h" - -namespace eCAL -{ - size_t CUdpRegistrationReceiver::ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType /*layer_*/) - { - if (!g_registration_receiver()) return 0; - return g_registration_receiver()->ApplySample(ecal_sample_); - }; - -#ifndef ECAL_LAYER_ICEORYX - ////////////////////////////////////////////////////////////////// - // CMemfileRegistrationReceiver - ////////////////////////////////////////////////////////////////// - - bool CMemfileRegistrationReceiver::Create(eCAL::CMemoryFileBroadcastReader* memfile_broadcast_reader_) - { - if (m_created) return false; - m_memfile_broadcast_reader = memfile_broadcast_reader_; - m_created = true; - return true; - } - - bool CMemfileRegistrationReceiver::Receive() - { - if (!m_created) return false; - - MemfileBroadcastMessageListT message_list; - if(!m_memfile_broadcast_reader->Read(message_list, 0)) - return false; - - eCAL::pb::SampleList sample_list; - bool return_value {true}; - - for(const auto& message: message_list) - { - if(sample_list.ParseFromArray(message.data, static_cast(message.size))) - { - for(const auto& sample: sample_list.samples()) - { - return_value &= ApplySample(sample); - } - } - else - return_value = false; - } - return return_value; - } - - bool CMemfileRegistrationReceiver::Destroy() - { - if (!m_created) return false; - m_memfile_broadcast_reader = nullptr; - m_created = false; - return true; - } - - bool CMemfileRegistrationReceiver::ApplySample(const eCAL::pb::Sample& ecal_sample_) - { - if (!g_registration_receiver()) return 0; - return (g_registration_receiver()->ApplySample(ecal_sample_) != 0); - } -#endif - - ////////////////////////////////////////////////////////////////// - // CRegistrationReceiver - ////////////////////////////////////////////////////////////////// - std::atomic CRegistrationReceiver::m_created; - - CRegistrationReceiver::CRegistrationReceiver() : - m_network(NET_ENABLED), - m_loopback(false), - m_callback_pub(nullptr), - m_callback_sub(nullptr), - m_callback_service(nullptr), - m_callback_client(nullptr), - m_callback_process(nullptr), - m_use_network_monitoring(false), - m_use_shm_monitoring(false), - m_callback_custom_apply_sample([](const auto&){}) - - { - }; - - CRegistrationReceiver::~CRegistrationReceiver() - { - Destroy(); - } - - void CRegistrationReceiver::Create() - { - if(m_created) return; - - // network mode - m_network = Config::IsNetworkEnabled(); - - m_use_shm_monitoring = Config::Experimental::IsShmMonitoringEnabled(); - m_use_network_monitoring = !Config::Experimental::IsNetworkMonitoringDisabled(); - - if (m_use_network_monitoring) - { - // start registration receive thread - SReceiverAttr attr; - bool local_only = !Config::IsNetworkEnabled(); - // for local only communication we switch to local broadcasting to bypass vpn's or firewalls - if (local_only) - { - attr.broadcast = true; - } - else - { - attr.broadcast = false; - } - attr.ipaddr = UDP::GetRegistrationMulticastAddress(); - attr.port = Config::GetUdpMulticastPort() + NET_UDP_MULTICAST_PORT_REG_OFF; - attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); - - m_reg_rcv.Create(attr); - m_reg_rcv_thread.Start(0, std::bind(&CUdpRegistrationReceiver::Receive, &m_reg_rcv_process, &m_reg_rcv)); - } - -#ifndef ECAL_LAYER_ICEORYX - if (m_use_shm_monitoring) - { - m_memfile_broadcast.Create(Config::Experimental::GetShmMonitoringDomain(), Config::Experimental::GetShmMonitoringQueueSize()); - m_memfile_broadcast.FlushLocalEventQueue(); - m_memfile_broadcast_reader.Bind(&m_memfile_broadcast); - - m_memfile_reg_rcv.Create(&m_memfile_broadcast_reader); - m_memfile_reg_rcv_thread.Start(Config::GetRegistrationRefreshMs() / 2 , std::bind(&CMemfileRegistrationReceiver::Receive, &m_memfile_reg_rcv)); - } -#endif - - m_created = true; - } - - void CRegistrationReceiver::Destroy() - { - if(!m_created) return; - - if(m_use_network_monitoring) - // stop network registration receive thread - m_reg_rcv_thread.Stop(); - -#ifndef ECAL_LAYER_ICEORYX - if(m_use_shm_monitoring) - { - // stop memfile registration receive thread and unbind reader - m_memfile_reg_rcv_thread.Stop(); - m_memfile_broadcast_reader.Unbind(); - m_memfile_broadcast.Destroy(); - } -#endif - - // reset callbacks - m_callback_pub = nullptr; - m_callback_sub = nullptr; - m_callback_service = nullptr; - m_callback_client = nullptr; - m_callback_process = nullptr; - - // finished - m_created = false; - } - - void CRegistrationReceiver::EnableLoopback(bool state_) - { - m_loopback = state_; - } - - size_t CRegistrationReceiver::ApplySample(const eCAL::pb::Sample& ecal_sample_) - { - if(!m_created) return 0; - - m_callback_custom_apply_sample(ecal_sample_); - - std::string reg_sample; - if ( m_callback_pub - || m_callback_sub - || m_callback_service - || m_callback_client - || m_callback_process - ) - { - reg_sample = ecal_sample_.SerializeAsString(); - } - - switch(ecal_sample_.cmd_type()) - { - case eCAL::pb::bct_none: - case eCAL::pb::bct_set_sample: - break; - case eCAL::pb::bct_reg_process: - if (m_callback_process) m_callback_process(reg_sample.c_str(), static_cast(reg_sample.size())); - break; - case eCAL::pb::bct_reg_service: - if (m_callback_service) m_callback_service(reg_sample.c_str(), static_cast(reg_sample.size())); - if (g_clientgate()) g_clientgate()->ApplyServiceRegistration(ecal_sample_); - break; - case eCAL::pb::bct_reg_client: - if (m_callback_client) m_callback_client(reg_sample.c_str(), static_cast(reg_sample.size())); - if (g_servicegate()) g_servicegate()->ApplyClientRegistration(ecal_sample_); - break; - case eCAL::pb::bct_reg_subscriber: - { - // process local subscriber registrations - if(IsLocalHost(ecal_sample_)) - { - // do not register subscriber of the same process - // only if loop back flag is set true - if(m_loopback || (ecal_sample_.topic().pid() != Process::GetProcessID())) - { - if (g_pubgate()) g_pubgate()->ApplyLocSubRegistration(ecal_sample_); - } - } - // process external subscriber registrations - else - { - if(m_network) - { - if (g_pubgate()) g_pubgate()->ApplyExtSubRegistration(ecal_sample_); - } - } - if (m_callback_sub) m_callback_sub(reg_sample.c_str(), static_cast(reg_sample.size())); - } - break; - case eCAL::pb::bct_reg_publisher: - { - // process local publisher registrations - if(IsLocalHost(ecal_sample_)) - { - // do not register publisher of the same process - // only if loop back flag is set true - if(m_loopback || (ecal_sample_.topic().pid() != Process::GetProcessID())) - { - if (g_subgate()) g_subgate()->ApplyLocPubRegistration(ecal_sample_); - } - } - else - { - if(m_network) - { - if (g_subgate()) g_subgate()->ApplyExtPubRegistration(ecal_sample_); - } - } - if (m_callback_pub) m_callback_pub(reg_sample.c_str(), static_cast(reg_sample.size())); - } - break; - default: - { - eCAL::Logging::Log(log_level_debug1, "CRegGate::ApplySample : unknown sample type"); - } - break; - } - - return 0; - } - - bool CRegistrationReceiver::AddRegistrationCallback(enum eCAL_Registration_Event event_, RegistrationCallbackT callback_) - { - if (!m_created) return false; - switch (event_) - { - case reg_event_publisher: - m_callback_pub = callback_; - return true; - case reg_event_subscriber: - m_callback_sub = callback_; - return true; - case reg_event_service: - m_callback_service = callback_; - return true; - case reg_event_client: - m_callback_client = callback_; - return true; - case reg_event_process: - m_callback_process = callback_; - return true; - default: - return false; - } - } - - bool CRegistrationReceiver::RemRegistrationCallback(enum eCAL_Registration_Event event_) - { - if (!m_created) return false; - switch (event_) - { - case reg_event_publisher: - m_callback_pub = nullptr; - return true; - case reg_event_subscriber: - m_callback_sub = nullptr; - return true; - case reg_event_service: - m_callback_service = nullptr; - return true; - case reg_event_client: - m_callback_client = nullptr; - return true; - case reg_event_process: - m_callback_process = nullptr; - return true; - default: - return false; - } - } - - bool CRegistrationReceiver::IsLocalHost(const eCAL::pb::Sample& ecal_sample_) - { - const std::string host_name = ecal_sample_.topic().hname(); - if (host_name.empty()) return false; - if (host_name != eCAL::Process::GetHostName()) return false; - return true; - } - - void CRegistrationReceiver::SetCustomApplySampleCallback(const ApplySampleCallbackT& callback_) - { - m_callback_custom_apply_sample = callback_; - } - - void CRegistrationReceiver::RemCustomApplySampleCallback() - { - m_callback_custom_apply_sample = [](const auto&){}; - } -}; diff --git a/ecal/core/src/ecal_registration_receiver.h b/ecal/core/src/ecal_registration_receiver.h deleted file mode 100644 index 6f777ed..0000000 --- a/ecal/core/src/ecal_registration_receiver.h +++ /dev/null @@ -1,129 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL registration receiver - * - * Receives registration information from external eCAL processes and forwards them to - * the internal publisher/subscriber, server/clients. - * -**/ - -#pragma once - -#include - -#include "ecal_def.h" -#include "ecal_thread.h" - -#include "io/rcv_sample.h" - -#ifndef ECAL_LAYER_ICEORYX -#include "io/ecal_memfile_broadcast.h" -#include "io/ecal_memfile_broadcast_reader.h" -#endif - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -namespace eCAL -{ - class CUdpRegistrationReceiver : public CSampleReceiver - { - bool HasSample(const std::string& /*sample_name_*/) { return(true); }; - size_t ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_); - }; - -#ifndef ECAL_LAYER_ICEORYX - class CMemfileRegistrationReceiver - { - public: - bool Create(eCAL::CMemoryFileBroadcastReader* memfile_broadcast_reader_); - bool Receive(); - bool Destroy(); - - bool ApplySample(const eCAL::pb::Sample& ecal_sample_); - - private: - bool m_created = false; - eCAL::CMemoryFileBroadcastReader* m_memfile_broadcast_reader = nullptr; - }; -#endif - - typedef std::function ApplySampleCallbackT; - - class CRegistrationReceiver - { - public: - CRegistrationReceiver(); - ~CRegistrationReceiver(); - - void Create(); - void Destroy(); - - void EnableLoopback(bool state_); - bool LoopBackEnabled() { return m_loopback; }; - - size_t ApplySample(const eCAL::pb::Sample& ecal_sample_); - - bool AddRegistrationCallback(enum eCAL_Registration_Event event_, RegistrationCallbackT callback_); - bool RemRegistrationCallback(enum eCAL_Registration_Event event_); - - void SetCustomApplySampleCallback(const ApplySampleCallbackT& callback_); - void RemCustomApplySampleCallback(); - - - protected: - bool IsLocalHost(const eCAL::pb::Sample & ecal_sample_); - - static std::atomic m_created; - bool m_network; - bool m_loopback; - - RegistrationCallbackT m_callback_pub; - RegistrationCallbackT m_callback_sub; - RegistrationCallbackT m_callback_service; - RegistrationCallbackT m_callback_client; - RegistrationCallbackT m_callback_process; - - CUDPReceiver m_reg_rcv; - CThread m_reg_rcv_thread; - CUdpRegistrationReceiver m_reg_rcv_process; - -#ifndef ECAL_LAYER_ICEORYX - eCAL::CMemoryFileBroadcast m_memfile_broadcast; - eCAL::CMemoryFileBroadcastReader m_memfile_broadcast_reader; - CMemfileRegistrationReceiver m_memfile_reg_rcv; - CThread m_memfile_reg_rcv_thread; -#endif - - bool m_use_network_monitoring; - bool m_use_shm_monitoring; - - ApplySampleCallbackT m_callback_custom_apply_sample; - }; -}; diff --git a/ecal/core/src/ecal_thread.cpp b/ecal/core/src/ecal_thread.cpp deleted file mode 100644 index e4bf8ae..0000000 --- a/ecal/core/src/ecal_thread.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief thread interface - windows platform -**/ - -#include - -#include "ecal_thread.h" -#include - -namespace eCAL -{ - CThread::CThread() - { - } - - CThread::~CThread() - { - try { Stop(); } catch(...) { /*??*/ } - } - - int CThread::Start(int period_, std::function ext_caller_) - { - if(m_tdata.is_started) return(0); - - gOpenEvent(&m_tdata.event); - m_tdata.do_stop = false; - m_tdata.period = period_; - m_tdata.ext_caller = ext_caller_; - m_tdata.thread = std::thread(CThread::HelperThread, (void*)&m_tdata); - m_tdata.is_started = true; - - gSetEvent(m_tdata.event); - - return(1); - } - - int CThread::Stop() - { - if(m_tdata.is_started) - { - // signal thread to stop - m_tdata.do_stop = true; - - // release thread wait barrier - Fire(); - - // join thread if joinable and wait for return - if(m_tdata.thread.joinable()) m_tdata.thread.join(); - - // ok the thread is stopped - m_tdata.is_started = false; - - // close event - gCloseEvent(m_tdata.event); - } - - return(1); - } - - int CThread::Fire() - { - gSetEvent(m_tdata.event); - return(1); - } - - void CThread::HelperThread(void* par_) - { - if(!par_) return; - - struct ThreadData* tdata = static_cast(par_); - if(!gEventIsValid(tdata->event)) return; - - // mark as running - tdata->is_running = true; - - int state = 0; - while(!tdata->do_stop) - { - // wait for timeout 'period' - if(tdata->period > 0) gWaitForEvent(tdata->event, tdata->period); - - // call external code - if(!tdata->do_stop && gEventIsValid(tdata->event) && tdata->ext_caller) - { - state = (tdata->ext_caller)(); - } - else - { - state = -1; - } - - if(state < 0) break; - } - - // mark as stopped - tdata->is_running = false; - - return; - } -} diff --git a/ecal/core/src/ecal_thread.h b/ecal/core/src/ecal_thread.h deleted file mode 100644 index 7c5fa3c..0000000 --- a/ecal/core/src/ecal_thread.h +++ /dev/null @@ -1,68 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL threading helper class -**/ - -#pragma once - -#include - -#include -#include -#include - -namespace eCAL -{ - class CThread - { - public: - CThread(); - virtual ~CThread(); - - int Start(int period, std::function ext_caller_); - int Stop(); - int Fire(); - - bool IsRunning() {return(m_tdata.is_running);}; - - protected: - struct ThreadData - { - ThreadData() : - period(0) - , is_running(false) - , is_started(false) - , do_stop(false) - { - }; - std::thread thread; - int period; - EventHandleT event; - std::atomic is_running; - std::atomic is_started; - std::atomic do_stop; - std::function ext_caller; - }; - struct ThreadData m_tdata; - - static void HelperThread(void* par_); - }; -} diff --git a/ecal/core/src/ecal_time.cpp b/ecal/core/src/ecal_time.cpp deleted file mode 100644 index f1040c6..0000000 --- a/ecal/core/src/ecal_time.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL time interface -**/ - -#include - -#include -#include - -#include "ecal_timegate.h" -#include "ecal_process.h" - -namespace eCAL -{ - namespace Time - { - std::string GetName() - { - if (!g_timegate() || !g_timegate()->IsValid()) return(""); - return(g_timegate()->GetName()); - } - - long long GetMicroSeconds() - { - if (!g_timegate() || !g_timegate()->IsValid()) - { - std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - return(std::chrono::duration_cast(now.time_since_epoch()).count()); - } - return(g_timegate()->GetMicroSeconds()); - } - - long long GetNanoSeconds() - { - if (!g_timegate() || !g_timegate()->IsValid()) - { - std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - return(std::chrono::duration_cast(now.time_since_epoch()).count()); - } - return(g_timegate()->GetNanoSeconds()); - } - - bool SetNanoSeconds(long long time_) - { - if (!g_timegate() || !g_timegate()->IsValid()) return(false); - return(g_timegate()->SetNanoSeconds(time_)); - } - - bool IsSynchronized() - { - if (!g_timegate() || !g_timegate()->IsValid()) return(false); - return(g_timegate()->IsSynchronized()); - } - - bool IsMaster() - { - if (!g_timegate() || !g_timegate()->IsValid()) return(false); - return(g_timegate()->IsMaster()); - } - - void SleepForNanoseconds(long long duration_nsecs_) - { - if (!g_timegate() || !g_timegate()->IsValid()) - { - eCAL::Process::SleepFor(std::chrono::nanoseconds(duration_nsecs_)); - } - else - { - g_timegate()->SleepForNanoseconds(duration_nsecs_); - } - } - - void GetStatus(int& error_, std::string* const status_message_) { - if (!g_timegate()) { - error_ = -1; - if (status_message_) { - status_message_->assign("Timegate has not been initialized!"); - } - } - else { - g_timegate()->GetStatus(error_, status_message_); - } - } - } -} diff --git a/ecal/core/src/ecal_util.cpp b/ecal/core/src/ecal_util.cpp deleted file mode 100644 index 703bdac..0000000 --- a/ecal/core/src/ecal_util.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL utility functions -**/ - -#include -#include - -#include "ecal_def.h" -#include "ecal_descgate.h" -#include "ecal_process.h" -#include "ecal_registration_receiver.h" -#include "pubsub/ecal_pubgate.h" -#include "mon/ecal_monitoring_def.h" - -#include -#include -#include -#include -#include -#include - -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_main.h" -#endif /* ECAL_OS_WINDOWS */ - -namespace eCAL -{ - /** - * @brief Return the eCAL process state. - * - * @return True if eCAL is in proper state. - **/ - bool Ok() - { - return(g_shutdown == 0); - } - - namespace Util - { - // take monitoring snapshot - static eCAL::pb::Monitoring GetMonitoring() - { - if (!eCAL::IsInitialized(eCAL::Init::Monitoring)) - { - eCAL::Initialize(0, nullptr, "", eCAL::Init::Monitoring); - eCAL::Process::SleepMS(1000); - } - - eCAL::pb::Monitoring monitoring; - if (eCAL::g_monitoring()) eCAL::g_monitoring()->Monitor(monitoring); - - return(monitoring); - } - - /** - * @brief Send shutdown event to specified local user process using it's process name. - * - * @param process_name_ Fully qualified process name (including the absolute path) - **/ - void ShutdownProcess(const std::string& process_name_) - { - eCAL::pb::Monitoring monitoring = GetMonitoring(); - std::string host_name = eCAL::Process::GetHostName(); - - std::vector proc_id_list; - for (int i = 0; i < monitoring.processes().size(); i++) - { - const eCAL::pb::Process& process = monitoring.processes(i); - std::string pname = process.pname(); - if ((pname == process_name_) - && (process.hname() == host_name) - ) - { - proc_id_list.push_back(process.pid()); - } - } - - for (auto id : proc_id_list) - { - ShutdownProcess(id); - } - } - - /** - * @brief Send shutdown event to specified local user process using it's process id. - * - * @param process_id_ Process id. - **/ - void ShutdownProcess(const int process_id_) - { - std::string event_name = EVENT_SHUTDOWN_PROC + std::string("_") + std::to_string(process_id_); - EventHandleT event; - if (gOpenEvent(&event, event_name)) - { - std::cout << "Shutdown local eCAL process " << process_id_ << std::endl; - gSetEvent(event); - gCloseEvent(event); - } - } - - /** - * @brief Send shutdown event to all local user processes. - **/ - void ShutdownProcesses() - { - eCAL::pb::Monitoring monitoring = GetMonitoring(); - std::string host_name = eCAL::Process::GetHostName(); - - std::vector proc_id_list; - for (int i = 0; i < monitoring.processes().size(); i++) - { - const eCAL::pb::Process& process = monitoring.processes(i); - std::string uname = process.uname(); - if ((uname != "eCALMon") - && (uname != "eCALRPCService") - && (uname != "eCALParam") - && (uname != "eCALPlay") - && (uname != "eCALPlayGUI") - && (uname != "eCALRec") - && (uname != "eCALCanRec") - && (uname != "eCALRecGUI") - && (uname != "eCALStop") - && (uname != "eCALTopic") - && (process.hname() == host_name) - ) - { - proc_id_list.push_back(process.pid()); - } - } - - for (auto id : proc_id_list) - { - ShutdownProcess(id); - } - } - - /** - * @brief Send shutdown event to all core components. - **/ - void ShutdownCore() - { - eCAL::pb::Monitoring monitoring = GetMonitoring(); - std::string host_name = eCAL::Process::GetHostName(); - - std::vector proc_id_list; - for (int i = 0; i < monitoring.processes().size(); i++) - { - const eCAL::pb::Process& process = monitoring.processes(i); - std::string uname = process.uname(); - if (((uname == "eCALMon") - || (uname == "eCALParam") - || (uname == "eCALPlay") - || (uname == "eCALPlayGUI") - || (uname == "eCALRec") - || (uname == "eCALCanRec") - || (uname == "eCALRecGUI") - || (uname == "eCALStop") - || (uname == "eCALTopic") - ) - && (process.hname() == host_name) - ) - { - proc_id_list.push_back(process.pid()); - } - } - - for (auto id : proc_id_list) - { - ShutdownProcess(id); - } - } - - /** - * @brief Enable eCAL message loop back, - * that means subscriber will receive messages from - * publishers of the same process (default == false). - * - * @param Switch on message loop back.. - **/ - void EnableLoopback(bool state_) - { - if (g_registration_receiver()) g_registration_receiver()->EnableLoopback(state_); - } - - /** - * @brief Enable process wide eCAL publisher topic type sharing - * that is needed for reflection on subscriber side. - * - * @param state_ Switch on type sharing - **/ - void PubShareType(bool state_) - { - if (g_pubgate()) g_pubgate()->ShareType(state_); - } - - /** - * @brief Enable process wide eCAL publisher topic description sharing - * that is needed for reflection on subscriber side. - * - * @param state_ Switch on description sharing - **/ - void PubShareDescription(bool state_) - { - if (g_pubgate()) g_pubgate()->ShareDescription(state_); - } - - /** - * @brief Get complete topic map (including types and descriptions). - * - * @param topic_info_map_ Map to store the topic informations. - * Map containing { TopicName -> (Type, Description) } mapping of all topics that are currently known. - **/ - void GetTopics(std::unordered_map& topic_info_map_) - { - if (!g_descgate()) return; - g_descgate()->GetTopics(topic_info_map_); - } - - /** - * @brief Get all topic names. - * - * @param topic_names_ Vector to store the topic names. - **/ - void GetTopicNames(std::vector& topic_names_) - { - if (!g_descgate()) return; - g_descgate()->GetTopicNames(topic_names_); - } - - /** - * @brief Gets type name of the specified topic. - * - * @param topic_name_ Topic name. - * @param topic_type_ String to store type name. - * - * @return True if succeeded. - **/ - bool GetTopicTypeName(const std::string& topic_name_, std::string& topic_type_) - { - if (!g_descgate()) return(false); - return(g_descgate()->GetTopicTypeName(topic_name_, topic_type_)); - } - - // [[deprecated]] - bool GetTypeName(const std::string& topic_name_, std::string& topic_type_) - { - return GetTopicTypeName(topic_name_, topic_type_); - } - - /** - * @brief Gets type name of the specified topic. - * - * @param topic_name_ Topic name. - * - * @return Topic type name. - **/ - std::string GetTopicTypeName(const std::string& topic_name_) - { - std::string topic_type; - if (GetTopicTypeName(topic_name_, topic_type)) - { - return(topic_type); - } - return(""); - } - - // [[deprecated]] - std::string GetTypeName(const std::string& topic_name_) - { - return GetTopicTypeName(topic_name_); - } - - /** - * @brief Gets description of the specified topic. - * - * @param topic_name_ Topic name. - * @param topic_desc_ String to store description. - * - * @return True if succeeded. - **/ - bool GetTopicDescription(const std::string& topic_name_, std::string& topic_desc_) - { - if (!g_descgate()) return(false); - return(g_descgate()->GetTopicDescription(topic_name_, topic_desc_)); - } - - // [[deprecated]] - bool GetDescription(const std::string& topic_name_, std::string& topic_desc_) - { - return GetTopicDescription(topic_name_, topic_desc_); - } - - /** - * @brief Gets description of the specified topic. - * - * @param topic_name_ Topic name. - * - * @return Topic description. - **/ - std::string GetTopicDescription(const std::string& topic_name_) - { - std::string topic_desc; - if (GetTopicDescription(topic_name_, topic_desc)) - { - return(topic_desc); - } - return(""); - } - - // [[deprecated]] - std::string GetDescription(const std::string& topic_name_) - { - return GetTopicDescription(topic_name_); - } - - /** - * @brief Get complete service map (including request and response types and descriptions). - * - * @param service_info_map_ Map to store the topic informations. - * Map { (ServiceName, MethodName) -> ( (ReqType, ReqDescription), (RespType, RespDescription) ) } mapping of all currently known services. - **/ - void GetServices(std::map, Util::SServiceMethodInfo>& service_info_map_) - { - if (!g_descgate()) return; - g_descgate()->GetServices(service_info_map_); - } - - /** - * @brief Get all service/method names. - * - * @param service_names_ Vector to store the service/method tuples (Vector { (ServiceName, MethodName) }). - **/ - void GetServiceNames(std::vector>& service_method_names_) - { - if (!g_descgate()) return; - g_descgate()->GetServiceNames(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. - **/ - bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_) - { - if (!g_descgate()) return(false); - return(g_descgate()->GetServiceTypeNames(service_name_, method_name_, req_type_, 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. - **/ - bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_) - { - if (!g_descgate()) return(false); - return(g_descgate()->GetServiceDescription(service_name_, method_name_, req_desc_, resp_desc_)); - } - } -} diff --git a/ecal/core/src/ecalc.cpp b/ecal/core/src/ecalc.cpp deleted file mode 100644 index 48ff4be..0000000 --- a/ecal/core/src/ecalc.cpp +++ /dev/null @@ -1,1386 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Implementation of the eCAL dll interface -**/ - -#include - -#include -#include -#include - -#include "ecal_process.h" - -static int CopyBuffer(void* target_, int target_len_, const std::string& source_s_) -{ - if(target_ == nullptr) return(0); - if(source_s_.empty()) return(0); - if(target_len_ == ECAL_ALLOCATE_4ME) - { - void* buf_alloc = malloc(source_s_.size()); - if(buf_alloc == nullptr) return(0); - int copied = CopyBuffer(buf_alloc, static_cast(source_s_.size()), source_s_); - if(copied > 0) - { - *((void**)target_) = buf_alloc; //-V206 - return(copied); - } - else - { - // copying buffer failed, so free allocated memory. - free(buf_alloc); - } - } - else - { - if(target_len_ < static_cast(source_s_.size())) return(0); - memcpy(target_, source_s_.data(), source_s_.size()); - return(static_cast(source_s_.size())); - } - return(0); -} - -///////////////////////////////////////////////////////// -// Core -///////////////////////////////////////////////////////// -extern "C" -{ - ECALC_API const char* eCAL_GetVersionString() - { - return(ECAL_VERSION); - } - - ECALC_API const char* eCAL_GetVersionDateString() - { - return(ECAL_DATE); - } - - ECALC_API int eCAL_GetVersion(int* major_, int* minor_, int* patch_) - { - if((major_ == nullptr) && (minor_ == nullptr) && (patch_ == nullptr)) return(-1); - if(major_) *major_ = ECAL_VERSION_MAJOR; - if(minor_) *minor_ = ECAL_VERSION_MINOR; - if(patch_) *patch_ = ECAL_VERSION_PATCH; - return(0); - } - - ECALC_API int eCAL_Initialize(int argc_, char **argv_, const char *unit_name_, unsigned int components_) - { - return(eCAL::Initialize(argc_, argv_, unit_name_, components_)); - } - - ECALC_API int eCAL_SetUnitName(const char *unit_name_) - { - return(eCAL::SetUnitName(unit_name_)); - } - - ECALC_API int eCAL_Finalize(unsigned int components_) - { - return(eCAL::Finalize(components_)); - } - - ECALC_API int eCAL_IsInitialized(unsigned int component_) - { - return(eCAL::IsInitialized(component_)); - } - - ECALC_API int eCAL_Ok() - { - return(eCAL::Ok()); - } - - ECALC_API void eCAL_FreeMem(void* mem_) - { - free(mem_); - } -} - -///////////////////////////////////////////////////////// -// Util -///////////////////////////////////////////////////////// -extern "C" -{ - ECALC_API void eCAL_Util_ShutdownUnitName(const char* unit_name_) - { - std::string unit_name = unit_name_; - eCAL::Util::ShutdownProcess(unit_name); - } - - ECALC_API void eCAL_Util_ShutdownProcessID(int process_id_) - { - eCAL::Util::ShutdownProcess(process_id_); - } - - ECALC_API void eCAL_Util_ShutdownProcesses() - { - eCAL::Util::ShutdownProcesses(); - } - - ECALC_API void eCAL_Util_ShutdownCore() - { - eCAL::Util::ShutdownCore(); - } - - ECALC_API void eCAL_Util_EnableLoopback(int state_) - { - 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_) return(0); - if(!topic_type_) return(0); - std::string topic_type; - if(eCAL::Util::GetTopicTypeName(topic_name_, topic_type)) - { - return(CopyBuffer(topic_type_, topic_type_len_, topic_type)); - } - return(0); - } - - // [[deprecated]] - ECALC_API int eCAL_Util_GetTypeName(const char* topic_name_, void* topic_type_, int topic_type_len_) - { - return(eCAL_Util_GetTopicTypeName(topic_name_, topic_type_, topic_type_len_)); - } - - ECALC_API int eCAL_Util_GetTopicDescription(const char* topic_name_, void* topic_desc_, int topic_desc_len_) - { - if(!topic_name_) return(0); - if(!topic_desc_) return(0); - std::string topic_desc; - if(eCAL::Util::GetTopicDescription(topic_name_, topic_desc)) - { - return(CopyBuffer(topic_desc_, topic_desc_len_, topic_desc)); - } - return(0); - } - - // [[deprecated]] - ECALC_API int eCAL_Util_GetDescription(const char* topic_name_, void* topic_desc_, int topic_desc_len_) - { - return(eCAL_Util_GetTopicDescription(topic_name_, topic_desc_, topic_desc_len_)); - } - - ECALC_API int eCAL_Util_GetServiceRequestTypeName(const char* service_name_, const char* method_name_, void* req_type_, int req_type_len_) - { - if (!service_name_) return(0); - if (!method_name_) return(0); - if (!req_type_) return(0); - std::string req_type, 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_) return(0); - if (!method_name_) return(0); - if (!resp_type_) return(0); - std::string req_type, 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_) return(0); - if (!method_name_) return(0); - if (!req_desc_) return(0); - std::string req_desc, 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_) return(0); - if (!method_name_) return(0); - if (!resp_desc_) return(0); - std::string req_desc, 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; - } -} - -///////////////////////////////////////////////////////// -// Process -///////////////////////////////////////////////////////// -extern "C" -{ - ECALC_API void eCAL_Process_DumpConfig() - { - eCAL::Process::DumpConfig(); - } - - ECALC_API int eCAL_Process_GetHostName(void* name_, int name_len_) - { - std::string name = eCAL::Process::GetHostName(); - if(!name.empty()) - { - return(CopyBuffer(name_, name_len_, name)); - } - return(0); - } - - ECALC_API int eCAL_Process_GetHostID() - { - return(eCAL::Process::internal::GetHostID()); - } - - ECALC_API int eCAL_Process_GetUnitName(void* name_, int name_len_) - { - std::string name = eCAL::Process::GetUnitName(); - if(!name.empty()) - { - return(CopyBuffer(name_, name_len_, name)); - } - return(0); - } - - ECALC_API int eCAL_Process_GetTaskParameter(void* par_, int par_len_, const char* sep_) - { - std::string par = eCAL::Process::GetTaskParameter(sep_); - if(!par.empty()) - { - return(CopyBuffer(par_, par_len_, par)); - } - return(0); - } - - ECALC_API void eCAL_Process_SleepMS(long time_ms_) - { - eCAL::Process::SleepMS(time_ms_); - } - - ECALC_API int eCAL_Process_GetProcessID() - { - return(eCAL::Process::GetProcessID()); - } - - ECALC_API int eCAL_Process_GetProcessName(void* name_, int name_len_) - { - std::string name = eCAL::Process::GetProcessName(); - if(!name.empty()) - { - return(CopyBuffer(name_, name_len_, name)); - } - return(0); - } - - ECALC_API int eCAL_Process_GetProcessParameter(void* par_, int par_len_) - { - std::string par = eCAL::Process::GetProcessParameter(); - if(!par.empty()) - { - return(CopyBuffer(par_, par_len_, par)); - } - return(0); - } - - ECALC_API float eCAL_Process_GetProcessCpuUsage() - { - return(eCAL::Process::GetProcessCpuUsage()); - } - - ECALC_API unsigned long eCAL_Process_GetProcessMemory() - { - return(eCAL::Process::GetProcessMemory()); - } - - ECALC_API long long eCAL_Process_GetSClock() - { - return(eCAL_Process_GetWClock()); - } - - ECALC_API long long eCAL_Process_GetSBytes() - { - return(eCAL_Process_GetWBytes()); - } - - ECALC_API long long eCAL_Process_GetWClock() - { - return(eCAL::Process::GetWClock()); - } - - ECALC_API long long eCAL_Process_GetWBytes() - { - return(eCAL::Process::GetWBytes()); - } - - ECALC_API long long eCAL_Process_GetRClock() - { - return(eCAL::Process::GetRClock()); - } - - ECALC_API long long eCAL_Process_GetRBytes() - { - return(eCAL::Process::GetRBytes()); - } - - ECALC_API void eCAL_Process_SetState(enum eCAL_Process_eSeverity severity_, enum eCAL_Process_eSeverity_Level level_, const char* info_) - { - eCAL::Process::SetState(severity_, level_, info_); - } - - ECALC_API int eCAL_Process_StartProcess(const char* proc_name_, const char* proc_args_, const char* working_dir_, int create_console_, enum eCAL_Process_eStartMode process_mode_, int block_) - { - return(eCAL::Process::StartProcess(proc_name_, proc_args_, working_dir_, create_console_ != 0, process_mode_, block_ != 0)); - } - - ECALC_API int eCAL_Process_StopProcessName(const char* proc_name_) - { - return(eCAL::Process::StopProcess(proc_name_)); - } - - ECALC_API int eCAL_Process_StopProcessID(int proc_id_) - { - return(eCAL::Process::StopProcess(proc_id_)); - } -} - -///////////////////////////////////////////////////////// -// Monitoring -///////////////////////////////////////////////////////// -extern "C" -{ - ECALC_API int eCAL_Monitoring_SetExclFilter(const char* filter_) - { - return(eCAL::Monitoring::SetExclFilter(std::string(filter_))); - } - - ECALC_API int eCAL_Monitoring_SetInclFilter(const char* filter_) - { - return(eCAL::Monitoring::SetInclFilter(std::string(filter_))); - } - - ECALC_API int eCAL_Monitoring_SetFilterState(int state_) - { - return(eCAL::Monitoring::SetFilterState(state_ != 0)); - } - - ECALC_API int eCAL_Monitoring_GetMonitoring(void* buf_, int buf_len_) - { - std::string buf; - if(eCAL::Monitoring::GetMonitoring(buf)) - { - return(CopyBuffer(buf_, buf_len_, buf)); - } - return(0); - } - - ECALC_API int eCAL_Monitoring_GetLogging(void* buf_, int buf_len_) - { - std::string buf; - if(eCAL::Monitoring::GetLogging(buf)) - { - return(CopyBuffer(buf_, buf_len_, buf)); - } - return(0); - } - - ECALC_API int eCAL_Monitoring_PubMonitoring(int state_, const char * name_) - { - return(eCAL::Monitoring::PubMonitoring(state_ != 0, name_)); - } - - ECALC_API int eCAL_Monitoring_PubLogging(int state_, const char * name_) - { - return(eCAL::Monitoring::PubLogging(state_ != 0, name_)); - } -} - -///////////////////////////////////////////////////////// -// Logging -///////////////////////////////////////////////////////// -extern "C" -{ - ECALC_API void eCAL_Logging_SetLogLevel(enum eCAL_Logging_eLogLevel level_) - { - eCAL::Logging::SetLogLevel(level_); - } - - ECALC_API enum eCAL_Logging_eLogLevel eCAL_Logging_GetLogLevel() - { - return(eCAL::Logging::GetLogLevel()); - } - - ECALC_API void eCAL_Logging_Log(const char* const msg_) - { - eCAL::Logging::Log(msg_); - } - - ECALC_API void eCAL_Logging_StartCoreTimer() - { - eCAL::Logging::StartCoreTimer(); - } - - ECALC_API void eCAL_Logging_StopCoreTimer() - { - eCAL::Logging::StopCoreTimer(); - } - - ECALC_API void eCAL_Logging_SetCoreTime(double time_) - { - eCAL::Logging::SetCoreTime(time_); - } - - ECALC_API double eCAL_Logging_GetCoreTime() - { - return(eCAL::Logging::GetCoreTime()); - } -} - -///////////////////////////////////////////////////////// -// Event -///////////////////////////////////////////////////////// -extern "C" -{ - ECALC_API ECAL_HANDLE eCAL_Event_gOpenEvent(const char* event_name_) - { - eCAL::EventHandleT* event_handle = new eCAL::EventHandleT; - bool success = eCAL::gOpenEvent(event_handle, event_name_); - if (success) - { - return(event_handle); - } - else - { - delete event_handle; - return(nullptr); - } - } - - ECALC_API int eCAL_Event_gCloseEvent(ECAL_HANDLE handle_) - { - if (handle_ == nullptr) return(0); - eCAL::EventHandleT* event_handle = static_cast(handle_); - bool success = eCAL::gCloseEvent(*event_handle); - delete event_handle; - return(success); - } - - ECALC_API int eCAL_Event_gSetEvent(ECAL_HANDLE handle_) - { - if (handle_ == nullptr) return(0); - eCAL::EventHandleT* event_handle = static_cast(handle_); - return(eCAL::gSetEvent(*event_handle)); - } - - ECALC_API int eCAL_Event_gWaitForEvent(ECAL_HANDLE handle_, long timeout_) - { - if (handle_ == nullptr) return(0); - eCAL::EventHandleT* event_handle = static_cast(handle_); - return(eCAL::gWaitForEvent(*event_handle, timeout_)); - } - - ECALC_API int eCAL_Event_gEventIsValid(ECAL_HANDLE handle_) - { - if (handle_ == nullptr) return(0); - eCAL::EventHandleT* event_handle = static_cast(handle_); - return(eCAL::gEventIsValid(*event_handle)); - } -} - -///////////////////////////////////////////////////////// -// Publisher -///////////////////////////////////////////////////////// -static std::recursive_mutex g_pub_callback_mtx; -static void g_pub_event_callback(const char* topic_name_, const struct eCAL::SPubEventCallbackData* data_, const PubEventCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_pub_callback_mtx); - SPubEventCallbackDataC data; - data.type = data_->type; - data.time = data_->time; - data.clock = data_->clock; - data.tid = data_->tid.c_str(); - data.ttype = data_->ttype.c_str(); - data.tdesc = data_->tdesc.c_str(); - callback_(topic_name_, &data, par_); -} - -extern "C" -{ - ECALC_API ECAL_HANDLE eCAL_Pub_New() - { - eCAL::CPublisher* pub = new eCAL::CPublisher; - return(pub); - } - - ECALC_API int eCAL_Pub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_, const char* topic_desc_, int topic_desc_len_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (!pub->Create(topic_name_, topic_type_, std::string(topic_desc_, static_cast(topic_desc_len_)))) return(0); - return(1); - } - - ECALC_API int eCAL_Pub_Destroy(ECAL_HANDLE handle_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - delete pub; - pub = NULL; - return(1); - } - - ECALC_API int eCAL_Pub_SetTypeName(ECAL_HANDLE handle_, const char* topic_type_name_, int topic_type_name_len_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->SetTypeName(std::string(topic_type_name_, static_cast(topic_type_name_len_)))) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_SetDescription(ECAL_HANDLE handle_, const char* topic_desc_, int topic_desc_len_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->SetDescription(std::string(topic_desc_, static_cast(topic_desc_len_)))) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_SetAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_, const char* attr_value_, int attr_value_len_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->SetAttribute(std::string(attr_name_, static_cast(attr_name_len_)), std::string(attr_value_, static_cast(attr_value_len_)))) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_ClearAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->ClearAttribute(std::string(attr_name_, static_cast(attr_name_len_)))) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_ShareType(ECAL_HANDLE handle_, int state_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - pub->ShareType(state_ != 0); - return(1); - } - - ECALC_API int eCAL_Pub_ShareDescription(ECAL_HANDLE handle_, int state_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - pub->ShareDescription(state_ != 0); - return(1); - } - - ECALC_API int eCAL_Pub_SetQOS(ECAL_HANDLE handle_, struct SWriterQOSC qos_) //-V813 - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - eCAL::QOS::SWriterQOS qos; - qos.history_kind = static_cast(qos_.history_kind); - qos.history_kind_depth = qos_.history_kind_depth; - qos.reliability = static_cast(qos_.reliability); - if (pub->SetQOS(qos)) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_GetQOS(ECAL_HANDLE handle_, struct SWriterQOSC* qos_) - { - if (handle_ == NULL) return(0); - if (qos_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - eCAL::QOS::SWriterQOS qos = pub->GetQOS(); - qos_->history_kind = static_cast(qos.history_kind); - qos_->history_kind_depth = qos.history_kind_depth;; - qos_->reliability = static_cast(qos.reliability); - return(0); - } - - ECALC_API int eCAL_Pub_SetLayerMode(ECAL_HANDLE handle_, enum eTransportLayerC layer_, enum eSendModeC mode_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->SetLayerMode(static_cast(layer_), static_cast(mode_))) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_SetMaxBandwidthUDP(ECAL_HANDLE handle_, long bandwidth_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->SetMaxBandwidthUDP(bandwidth_)) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_ShmSetBufferCount(ECAL_HANDLE handle_, long buffering_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->ShmSetBufferCount(buffering_)) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_ShmEnableZeroCopy(ECAL_HANDLE handle_, int state_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->ShmEnableZeroCopy(state_ != 0)) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_SetID(ECAL_HANDLE handle_, long long id_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->SetID(id_)) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_IsSubscribed(ECAL_HANDLE handle_) - { - if(handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if(pub->IsSubscribed()) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_Send(ECAL_HANDLE handle_, const void* const buf_, int buf_len_, long long time_) - { - if(handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - size_t ret = pub->Send(buf_, static_cast(buf_len_), time_); - if(static_cast(ret) == buf_len_) - { - return(buf_len_); - } - return(0); - } - - ECALC_API int eCAL_Pub_AddEventCallback(ECAL_HANDLE handle_, eCAL_Publisher_Event type_, PubEventCallbackCT callback_, void * par_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - auto callback = std::bind(g_pub_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - if (pub->AddEventCallback(type_, callback)) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_AddEventCallbackC(ECAL_HANDLE handle_, eCAL_Publisher_Event type_, PubEventCallbackCT callback_, void* par_) - { - return eCAL_Pub_AddEventCallback(handle_, type_, callback_, par_); - } - - ECALC_API int eCAL_Pub_RemEventCallback(ECAL_HANDLE handle_, eCAL_Publisher_Event type_) - { - if (handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - if (pub->RemEventCallback(type_)) return(1); - return(0); - } - - ECALC_API int eCAL_Pub_Dump(ECAL_HANDLE handle_, void* buf_, int buf_len_) - { - if(handle_ == NULL) return(0); - eCAL::CPublisher* pub = static_cast(handle_); - std::string dump = pub->Dump(); - if(!dump.empty()) - { - return(CopyBuffer(buf_, buf_len_, dump)); - } - return(0); - } -} - -///////////////////////////////////////////////////////// -// Subscriber -///////////////////////////////////////////////////////// -static std::recursive_mutex g_sub_callback_mtx; -static void g_sub_receive_callback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_, const ReceiveCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_sub_callback_mtx); - SReceiveCallbackDataC data; - data.buf = data_->buf; - data.size = data_->size; - data.id = data_->id; - data.time = data_->time; - data.clock = data_->clock; - callback_(topic_name_, &data, par_); -} - -static void g_sub_event_callback(const char* topic_name_, const struct eCAL::SSubEventCallbackData* data_, const SubEventCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_sub_callback_mtx); - SSubEventCallbackDataC data; - data.type = data_->type; - data.time = data_->time; - data.clock = data_->clock; - data.tid = data_->tid.c_str(); - data.ttype = data_->ttype.c_str(); - data.tdesc = data_->tdesc.c_str(); - callback_(topic_name_, &data, par_); -} - -extern "C" -{ - ECALC_API ECAL_HANDLE eCAL_Sub_New() - { - eCAL::CSubscriber* sub = new eCAL::CSubscriber; - return(sub); - } - - ECALC_API int eCAL_Sub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_, const char* topic_desc_, int topic_desc_len_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - if (!sub->Create(topic_name_, topic_type_, std::string(topic_desc_, static_cast(topic_desc_len_)))) return(0); - return(1); - } - - ECALC_API int eCAL_Sub_Destroy(ECAL_HANDLE handle_) - { - if(handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - delete sub; - sub = NULL; - return(1); - } - - ECALC_API int eCAL_Sub_SetQOS(ECAL_HANDLE handle_, struct SReaderQOSC qos_) //-V813 - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - eCAL::QOS::SReaderQOS qos; - qos.history_kind = static_cast(qos_.history_kind); - qos.history_kind_depth = qos_.history_kind_depth; - qos.reliability = static_cast(qos_.reliability); - if (sub->SetQOS(qos)) return(1); - return(0); - } - - ECALC_API int eCAL_Sub_SetID(ECAL_HANDLE handle_, const long long* id_array_, const int id_num_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - std::set id_set; - if (id_array_ != NULL) - { - for (size_t i = 0; i < static_cast(id_num_); ++i) - { - id_set.insert(id_array_[i]); - } - } - if (sub->SetID(id_set)) return(1); - return(1); - } - - ECALC_API int eCAL_Sub_SetAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_, const char* attr_value_, int attr_value_len_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - if (sub->SetAttribute(std::string(attr_name_, static_cast(attr_name_len_)), std::string(attr_value_, static_cast(attr_value_len_)))) return(1); - return(0); - } - - ECALC_API int eCAL_Sub_ClearAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - if (sub->ClearAttribute(std::string(attr_name_, static_cast(attr_name_len_)))) return(1); - return(0); - } - - ECALC_API int eCAL_Sub_GetQOS(ECAL_HANDLE handle_, struct SReaderQOSC* qos_) - { - if (handle_ == NULL) return(0); - if (qos_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - eCAL::QOS::SReaderQOS qos = sub->GetQOS(); - qos_->history_kind = static_cast(qos.history_kind); - qos_->history_kind_depth = qos.history_kind_depth;; - qos_->reliability = static_cast(qos.reliability); - return(0); - } - - ECALC_API int eCAL_Sub_Receive(ECAL_HANDLE handle_, void* buf_, int buf_len_, long long* time_, int rcv_timeout_) - { - if (buf_len_ == ECAL_ALLOCATE_4ME) - { - return eCAL_Sub_Receive_Alloc(handle_, static_cast(buf_), time_, rcv_timeout_); //-V206 - } - else - { - return eCAL_Sub_Receive_ToBuffer(handle_, buf_, buf_len_, time_, rcv_timeout_); - } - } - - ECALC_API int eCAL_Sub_Receive_ToBuffer(ECAL_HANDLE handle_, void* buf_, int buf_len_, long long* time_, int rcv_timeout_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - - std::string buf; - if (sub->ReceiveBuffer(buf, time_, rcv_timeout_)) - { - return(CopyBuffer(buf_, buf_len_, buf)); - } - return(0); - } - - ECALC_API int eCAL_Sub_Receive_Alloc(ECAL_HANDLE handle_, void** buf_, long long* time_, int rcv_timeout_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - - std::string buf; - if (sub->ReceiveBuffer(buf, time_, rcv_timeout_)) - { - return(CopyBuffer(buf_, ECAL_ALLOCATE_4ME, buf)); - } - return(0); - } - - ECALC_API int eCAL_Sub_Receive_Buffer_Alloc(ECAL_HANDLE handle_, void** buf_, int* buf_len_, long long* time_, int rcv_timeout_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - - std::string buf; - if (sub->ReceiveBuffer(buf, time_, rcv_timeout_)) - { - CopyBuffer(buf_, ECAL_ALLOCATE_4ME, buf); - if (buf_len_) *buf_len_ = static_cast(buf.size()); - return(1); - } - return(0); - } - - ECALC_API int eCAL_Sub_AddReceiveCallback(ECAL_HANDLE handle_, ReceiveCallbackCT callback_, void* par_) - { - if(handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - auto callback = std::bind(g_sub_receive_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - if(sub->AddReceiveCallback(callback)) return(1); - return(0); - } - - ECALC_API_DEPRECATED int eCAL_Sub_AddReceiveCallbackC(ECAL_HANDLE handle_, ReceiveCallbackCT callback_, void* par_) - { - return eCAL_Sub_AddReceiveCallback(handle_, callback_, par_); - } - - ECALC_API int eCAL_Sub_RemReceiveCallback(ECAL_HANDLE handle_) - { - if(handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - if(sub->RemReceiveCallback()) return(1); - return(0); - } - - ECALC_API int eCAL_Sub_AddEventCallback(ECAL_HANDLE handle_, eCAL_Subscriber_Event type_, SubEventCallbackCT callback_, void* par_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - auto callback = std::bind(g_sub_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - if (sub->AddEventCallback(type_, callback)) return(1); - return(0); - } - - ECALC_API_DEPRECATED int eCAL_Sub_AddEventCallbackC(ECAL_HANDLE handle_, eCAL_Subscriber_Event type_, SubEventCallbackCT callback_, void* par_) - { - return eCAL_Sub_AddEventCallback(handle_, type_, callback_, par_); - } - - ECALC_API int eCAL_Sub_RemEventCallback(ECAL_HANDLE handle_, eCAL_Subscriber_Event type_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - if (sub->RemEventCallback(type_)) return(1); - return(0); - } - - ECALC_API int eCAL_Sub_GetDescription(ECAL_HANDLE handle_, void* buf_, int buf_len_) - { - if(handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - std::string desc = sub->GetDescription(); - int buffer_len = CopyBuffer(buf_, buf_len_, desc); - if (buffer_len != static_cast(desc.size())) - { - return(0); - } - else - { - return(buffer_len); - } - } - - ECALC_API int eCAL_Sub_SetTimeout(ECAL_HANDLE handle_, int timeout_) - { - if (handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - return(sub->SetTimeout(timeout_)); - } - - ECALC_API int eCAL_Sub_Dump(ECAL_HANDLE handle_, void* buf_, int buf_len_) - { - if(handle_ == NULL) return(0); - eCAL::CSubscriber* sub = static_cast(handle_); - std::string dump = sub->Dump(); - if(!dump.empty()) - { - return(CopyBuffer(buf_, buf_len_, dump)); - } - return(0); - } -} - -static std::recursive_mutex g_dyn_json_sub_receive_callback_mtx; -static void g_dyn_json_sub_receive_callback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_, const ReceiveCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_dyn_json_sub_receive_callback_mtx); - SReceiveCallbackDataC data; - data.buf = data_->buf; - data.size = data_->size; - data.id = data_->id; - data.time = data_->time; - data.clock = data_->clock; - callback_(topic_name_, &data, par_); -} - -extern "C" -{ - ECAL_HANDLE eCAL_Proto_Dyn_JSON_Sub_Create(const char* topic_name_) - { - eCAL::protobuf::CDynamicJSONSubscriber* sub = new eCAL::protobuf::CDynamicJSONSubscriber(topic_name_); - if (!sub->IsCreated()) - { - delete sub; - return(nullptr); - } - return(sub); - } - - int eCAL_Proto_Dyn_JSON_Sub_Destroy(ECAL_HANDLE handle_) - { - eCAL::protobuf::CDynamicJSONSubscriber* sub = static_cast(handle_); - if (sub) - { - delete sub; - sub = nullptr; - return(1); - } - return(0); - } - - int eCAL_Proto_Dyn_JSON_Sub_AddReceiveCallback(ECAL_HANDLE handle_, const ReceiveCallbackCT callback_, void* par_) - { - eCAL::protobuf::CDynamicJSONSubscriber* sub = static_cast(handle_); - - auto callback = std::bind(g_dyn_json_sub_receive_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - return(sub->AddReceiveCallback(callback)); - } - - int eCAL_Proto_Dyn_JSON_Sub_AddReceiveCallbackC(ECAL_HANDLE handle_, const ReceiveCallbackCT callback_, void* par_) - { - return eCAL_Proto_Dyn_JSON_Sub_AddReceiveCallback(handle_, callback_, par_); - } - - int eCAL_Proto_Dyn_JSON_Sub_RemReceiveCallback(ECAL_HANDLE handle_) - { - eCAL::protobuf::CDynamicJSONSubscriber* sub = static_cast(handle_); - - return(sub->RemReceiveCallback()); - } -} - -///////////////////////////////////////////////////////// -// Time -///////////////////////////////////////////////////////// -ECALC_API int eCAL_Time_GetName(void* name_, int name_len_) -{ - std::string name = eCAL::Time::GetName(); - if (!name.empty()) - { - return(CopyBuffer(name_, name_len_, name)); - } - return(0); -} - -ECALC_API long long eCAL_Time_GetMicroSeconds() -{ - return(eCAL::Time::GetMicroSeconds()); -} - -ECALC_API long long eCAL_Time_GetNanoSeconds() -{ - return(eCAL::Time::GetNanoSeconds()); -} - -ECALC_API int eCAL_Time_SetNanoSeconds(long long time_) -{ - return(eCAL::Time::SetNanoSeconds(time_)); -} - -ECALC_API int eCAL_Time_IsTimeSynchronized() -{ - return(eCAL::Time::IsSynchronized()); -} - -ECALC_API int eCAL_Time_IsTimeMaster() -{ - return(eCAL::Time::IsMaster()); -} - -ECALC_API void eCAL_Time_SleepForNanoseconds(long long duration_nsecs_) -{ - eCAL::Time::SleepForNanoseconds(duration_nsecs_); -} - -ECALC_API int eCAL_Time_GetStatus(int* error_, char** status_message_, const int max_len_) -{ - if (max_len_ == ECAL_ALLOCATE_4ME || max_len_ > 0) - { - std::string status_message; - eCAL::Time::GetStatus(*error_, &status_message); - - if (!status_message.empty()) - { - return CopyBuffer(status_message_, max_len_, status_message); - } - return 0; - } - else - { - eCAL::Time::GetStatus(*error_, nullptr); - return 0; - } -} - -///////////////////////////////////////////////////////// -// Timer -///////////////////////////////////////////////////////// -static std::recursive_mutex g_timer_callback_mtx; -static void g_timer_callback(const TimerCallbackCT callback_, void* par_) -{ - std::lock_guard lock(g_timer_callback_mtx); - callback_(par_); -} - -extern "C" -{ - ECALC_API ECAL_HANDLE eCAL_Timer_Create() - { - eCAL::CTimer* timer = new eCAL::CTimer; - return(timer); - } - - ECALC_API int eCAL_Timer_Destroy(ECAL_HANDLE handle_) - { - if(handle_ == NULL) return(0); - eCAL::CTimer* timer = static_cast(handle_); - delete timer; - timer = NULL; - return(1); - } - - ECALC_API int eCAL_Timer_Start(ECAL_HANDLE handle_, int timeout_, TimerCallbackCT callback_, int delay_, void* par_) - { - if(handle_ == NULL) return(0); - eCAL::CTimer* timer = static_cast(handle_); - auto callback = std::bind(g_timer_callback, callback_, par_); - if(timer->Start(timeout_, callback, delay_)) return(1); - else return(0); - } - - ECALC_API int eCAL_Timer_Stop(ECAL_HANDLE handle_) - { - if(handle_ == NULL) return(0); - eCAL::CTimer* timer = static_cast(handle_); - if(timer->Stop()) return(1); - else return(0); - } -} - -///////////////////////////////////////////////////////// -// Service Server -///////////////////////////////////////////////////////// -extern "C" -{ - static std::recursive_mutex g_request_callback_mtx; - static int g_method_callback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_, MethodCallbackCT callback_, void* par_) - { - std::lock_guard lock(g_request_callback_mtx); - void* response(nullptr); - int response_len(ECAL_ALLOCATE_4ME); - int ret_state = callback_(method_.c_str(), req_type_.c_str(), resp_type_.c_str(), request_.c_str(), static_cast(request_.size()), &response, &response_len, par_); - if (response_len > 0) - { - response_ = std::string(static_cast(response), static_cast(response_len)); - } - return ret_state; - } - - static void g_server_event_callback(const char* name_, const struct eCAL::SServerEventCallbackData* data_, const ServerEventCallbackCT callback_, void* par_) - { - std::lock_guard lock(g_sub_callback_mtx); - SServerEventCallbackDataC data; - data.time = data_->time; - data.type = data_->type; - callback_(name_, &data, par_); - } - - ECALC_API ECAL_HANDLE eCAL_Server_Create(const char* service_name_) - { - if (service_name_ == NULL) return(NULL); - eCAL::CServiceServer* server = new eCAL::CServiceServer(service_name_); - return(server); - } - - ECALC_API int eCAL_Server_Destroy(ECAL_HANDLE handle_) - { - if (handle_ == NULL) return(0); - eCAL::CServiceServer* server = static_cast(handle_); - delete server; - server = NULL; - return(1); - } - - ECALC_API int eCAL_Server_AddMethodCallback(ECAL_HANDLE handle_, const char* method_, const char* req_type_, const char* resp_type_, MethodCallbackCT callback_, void* par_) - { - if (handle_ == NULL) return(0); - eCAL::CServiceServer* server = static_cast(handle_); - auto callback = std::bind(g_method_callback, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, callback_, par_); - return server->AddMethodCallback(method_, req_type_, resp_type_, callback); - } - - ECALC_API_DEPRECATED int eCAL_Server_AddMethodCallbackC(ECAL_HANDLE handle_, const char* method_, const char* req_type_, const char* resp_type_, MethodCallbackCT callback_, void* par_) - { - return eCAL_Server_AddMethodCallback(handle_, method_, req_type_, resp_type_, callback_, par_); - } - - ECALC_API int eCAL_Server_RemMethodCallback(ECAL_HANDLE handle_, const char* method_) - { - if (handle_ == NULL) return(0); - eCAL::CServiceServer* server = static_cast(handle_); - return server->RemMethodCallback(method_); - } - - ECALC_API_DEPRECATED int eCAL_Server_RemMethodCallbackC(ECAL_HANDLE handle_, const char* method_) - { - return eCAL_Server_RemMethodCallback(handle_, method_); - } - - ECALC_API int eCAL_Server_AddEventCallback(ECAL_HANDLE handle_, eCAL_Server_Event type_, ServerEventCallbackCT callback_, void* par_) - { - if (handle_ == NULL) return(0); - eCAL::CServiceServer* server = static_cast(handle_); - auto callback = std::bind(g_server_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - if (server->AddEventCallback(type_, callback)) return(1); - return(0); - } - - ECALC_API int eCAL_Server_RemEventCallback(ECAL_HANDLE handle_, eCAL_Server_Event type_) - { - if (handle_ == NULL) return(0); - eCAL::CServiceServer* server = static_cast(handle_); - if (server->RemEventCallback(type_)) return(1); - return(0); - } - - ECALC_API int eCAL_Server_GetServiceName(ECAL_HANDLE handle_, void* buf_, int buf_len_) - { - if (handle_ == NULL) return(0); - eCAL::CServiceServer* server = static_cast(handle_); - std::string service_name = server->GetServiceName(); - int buffer_len = CopyBuffer(buf_, buf_len_, service_name); - if (buffer_len != static_cast(service_name.size())) - { - return(0); - } - else - { - return(buffer_len); - } - } -} - -///////////////////////////////////////////////////////// -// Service Client -///////////////////////////////////////////////////////// -extern "C" -{ - static std::recursive_mutex g_response_callback_mtx; - static void g_response_callback(const struct eCAL::SServiceResponse& service_response_, const ResponseCallbackCT callback_, void* par_) - { - std::lock_guard lock(g_response_callback_mtx); - struct SServiceResponseC service_response; - service_response.host_name = service_response_.host_name.c_str(); - service_response.service_name = service_response_.service_name.c_str(); - service_response.service_id = service_response_.service_id.c_str(); - service_response.method_name = service_response_.method_name.c_str(); - service_response.error_msg = service_response_.error_msg.c_str(); - service_response.ret_state = service_response_.ret_state; - service_response.call_state = service_response_.call_state; - service_response.response = service_response_.response.c_str(); - service_response.response_len = static_cast(service_response_.response.size()); - callback_(&service_response, par_); - } - - static void g_client_event_callback(const char* name_, const struct eCAL::SClientEventCallbackData* data_, const ClientEventCallbackCT callback_, void* par_) - { - std::lock_guard lock(g_sub_callback_mtx); - SClientEventCallbackDataC data; - data.time = data_->time; - data.type = data_->type; - callback_(name_, &data, par_); - } - - ECALC_API ECAL_HANDLE eCAL_Client_Create(const char* service_name_) - { - if(service_name_ == NULL) return(NULL); - eCAL::CServiceClient* client = new eCAL::CServiceClient(service_name_); - return(client); - } - - ECALC_API int eCAL_Client_Destroy(ECAL_HANDLE handle_) - { - if(handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - delete client; - client = NULL; - return(1); - } - - ECALC_API int eCAL_Client_SetHostName(ECAL_HANDLE handle_, const char* host_name_) - { - if (handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - if (client->SetHostName(host_name_)) return(1); - return(0); - } - - ECALC_API int eCAL_Client_Call(ECAL_HANDLE handle_, const char* method_name_, const char* request_, int request_len_, int timeout_) - { - if(handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - if(client->Call(method_name_, std::string(request_, static_cast(request_len_)), timeout_)) return(1); - return(0); - } - - // The C API variant is not able to return all service repsonses but only the first one ! - ECALC_API int eCAL_Client_Call_Wait(ECAL_HANDLE handle_, const char* method_name_, const char* request_, int request_len_, int timeout_, struct SServiceResponseC* service_response_, void* response_, int response_len_) - { - if(handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - eCAL::ServiceResponseVecT service_response_vec; - if(client->Call(method_name_, std::string(request_, static_cast(request_len_)), timeout_, &service_response_vec)) - { - if (service_response_vec.size() > 0) - { - service_response_->host_name = NULL; - service_response_->service_name = NULL; - service_response_->service_id = NULL; - service_response_->method_name = NULL; - service_response_->error_msg = NULL; - service_response_->ret_state = service_response_vec[0].ret_state; - service_response_->call_state = service_response_vec[0].call_state; - service_response_->response = NULL; - service_response_->response_len = 0; - return(CopyBuffer(response_, response_len_, service_response_vec[0].response)); - } - } - return(0); - } -} - -ECALC_API int eCAL_Client_Call_Async(ECAL_HANDLE handle_, const char* method_name_, const char* request_, int request_len_, int timeout_) -{ - if (handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - if (client->CallAsync(method_name_, std::string(request_, static_cast(request_len_)), timeout_)) return(1); - return(0); -} - -int eCAL_Client_AddResponseCallback(ECAL_HANDLE handle_, ResponseCallbackCT callback_, void* par_) -{ - if(handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - auto callback = std::bind(g_response_callback, std::placeholders::_1, callback_, par_); - return client->AddResponseCallback(callback); -} - -int eCAL_Client_AddResponseCallbackC(ECAL_HANDLE handle_, ResponseCallbackCT callback_, void* par_) -{ - return eCAL_Client_AddResponseCallback(handle_, callback_, par_); -} - - -int eCAL_Client_RemResponseCallback(ECAL_HANDLE handle_) -{ - if(handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - return client->RemResponseCallback(); -} - -ECALC_API int eCAL_Client_AddEventCallback(ECAL_HANDLE handle_, eCAL_Client_Event type_, ClientEventCallbackCT callback_, void* par_) -{ - if (handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - auto callback = std::bind(g_client_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); - if (client->AddEventCallback(type_, callback)) return(1); - return(0); -} - -ECALC_API int eCAL_Client_RemEventCallback(ECAL_HANDLE handle_, eCAL_Client_Event type_) -{ - if (handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - if (client->RemEventCallback(type_)) return(1); - return(0); -} - -ECALC_API int eCAL_Client_GetServiceName(ECAL_HANDLE handle_, void* buf_, int buf_len_) -{ - if (handle_ == NULL) return(0); - eCAL::CServiceClient* client = static_cast(handle_); - std::string service_name = client->GetServiceName(); - int buffer_len = CopyBuffer(buf_, buf_len_, service_name); - if (buffer_len != static_cast(service_name.size())) - { - return(0); - } - else - { - return(buffer_len); - } -} diff --git a/ecal/core/src/io/ecal_receiver.h b/ecal/core/src/io/ecal_receiver.h deleted file mode 100644 index 808fa9c..0000000 --- a/ecal/core/src/io/ecal_receiver.h +++ /dev/null @@ -1,97 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL receiver base class -**/ - -#pragma once - -#include -#include "ecal_def.h" - -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_socket.h" -#endif - -#ifdef ECAL_OS_LINUX -#include -#include -#include -#endif - -#include - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // receiver base class - //////////////////////////////////////////////////////// - struct SReceiverAttr - { - SReceiverAttr() : - port(0), - broadcast(false), - unicast(false), - loopback(true), - rcvbuf(1024 * 1024) - {}; - - std::string ipaddr; - int port; - bool broadcast; - bool unicast; - bool loopback; - int rcvbuf; - }; - - class CReceiver - { - public: - enum eSocketType - { - SType_Unknown = 0, - SType_ReceiverUDP, - SType_ReceiverTCP - }; - - public: - CReceiver() : m_skt_type(SType_Unknown) - { - }; - - explicit CReceiver(eSocketType skt_type_) : m_skt_type(skt_type_) - { - }; - - virtual ~CReceiver() - { - }; - - virtual bool Create(const SReceiverAttr& attr_) = 0; - virtual bool Destroy() = 0; - - virtual size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) = 0; - - inline eSocketType GetType() const {return m_skt_type;}; - - protected: - eSocketType m_skt_type; - }; -} diff --git a/ecal/core/src/io/ecal_sender.h b/ecal/core/src/io/ecal_sender.h deleted file mode 100644 index db899cf..0000000 --- a/ecal/core/src/io/ecal_sender.h +++ /dev/null @@ -1,85 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL sender base class -**/ - -#pragma once - -#include -#include - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // sender base class - //////////////////////////////////////////////////////// - struct SSenderAttr - { - SSenderAttr() : - port(0), - ttl(0), - broadcast(false), - unicast(false), - loopback(true), - sndbuf(1024 * 1024) - {}; - - std::string ipaddr; - int port; - int ttl; - bool broadcast; - bool unicast; - bool loopback; - int sndbuf; - }; - - class CSender - { - public: - enum eSocketType - { - SType_Unknown = 0, - SType_SenderUDP, - }; - - CSender() : m_skt_type(SType_Unknown) - { - }; - - explicit CSender(eSocketType skt_type_) : m_skt_type(skt_type_) - { - }; - - virtual ~CSender() - { - }; - - virtual bool Create(const SSenderAttr& attr_) = 0; - virtual bool Destroy() = 0; - - virtual size_t Send(const void* buf_, const size_t len_, const char* ipaddr_ = nullptr) = 0; - - inline eSocketType GetType() const {return m_skt_type;}; - - protected: - eSocketType m_skt_type; - }; -} diff --git a/ecal/core/src/io/linux/ecal_socket_option_linux.h b/ecal/core/src/io/linux/ecal_socket_option_linux.h deleted file mode 100644 index f4cdafd..0000000 --- a/ecal/core/src/io/linux/ecal_socket_option_linux.h +++ /dev/null @@ -1,85 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 - Teknique Limited - * - * 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 -#include -#include - - -namespace eCAL -{ - inline static std::vector get_interface_index_list() - { - std::vector interface_index_list; - ifaddrs* ifa = nullptr; - ifaddrs* ifap = nullptr; - - // get a list of network interfaces - getifaddrs(&ifap); - - // create a list of network interfaces indexes - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_PACKET) - { - int index = if_nametoindex(ifa->ifa_name); - if (index) - { - interface_index_list.push_back(index); - } - } - } - - freeifaddrs(ifap); - - return interface_index_list; - } - - inline static bool set_socket_mcast_group_option(int socket, const char* ipaddr_, int option) - { - // set the multicast socket option on all interfaces - for (int iface : get_interface_index_list()) - { - group_req group_req = {}; - sockaddr_in *group = nullptr; - - memset(&group_req, 0, sizeof(group_req)); - group_req.gr_interface = iface; - group = reinterpret_cast(&group_req.gr_group); - group->sin_family = AF_INET; - group->sin_addr.s_addr = inet_addr(ipaddr_); - group->sin_port = 0; - - int rc = setsockopt(socket, IPPROTO_IP, option, &group_req, sizeof(group_source_req)); - if (rc != 0) - { - std::cerr << "setsockopt failed. Unable to set multicast group option: " << strerror(errno) << std::endl; - return(false); - } - } - - return(true); - } -} diff --git a/ecal/core/src/io/rcv_sample.cpp b/ecal/core/src/io/rcv_sample.cpp deleted file mode 100644 index 96d9075..0000000 --- a/ecal/core/src/io/rcv_sample.cpp +++ /dev/null @@ -1,460 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Receiver thread for ecal samples -**/ - -#include - -#include - -#include "ecal_def.h" -#include "rcv_sample.h" - - -CReceiveSlot::CReceiveSlot() - : m_timeout(0.0) - , m_recv_mode(rcm_waiting) - , m_message_id(0) - , m_message_total_num(0) - , m_message_total_len(0) - , m_message_curr_num(0) - , m_message_curr_len(0) -{ -} - -CReceiveSlot::~CReceiveSlot() -{ -} - -int CReceiveSlot::ApplyMessage(const struct SUDPMessage& ecal_message_) -{ - // reset timeout - m_timeout = std::chrono::duration(0.0); - - // process current packet - switch(ecal_message_.header.type) - { - // new message started - case msg_type_header: - OnMessageStart(ecal_message_); - break; - // message data package - case msg_type_content: - if(m_recv_mode == rcm_reading) - { - OnMessageData(ecal_message_); - } - break; - } - - // we have a complete message in the receive buffer - if(m_recv_mode == rcm_completed) - { - // call complete event - OnMessageCompleted(std::move(m_recv_buffer)); - } - - return(0); -} - -int CReceiveSlot::OnMessageStart(const struct SUDPMessage& ecal_message_) -{ - // store header info - m_message_id = ecal_message_.header.id; - m_message_total_num = ecal_message_.header.num; - m_message_total_len = ecal_message_.header.len; - - // reset current message states - m_message_curr_num = 0; - m_message_curr_len = 0; - - // prepare receive buffer - m_recv_buffer.clear(); - m_recv_buffer.reserve(static_cast(m_message_total_len)); - - // switch to reading mode - m_recv_mode = rcm_reading; - - return(0); -} - -int CReceiveSlot::OnMessageData(const struct SUDPMessage& ecal_message_) -{ - // check message id - if(ecal_message_.header.id != m_message_id) - { -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET ID " + std::to_string(ecal_message_.header.id)); -#endif - m_recv_mode = rcm_aborted; - return(-1); - } - - // check current packet counter - if(ecal_message_.header.num != m_message_curr_num) - { -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET NUMBER " + std::to_string(ecal_message_.header.num) + " / " + std::to_string(m_message_curr_num)); -#endif - m_recv_mode = rcm_aborted; - return(-1); - } - - // check current packet length - if(ecal_message_.header.len <= 0) - { -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET LENGTH " + std::to_string(ecal_message_.header.len)); -#endif - m_recv_mode = rcm_aborted; - return(-1); - } - - // copy the message part to the receive message buffer - //std::copy(ecal_message_.payload, ecal_message_.payload + ecal_message_.header.len, std::back_inserter(m_recv_buffer)); - m_recv_buffer.resize(m_recv_buffer.size() + static_cast(ecal_message_.header.len)); - memcpy(m_recv_buffer.data() + m_recv_buffer.size() - static_cast(ecal_message_.header.len), ecal_message_.payload, static_cast(ecal_message_.header.len)); - - // increase packet counter - m_message_curr_num++; - - // increase current length - m_message_curr_len += ecal_message_.header.len; - - // last message packet ? -> switch to completed mode - if(m_message_curr_num == m_message_total_num) m_recv_mode = rcm_completed; - - return(0); -} - - -CSampleReceiver::CSampleReceiveSlot::CSampleReceiveSlot(CSampleReceiver* sample_receiver_) : - m_sample_receiver(sample_receiver_) -{ -} - -CSampleReceiver::CSampleReceiveSlot::~CSampleReceiveSlot() -{ -} - -int CSampleReceiver::CSampleReceiveSlot::OnMessageCompleted(std::vector &&msg_buffer_) -{ - if(!m_sample_receiver) return(0); - - // read sample_name size - unsigned short sample_name_size = ((unsigned short*)(msg_buffer_.data()))[0]; - // read sample_name - std::string sample_name(msg_buffer_.data() + sizeof(sample_name_size)); - - if(m_sample_receiver->HasSample(sample_name)) - { - // read sample - if(!m_ecal_sample.ParseFromArray(msg_buffer_.data() + sizeof(sample_name_size) + sample_name_size, static_cast(msg_buffer_.size() - (sizeof(sample_name_size) + sample_name_size)))) return(0); -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, sample_name + "::UDP Sample Completed"); - - // log it - switch(m_ecal_sample.cmd_type()) - { - case eCAL::pb::bct_none: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - NONE"); - break; - case eCAL::pb::bct_set_sample: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - SAMPLE"); - break; - case eCAL::pb::bct_reg_publisher: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PUBLISHER"); - break; - case eCAL::pb::bct_reg_subscriber: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SUBSCRIBER"); - break; - case eCAL::pb::bct_reg_process: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PROCESS"); - break; - case eCAL::pb::bct_reg_service: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SERVER"); - break; - case eCAL::pb::bct_reg_client: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER CLIENT"); - break; - default: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - UNKNOWN"); - break; - } -#endif - // get layer if this is a payload sample - eCAL::pb::eTLayerType layer = eCAL::pb::eTLayerType::tl_none; - if (m_ecal_sample.cmd_type() == eCAL::pb::eCmdType::bct_set_sample) - { - if (m_ecal_sample.topic().tlayer_size() > 0) - { - layer = m_ecal_sample.topic().tlayer(0).type(); - } - } - // apply sample - m_sample_receiver->ApplySample(m_ecal_sample, layer); - } - - return(0); -} - -CSampleReceiver::CSampleReceiver() -{ - m_msg_buffer.resize(MSG_BUFFER_SIZE); - m_cleanup_start = std::chrono::steady_clock::now(); -} - -CSampleReceiver::~CSampleReceiver() -{ -} - -int CSampleReceiver::Receive(eCAL::CUDPReceiver* sample_receiver_) -{ - if(!sample_receiver_) return(-1); - - // wait for any incoming message - size_t recv_len = sample_receiver_->Receive(m_msg_buffer.data(), m_msg_buffer.size(), 10); - if(recv_len > 0) - { - return(Process(m_msg_buffer.data(), recv_len)); - } - - return(0); -} - -int CSampleReceiver::Process(const char* sample_buffer_, size_t sample_buffer_len_) -{ - // we need at least the header information to start - if (sample_buffer_len_ < sizeof(SUDPMessageHead)) return(0); - - // cast buffer to udp message struct - struct SUDPMessage* ecal_message = (struct SUDPMessage*)sample_buffer_; - - // check for eCAL 4.x header - if ((ecal_message->header.head[0] == 'e') - && (ecal_message->header.head[1] == 'C') - && (ecal_message->header.head[2] == 'A') - && (ecal_message->header.head[3] == 'L') - ) - { - eCAL::Logging::Log(log_level_warning, "Received eCAL 4 traffic"); - return(0); - } - - // check for valid header - else if ( (ecal_message->header.head[0] != 'E') - || (ecal_message->header.head[1] != 'C') - || (ecal_message->header.head[2] != 'A') - || (ecal_message->header.head[3] != 'L') - ) - { - eCAL::Logging::Log(log_level_warning, "Received invalid traffic (eCAL Header missing)"); - return(0); - } - - // check integrity - switch (ecal_message->header.type) - { - case msg_type_header: - break; - case msg_type_content: - case msg_type_header_with_content: - if (sample_buffer_len_ < sizeof(SUDPMessageHead) + static_cast(ecal_message->header.len)) - return(0); - break; - default: - return(0); - } - -#ifndef NDEBUG - // log it - switch (ecal_message->header.type) - { - case msg_type_header_with_content: - eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER_WITH_CONTENT"); - break; - case msg_type_header: - eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER"); - break; - case msg_type_content: - eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - CONTENT"); - break; - } -#endif - - switch (ecal_message->header.type) - { - case msg_type_header_with_content: - { - // read sample_name size - unsigned short sample_name_size = 0; - memcpy(&sample_name_size, ecal_message->payload, 2); - // read sample_name - std::string sample_name = ecal_message->payload + sizeof(sample_name_size); - - if (HasSample(sample_name)) - { - // read sample - if (!m_ecal_sample.ParseFromArray(ecal_message->payload + static_cast(sizeof(sample_name_size) + sample_name_size), static_cast(static_cast(ecal_message->header.len) - (sizeof(sample_name_size) + sample_name_size)))) return(0); - -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug3, sample_name + "::UDP Sample Completed"); - - // log it - switch (m_ecal_sample.cmd_type()) - { - case eCAL::pb::bct_none: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - NONE"); - break; - case eCAL::pb::bct_set_sample: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - SAMPLE"); - break; - case eCAL::pb::bct_reg_publisher: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PUBLISHER"); - break; - case eCAL::pb::bct_reg_subscriber: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SUBSCRIBER"); - break; - case eCAL::pb::bct_reg_process: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER PROCESS"); - break; - case eCAL::pb::bct_reg_service: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER SERVICE"); - break; - case eCAL::pb::bct_reg_client: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - REGISTER CLIENT"); - break; - default: - eCAL::Logging::Log(log_level_debug4, sample_name + "::UDP Sample Command Type - UNKNOWN"); - break; - } -#endif - // get layer if this is a payload sample - eCAL::pb::eTLayerType layer = eCAL::pb::eTLayerType::tl_none; - if (m_ecal_sample.cmd_type() == eCAL::pb::eCmdType::bct_set_sample) - { - if (m_ecal_sample.topic().tlayer_size() > 0) - { - layer = m_ecal_sample.topic().tlayer(0).type(); - } - } - // apply sample - ApplySample(m_ecal_sample, layer); - } - } - break; - // if we have a header only package - // we create a receive slot and apply it to the receive slot map - // to process the following data packages - // we do not know the name here unfortunately - // so we have to wait for the first payload package :-( - case msg_type_header: - { - // create new receive slot - std::shared_ptr receive_slot(nullptr); - receive_slot = std::make_shared(CSampleReceiveSlot(this)); - m_receive_slot_map[ecal_message->header.id] = receive_slot; - // apply message - receive_slot->ApplyMessage(*ecal_message); - } - break; - // if we have a payload package - // we check for an existing receive slot and apply the data to it - case msg_type_content: - { - // first data package ? - if (ecal_message->header.num == 0) - { - // read sample_name size - unsigned short sample_name_size = 0; - memcpy(&sample_name_size, ecal_message->payload, 2); - // read sample_name - std::string sample_name = ecal_message->payload + sizeof(sample_name_size); - - // remove the matching slot if we are not interested in this sample - if (!HasSample(sample_name)) - { - auto riter = m_receive_slot_map.find(ecal_message->header.id); - if (riter != m_receive_slot_map.end()) - { -#ifndef NDEBUG - // log timeouted slot - eCAL::Logging::Log(log_level_debug3, "CSampleReceiver::Receive - DISCARD PACKAGE FOR TOPIC: " + sample_name); -#endif - m_receive_slot_map.erase(riter); - break; - } - } - } - - // process data package - auto iter = m_receive_slot_map.find(ecal_message->header.id); - if (iter != m_receive_slot_map.end()) - { - // apply message - iter->second->ApplyMessage(*ecal_message); - } - } - break; - default: - break; - } - - // cleanup finished or zombie received slots - auto diff_time = std::chrono::steady_clock::now() - m_cleanup_start; - std::chrono::duration step_time = std::chrono::milliseconds(NET_UDP_RECBUFFER_CLEANUP); - if (diff_time > step_time) - { - m_cleanup_start = std::chrono::steady_clock::now(); - - for (ReceiveSlotMapT::iterator riter = m_receive_slot_map.begin(); riter != m_receive_slot_map.end();) - { - bool finished = riter->second->HasFinished(); - bool timeouted = riter->second->HasTimedOut(step_time); - if (finished || timeouted) - { -#ifndef NDEBUG - int32_t total_len = riter->second->GetMessageTotalLength(); - int32_t current_len = riter->second->GetMessageCurrentLength(); -#endif - riter = m_receive_slot_map.erase(riter); -#ifndef NDEBUG - // log timeouted slot - if (timeouted) - { - eCAL::Logging::Log(log_level_debug3, "CSampleReceiver::Receive - TIMEOUT (TotalLength / CurrentLength): " + std::to_string(total_len) + " / " + std::to_string(current_len)); - } -#endif - } - else - { - ++riter; - } - } - } - - return(static_cast(sample_buffer_len_)); -} diff --git a/ecal/core/src/io/rcv_sample.h b/ecal/core/src/io/rcv_sample.h deleted file mode 100644 index 300723a..0000000 --- a/ecal/core/src/io/rcv_sample.h +++ /dev/null @@ -1,116 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Receiver thread for ecal samples -**/ - -#pragma once - -#include "ecal_def.h" -#include "udp_receiver.h" -#include "ecal_thread.h" -#include "msg_type.h" - -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -class CReceiveSlot -{ -public: - CReceiveSlot(); - virtual ~CReceiveSlot(); - - int ApplyMessage(const struct SUDPMessage& ecal_message_); - bool HasFinished() {return((m_recv_mode == rcm_aborted) || (m_recv_mode == rcm_completed));}; - bool HasTimedOut(const std::chrono::duration& diff_time_) {m_timeout += diff_time_; return(m_timeout >= std::chrono::milliseconds(NET_UDP_RECBUFFER_TIMEOUT));}; - int32_t GetMessageTotalLength() {return(m_message_total_len);}; - int32_t GetMessageCurrentLength() {return(m_message_curr_len);}; - - virtual int OnMessageCompleted(std::vector &&msg_buffer_) = 0; - -protected: - int OnMessageStart(const struct SUDPMessage& ecal_message_); - int OnMessageData(const struct SUDPMessage& ecal_message_); - - enum eReceiveMode - { - rcm_waiting = 1, - rcm_reading, - rcm_aborted, - rcm_completed - }; - - std::chrono::duration m_timeout; - std::vector m_recv_buffer; - eReceiveMode m_recv_mode; - - int32_t m_message_id; - int32_t m_message_total_num; - int32_t m_message_total_len; - - int32_t m_message_curr_num; - int32_t m_message_curr_len; - - eCAL::pb::Sample m_ecal_sample; -}; - - -class CSampleReceiver -{ - class CSampleReceiveSlot : public CReceiveSlot - { - public: - explicit CSampleReceiveSlot(CSampleReceiver* sample_receiver_); - virtual ~CSampleReceiveSlot(); - - virtual int OnMessageCompleted(std::vector &&msg_buffer_); - - protected: - CSampleReceiver* m_sample_receiver; - }; - -public: - CSampleReceiver(); - virtual ~CSampleReceiver(); - - virtual bool HasSample(const std::string& sample_name_) = 0; - virtual size_t ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_) = 0; - - int Receive(eCAL::CUDPReceiver* sample_receiver_); - int Process(const char* sample_buffer_, size_t sample_buffer_len_); - -protected: - typedef std::unordered_map> ReceiveSlotMapT; - ReceiveSlotMapT m_receive_slot_map; - std::vector m_msg_buffer; - eCAL::pb::Sample m_ecal_sample; - - std::chrono::steady_clock::time_point m_cleanup_start; -}; diff --git a/ecal/core/src/io/snd_raw_buffer.cpp b/ecal/core/src/io/snd_raw_buffer.cpp deleted file mode 100644 index 103c7dd..0000000 --- a/ecal/core/src/io/snd_raw_buffer.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief raw message buffer handling -**/ - -#include - -#include "ecal_process.h" - -#include "snd_raw_buffer.h" -#include "io/msg_type.h" - -namespace -{ - // random number generator - unsigned long xorshf96(unsigned long& x, unsigned long& y, unsigned long& z) // period 2^96-1 - { - unsigned long t; - x ^= x << 16; - x ^= x >> 5; - x ^= x << 1; - - t = x; - x = y; - y = z; - z = t ^ x ^ y; - - return z; - } -} - -namespace eCAL -{ - size_t CreateSampleBuffer(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, std::vector& payload_) - { - unsigned short sample_name_size = (unsigned short)sample_name_.size() + 1; -#if GOOGLE_PROTOBUF_VERSION >= 3001000 - size_t sample_size = ecal_sample_.ByteSizeLong(); -#else - size_t sample_size = ecal_sample_.ByteSize(); -#endif - size_t data_size = sizeof(sample_name_size) + sample_name_size + sample_size; - - // create payload buffer with reserved space for first message head - payload_.resize(data_size + sizeof(struct SUDPMessageHead)); - char* payload_data = payload_.data() + sizeof(struct SUDPMessageHead); - - // write topic name size - ((unsigned short*)payload_data)[0] = sample_name_size; - // write topic name - memcpy(payload_data + sizeof(sample_name_size), sample_name_.c_str(), sample_name_size); - - // write payload - if (ecal_sample_.SerializeWithCachedSizesToArray((google::protobuf::uint8*)payload_data + sizeof(sample_name_size) + sample_name_size)) - { - return data_size; - } - - return (0); - } - - size_t SendSampleBuffer(char* buf_, size_t buf_len_, long bandwidth_, TransmitCallbackT transmit_cb_) - { - if (!buf_) return(0); - - size_t sent(0); - size_t sent_sum(0); - - int32_t total_packet_num = int32_t(buf_len_ / MSG_PAYLOAD_SIZE); - if (buf_len_%MSG_PAYLOAD_SIZE) total_packet_num++; - - // create message header - struct SUDPMessageHead msg_header; - - switch (total_packet_num) - { - case 1: - { - // create start packet - msg_header.type = msg_type_header_with_content; - msg_header.id = -1; // not needed for combined header / data message - msg_header.num = 1; - msg_header.len = int32_t(buf_len_); - - // copy msg_header in send buffer - memcpy(buf_, &msg_header, sizeof(struct SUDPMessageHead)); - - // send single header + data package - sent = transmit_cb_(buf_, sizeof(struct SUDPMessageHead) + buf_len_); - if (sent == 0) return(sent); - sent_sum += sent; - -#ifndef NDEBUG - // log it - //std::cout << "SendRawBuffer Packet Sent - HEADER_WITH_CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; -#endif - } - break; - default: - { - // calculate bandwidth timing parameter - long long send_sleep_us(0); - if (bandwidth_ > 0) - { - send_sleep_us = MSG_BUFFER_SIZE; - send_sleep_us *= 1000 * 1000; - send_sleep_us /= bandwidth_; - } - - // create start package - msg_header.type = msg_type_header; - { - // create random number for message id - static unsigned long x = 123456789, y = 362436069, z = 521288629; - msg_header.id = xorshf96(x, y, z); - } - msg_header.num = total_packet_num; - msg_header.len = int32_t(buf_len_); - - // send start package - sent = transmit_cb_(&msg_header, sizeof(struct SUDPMessageHead)); - if (sent == 0) return(sent); - sent_sum += sent; - -#ifndef NDEBUG - // log it - //std::cout << "SendRawBuffer Packet Sent - HEADER (" + std::to_string(sent) + " Bytes)" << std::endl; -#endif - - // send data packages - msg_header.type = msg_type_content; - for (int32_t current_packet_num = 0; current_packet_num < total_packet_num; current_packet_num++) - { - // calculate current payload - size_t current_snd_len = buf_len_; - if (current_snd_len > MSG_PAYLOAD_SIZE) current_snd_len = MSG_PAYLOAD_SIZE; - - if (current_snd_len > 0) - { - // reduce total send len - buf_len_ -= current_snd_len; - - // create data packet numbering - msg_header.num = current_packet_num; - msg_header.len = int32_t(current_snd_len); - - // copy msg_header in send buffer - memcpy(buf_ + static_cast(current_packet_num)*MSG_PAYLOAD_SIZE, &msg_header, sizeof(struct SUDPMessageHead)); - - // send data package - sent = transmit_cb_(buf_ + static_cast(current_packet_num)*MSG_PAYLOAD_SIZE, sizeof(struct SUDPMessageHead) + current_snd_len); - if (sent == 0) return(sent); - if (send_sleep_us) - eCAL::Process::SleepFor(std::chrono::microseconds(send_sleep_us)); - -#ifndef NDEBUG - // log it - //std::cout << "SendRawBuffer Packet Sent - CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; -#endif - sent_sum += sent; - } - } - } - break; - } - - return(sent_sum); - } -} diff --git a/ecal/core/src/io/snd_sample.cpp b/ecal/core/src/io/snd_sample.cpp deleted file mode 100644 index 7cc6b65..0000000 --- a/ecal/core/src/io/snd_sample.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Sender thread for ecal samples -**/ - -#include - -#include "ecal_def.h" -#include "snd_sample.h" -#include "snd_raw_buffer.h" - -namespace -{ - size_t TransmitToUDP(const void* buf_, const size_t len_, eCAL::CUDPSender* sample_sender_, const std::string& mcast_address_) - { - return (sample_sender_->Send(buf_, len_, mcast_address_.c_str())); - } -} - -namespace eCAL -{ - size_t SendSample(eCAL::CUDPSender* udp_sender_, const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, const std::string& ipaddr_, long bandwidth_) - { - if (udp_sender_ == nullptr) return(0); - - // return value - size_t sent_sum(0); - - std::vector payload_sum; - size_t data_size = CreateSampleBuffer(sample_name_, ecal_sample_, payload_sum); - if (data_size > 0) - { - // and send it - sent_sum = SendSampleBuffer(payload_sum.data(), data_size, bandwidth_, std::bind(TransmitToUDP, std::placeholders::_1, std::placeholders::_2, udp_sender_, ipaddr_)); - -#ifndef NDEBUG - // log it - eCAL::Logging::Log(log_level_debug4, "UDP Sample Buffer Sent (" + std::to_string(sent_sum) + " Bytes)"); -#endif - } - - // return bytes sent - return(sent_sum); - } -} diff --git a/ecal/core/src/io/udp_configurations.cpp b/ecal/core/src/io/udp_configurations.cpp deleted file mode 100644 index d9e327a..0000000 --- a/ecal/core/src/io/udp_configurations.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "io/udp_configurations.h" - -#include -#include "topic2mcast.h" - -const std::string localhost_udp_address{ "127.255.255.255" }; - - -std::string eCAL::UDP::GetRegistrationMulticastAddress() -{ - bool local_only = !Config::IsNetworkEnabled(); - if (local_only) - { - return localhost_udp_address; - } - else - { - // both in v1 and v2, the mulicast group is returned as the adress for the registration layer - return Config::GetUdpMulticastGroup(); - } -} - -std::string eCAL::UDP::GetLoggingMulticastAddress() -{ - //TODO: At the moment, both logging and monitoring addresses seem to be the same - // Should it be kept or changed? - return GetRegistrationMulticastAddress(); -} - -std::string eCAL::UDP::GetTopicMulticastAddress(const std::string& topic_name) -{ - if (Config::GetUdpMulticastConfigVersion() == Config::UdpConfigVersion::V1) - return UDP::V1::topic2mcast(topic_name, Config::GetUdpMulticastGroup(), Config::GetUdpMulticastMask()); - // v2 - return UDP::V2::topic2mcast(topic_name, Config::GetUdpMulticastGroup(), Config::GetUdpMulticastMask()); -} diff --git a/ecal/core/src/io/udp_init.cpp b/ecal/core/src/io/udp_init.cpp deleted file mode 100644 index 449313e..0000000 --- a/ecal/core/src/io/udp_init.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief UDP initialization -**/ - -#include - -#include -#include - -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_socket.h" -#endif /* ECAL_OS_WINDOWS */ - -static std::atomic g_socket_init_refcnt(0); - -namespace eCAL -{ - namespace Net - { - int Initialize() - { - g_socket_init_refcnt++; - if(g_socket_init_refcnt == 1) - { -#ifdef ECAL_OS_WINDOWS - WORD wVersionRequested = MAKEWORD(2, 2); - - WSADATA wsaData; - int err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) - { - /* Tell the user that we could not find a usable */ - /* Winsock DLL. */ - printf("WSAStartup failed with error: %d\n", err); - return(-1); - } - - /* Confirm that the WinSock DLL supports 2.2.*/ - /* Note that if the DLL supports versions greater */ - /* than 2.2 in addition to 2.2, it will still return */ - /* 2.2 in wVersion since that is the version we */ - /* requested. */ - - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) - { - /* Tell the user that we could not find a usable */ - /* WinSock DLL. */ - printf("Could not find a usable version of Winsock.dll\n"); - WSACleanup(); - } -#endif /* ECAL_OS_WINDOWS */ - } - return(0); - } - - int Finalize() - { - if(g_socket_init_refcnt == 0) return(0); - - g_socket_init_refcnt--; - if(g_socket_init_refcnt == 0) - { -#ifdef ECAL_OS_WINDOWS - WSACleanup(); -#endif /* ECAL_OS_WINDOWS */ - } - - return(0); - } - } -} diff --git a/ecal/core/src/io/udp_receiver.cpp b/ecal/core/src/io/udp_receiver.cpp deleted file mode 100644 index d7e6925..0000000 --- a/ecal/core/src/io/udp_receiver.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief UDP receiver class -**/ - -#include - -#include "udp_receiver.h" - -#include - -#include "io/udp_receiver_base.h" -#include "io/udp_receiver_asio.h" -#ifdef ECAL_NPCAP_SUPPORT -#include "io/udp_receiver_npcap.h" -#endif - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // udp receiver class - //////////////////////////////////////////////////////// - CUDPReceiver::CUDPReceiver() - : CReceiver(CReceiver::SType_ReceiverUDP) - , m_use_npcap(false) - { -#ifdef ECAL_NPCAP_SUPPORT - if (Config::IsNpcapEnabled()) - { - m_use_npcap = Udpcap::Initialize(); // Only use NPCAP if we can initialize it (or it has already been initialized successfully) - if (!m_use_npcap) - { - std::cerr << "Npcap is enabled, but cannot be initialized. Using socket fallback mode." << std::endl; - } - } -#endif //ECAL_NPCAP_SUPPORT - } - - bool CUDPReceiver::Create(const SReceiverAttr& attr_) - { - if (m_socket_impl) return false; - -#ifdef ECAL_NPCAP_SUPPORT - if (m_use_npcap) - { - m_socket_impl = std::make_shared(attr_); - return true; - } -#endif // ECAL_NPCAP_SUPPORT - - m_socket_impl = std::make_shared(attr_); - return(true); - } - - bool CUDPReceiver::Destroy() - { - if (!m_socket_impl) return(false); - m_socket_impl.reset(); - return(true); - } - - bool CUDPReceiver::AddMultiCastGroup(const char* ipaddr_) - { - if (!m_socket_impl) return(false); - return(m_socket_impl->AddMultiCastGroup(ipaddr_)); - } - - bool CUDPReceiver::RemMultiCastGroup(const char* ipaddr_) - { - if (!m_socket_impl) return(false); - return(m_socket_impl->RemMultiCastGroup(ipaddr_)); - } - - size_t CUDPReceiver::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) - { - if (!m_socket_impl) return(0); - return(m_socket_impl->Receive(buf_, len_, timeout_, address_)); - } -} diff --git a/ecal/core/src/io/udp_receiver_asio.cpp b/ecal/core/src/io/udp_receiver_asio.cpp deleted file mode 100644 index a77f0e2..0000000 --- a/ecal/core/src/io/udp_receiver_asio.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - - -#include - -#ifdef __linux__ -#include "linux/ecal_socket_option_linux.h" -#endif - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // Default ASIO based receiver class implementation - //////////////////////////////////////////////////////// - CUDPReceiverAsio::CUDPReceiverAsio(const SReceiverAttr& attr_) : - CUDPReceiverBase(attr_), - m_created(false), - m_broadcast(attr_.broadcast), - m_unicast(attr_.unicast), - m_socket(m_iocontext) - { - if (m_broadcast && m_unicast) - { - std::cerr << "CUDPReceiverAsio: Setting broadcast and unicast option true is not allowed." << std::endl; - return; - } - - // create socket - asio::ip::udp::endpoint listen_endpoint(asio::ip::udp::v4(), static_cast(attr_.port)); - { - asio::error_code ec; - m_socket.open(listen_endpoint.protocol(), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to open socket: " << ec.message() << std::endl; - return; - } - } - - // set socket reuse - { - asio::error_code ec; - m_socket.set_option(asio::ip::udp::socket::reuse_address(true), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to set reuse-address option: " << ec.message() << std::endl; - } - } - - // bind socket - { - asio::error_code ec; - m_socket.bind(listen_endpoint, ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to bind socket to " << listen_endpoint.address().to_string() << ":" << listen_endpoint.port() << ": " << ec.message() << std::endl; - return; - } - } - - if (!m_unicast) - { - // set loopback option - asio::ip::multicast::enable_loopback loopback(attr_.loopback); - asio::error_code ec; - m_socket.set_option(loopback, ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to enable loopback: " << ec.message() << std::endl; - } - } - - // set receive buffer size (default = 1 MB) - { - int rcvbuf = 1024 * 1024; - if (attr_.rcvbuf > 0) rcvbuf = attr_.rcvbuf; - asio::socket_base::receive_buffer_size recbufsize(rcvbuf); - asio::error_code ec; - m_socket.set_option(recbufsize, ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to set receive buffer size: " << ec.message() << std::endl; - } - } - - // join multicast group - AddMultiCastGroup(attr_.ipaddr.c_str()); - - // state successful creation - m_created = true; - } - - bool CUDPReceiverAsio::AddMultiCastGroup(const char* ipaddr_) - { - if (!m_broadcast && !m_unicast) - { - // join multicast group -#ifdef __linux__ - if (eCAL::Config::IsUdpMulticastJoinAllIfEnabled()) - { - if (!set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_JOIN_GROUP)) - { - return(false); - } - } - else -#endif - { - asio::error_code ec; - m_socket.set_option(asio::ip::multicast::join_group(asio::ip::make_address(ipaddr_)), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to join multicast group: " << ec.message() << std::endl; - return(false); - } - } - -#ifdef ECAL_JOIN_MULTICAST_TWICE - // this is a very bad workaround because of an identified bug on a specific embedded device - // we join the multicast group multiple times otherwise the socket will not receive any data - std::cerr << "eCAL was compiled with ECAL_JOIN_MULTICAST_TWICE" << std::endl; - m_socket.set_option(asio::ip::multicast::leave_group(asio::ip::make_address(ipaddr_))); - m_socket.set_option(asio::ip::multicast::join_group(asio::ip::make_address(ipaddr_))); -#endif // ECAL_JOIN_MULTICAST_TWICE - } - return(true); - } - - bool CUDPReceiverAsio::RemMultiCastGroup(const char* ipaddr_) - { - if (!m_broadcast && !m_unicast) - { - // Leave multicast group -#ifdef __linux__ - if (eCAL::Config::IsUdpMulticastJoinAllIfEnabled()) - { - if (!set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_LEAVE_GROUP)) - { - return(false); - } - } - else -#endif - { - asio::error_code ec; - m_socket.set_option(asio::ip::multicast::leave_group(asio::ip::make_address(ipaddr_)), ec); - if (ec) - { - std::cerr << "CUDPReceiverAsio: Unable to leave multicast group: " << ec.message() << std::endl; - return(false); - } - } - } - return(true); - } - - size_t CUDPReceiverAsio::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) - { - if (!m_created) return 0; - - size_t reclen(0); - m_socket.async_receive_from(asio::buffer(buf_, len_), m_sender_endpoint, - [&reclen](std::error_code ec, std::size_t length) - { - if (!ec) - { - reclen = length; - } - }); - - // run for timeout ms - RunIOContext(asio::chrono::milliseconds(timeout_)); - - // retrieve underlaying raw socket informations - if (address_) - { - if (m_sender_endpoint.address().is_v4()) - { - asio::detail::sockaddr_in4_type* in4 = reinterpret_cast(m_sender_endpoint.data()); - address_->sin_addr = in4->sin_addr; - address_->sin_family = in4->sin_family; - address_->sin_port = in4->sin_port; - memset(&(address_->sin_zero), 0, 8); - } - else - { - std::cout << "CUDPReceiverAsio: ipv4 address conversion failed." << std::endl; - } - } - - return (reclen); - } - - void CUDPReceiverAsio::RunIOContext(const asio::chrono::steady_clock::duration& timeout) - { - // restart the io_context, as it may have been left in the "stopped" state by a previous operation - m_iocontext.restart(); - - // block until the asynchronous operation has completed, or timed out - m_iocontext.run_for(timeout); - - // stop the context if even the operation was not successful completed - if (!m_iocontext.stopped()) - { - // cancel the outstanding asynchronous operation - m_socket.cancel(); - - // run the io_context again until the operation completes - m_iocontext.run(); - } - } -} diff --git a/ecal/core/src/io/udp_receiver_base.h b/ecal/core/src/io/udp_receiver_base.h deleted file mode 100644 index 1e3a665..0000000 --- a/ecal/core/src/io/udp_receiver_base.h +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#include - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // udp receiver class implementation - //////////////////////////////////////////////////////// - class CUDPReceiverBase - { - public: - CUDPReceiverBase(const SReceiverAttr& /*attr_*/) {}; - // We don't technically need a virtual destructor, if we are working with shared_ptrs... - virtual ~CUDPReceiverBase() = default; - // Delete copy / move operations to prevent slicing - CUDPReceiverBase(CUDPReceiverBase&&) = delete; - CUDPReceiverBase& operator=(CUDPReceiverBase&&) = delete; - CUDPReceiverBase(const CUDPReceiverBase&) = delete; - CUDPReceiverBase& operator=(const CUDPReceiverBase&) = delete; - - virtual bool AddMultiCastGroup(const char* ipaddr_) = 0; - virtual bool RemMultiCastGroup(const char* ipaddr_) = 0; - - virtual size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) = 0; - }; -} \ No newline at end of file diff --git a/ecal/core/src/io/udp_receiver_npcap.cpp b/ecal/core/src/io/udp_receiver_npcap.cpp deleted file mode 100644 index b750e9b..0000000 --- a/ecal/core/src/io/udp_receiver_npcap.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ -#include - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // Npcap based receiver class implementation - //////////////////////////////////////////////////////// - CUDPReceiverPcap::CUDPReceiverPcap(const SReceiverAttr& attr_) - : CUDPReceiverBase(attr_) - , m_created(false) - , m_unicast(attr_.unicast) - { - // set receive buffer size (default = 1 MB) - int rcvbuf = 1024 * 1024; - if (attr_.rcvbuf > 0) - { - rcvbuf = attr_.rcvbuf; - } - if (!m_socket.setReceiveBufferSize(rcvbuf)) - { - std::cerr << "CUDPReceiverPcap: Unable to set receive buffer size." << std::endl; - } - - // bind socket - if (!m_socket.bind(Udpcap::HostAddress::Any(), static_cast(attr_.port))) - { - std::cerr << "CUDPReceiverPcap: Unable to bind socket." << std::endl; - return; - } - - if (!m_unicast) - { - // set loopback option - m_socket.setMulticastLoopbackEnabled(attr_.loopback); - } - - // join multicast group - AddMultiCastGroup(attr_.ipaddr.c_str()); - - // state successful creation - m_created = true; - } - - bool CUDPReceiverPcap::AddMultiCastGroup(const char* ipaddr_) - { - if (!m_unicast) - { - // join multicast group - if (!m_socket.joinMulticastGroup(Udpcap::HostAddress(ipaddr_))) - { - std::cerr << "CUDPReceiverPcap: Unable to join multicast group." << std::endl; - return(false); - } - } - return(true); - } - - bool CUDPReceiverPcap::RemMultiCastGroup(const char* ipaddr_) - { - if (!m_unicast) - { - // leave multicast group - if (!m_socket.leaveMulticastGroup(Udpcap::HostAddress(ipaddr_))) - { - std::cerr << "CUDPReceiverPcap: Unable to leave multicast group." << std::endl; - return(false); - } - } - return(true); - } - - size_t CUDPReceiverPcap::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) - { - if (!m_created) return 0; - - size_t bytes_received; - if (address_) - { - Udpcap::HostAddress source_address; - uint16_t source_port; - bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_), &source_address, &source_port); - - if (bytes_received && source_address.isValid()) - { - address_->sin_addr.s_addr = source_address.toInt(); - address_->sin_family = AF_INET; - address_->sin_port = source_port; - memset(&(address_->sin_zero), 0, 8); - } - } - else - { - bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_)); - } - return bytes_received; - } -} diff --git a/ecal/core/src/io/udp_sender.cpp b/ecal/core/src/io/udp_sender.cpp deleted file mode 100644 index e1a53be..0000000 --- a/ecal/core/src/io/udp_sender.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief UDP sender class -**/ - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4834) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "udp_sender.h" - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // udp sender class implementation - //////////////////////////////////////////////////////// - class CUDPSenderImpl - { - public: - CUDPSenderImpl(const SSenderAttr& attr_); - - size_t Send (const void* buf_, const size_t len_, const char* ipaddr_ = nullptr); - void SendAsync(const void* buf_, const size_t len_, const char* ipaddr_ = nullptr); - - protected: - void SendAsync(asio::const_buffer buf_, const char* ipaddr_); - void OnSendAsync(asio::error_code ec_, std::size_t bytes_transferred_, asio::const_buffer buf_, const char* ipaddr_); - - bool m_broadcast; - bool m_unicast; - asio::io_context m_iocontext; - asio::ip::udp::endpoint m_endpoint; - asio::ip::udp::socket m_socket; - unsigned short m_port; - }; - - CUDPSenderImpl::CUDPSenderImpl(const SSenderAttr& attr_) : - m_broadcast(attr_.broadcast), - m_unicast(attr_.unicast), - m_endpoint(asio::ip::make_address(attr_.ipaddr), static_cast(attr_.port)), - m_socket(m_iocontext, m_endpoint.protocol()), - m_port(static_cast(attr_.port)) - { - if (m_broadcast && m_unicast) - { - std::cerr << "CUDPSender: Setting broadcast and unicast option true is not allowed." << std::endl; - return; - } - - if (m_broadcast || m_unicast) - { - // set unicast packet TTL - asio::ip::unicast::hops ttl(attr_.ttl); - asio::error_code ec; - m_socket.set_option(ttl, ec); - if (ec) - std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; - } - else - { - // set multicast packet TTL - { - asio::ip::multicast::hops ttl(attr_.ttl); - asio::error_code ec; - m_socket.set_option(ttl, ec); - if (ec) - std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; - } - - // set loopback option - { - asio::ip::multicast::enable_loopback loopback(attr_.loopback); - asio::error_code ec; - m_socket.set_option(loopback); - if (ec) - std::cerr << "CUDPSender: Error setting loopback option: " << ec.message() << std::endl; - } - } - - if (m_broadcast) - { - asio::error_code ec; - m_socket.set_option(asio::socket_base::broadcast(true), ec); - if (ec) - std::cerr << "CUDPSender: Setting broadcast mode failed: " << ec.message() << std::endl; - } - } - - size_t CUDPSenderImpl::Send(const void* buf_, const size_t len_, const char* ipaddr_) - { - asio::socket_base::message_flags flags(0); - asio::error_code ec; - size_t sent(0); - if (ipaddr_ && (ipaddr_[0] != '\0')) sent = m_socket.send_to(asio::buffer(buf_, len_), asio::ip::udp::endpoint(asio::ip::make_address(ipaddr_), m_port), flags, ec); - else sent = m_socket.send_to(asio::buffer(buf_, len_), m_endpoint, flags, ec); - if (ec) - { - std::cout << "CUDPSender::Send failed with: \'" << ec.message() << "\'" << std::endl; - return (0); - } - return(sent); - } - - void CUDPSenderImpl::SendAsync(const void* buf_, const size_t len_, const char* ipaddr_) - { - SendAsync(asio::buffer(buf_, len_), ipaddr_); - m_iocontext.run(); - } - - void CUDPSenderImpl::SendAsync(asio::const_buffer buf_, const char* ipaddr_) - { - if (ipaddr_ && (ipaddr_[0] != '\0')) m_socket.async_send_to(buf_, asio::ip::udp::endpoint(asio::ip::make_address(ipaddr_), m_port), std::bind(&CUDPSenderImpl::OnSendAsync, this, std::placeholders::_1, std::placeholders::_2, buf_, ipaddr_)); - else m_socket.async_send_to(buf_, m_endpoint, std::bind(&CUDPSenderImpl::OnSendAsync, this, std::placeholders::_1, std::placeholders::_2, buf_, ipaddr_)); - } - - void CUDPSenderImpl::OnSendAsync(asio::error_code ec_, std::size_t bytes_transferred_, asio::const_buffer buf_, const char* ipaddr_) - { - if (!ec_) - { - // bytes_transferred_ amount of data was successfully sent - auto bytes_left = buf_ + bytes_transferred_; - if (bytes_left.size()) - { - // send "rest data" in the next SendAsync operation - SendAsync(bytes_left, ipaddr_); - } - m_iocontext.stop(); - } - else - { - std::cout << "CUDPSender::OnSend failed with: \'" << ec_.message() << "\'" << std::endl; - } - }; - - //////////////////////////////////////////////////////// - // udp sender class - //////////////////////////////////////////////////////// - CUDPSender::CUDPSender() : CSender(CSender::SType_SenderUDP) {} - - bool CUDPSender::Create(const SSenderAttr& attr_) - { - if (m_socket_impl) return(false); - m_socket_impl = std::make_shared(attr_); - return(true); - } - - bool CUDPSender::Destroy() - { - if (!m_socket_impl) return(false); - m_socket_impl = nullptr; - return(true); - } - - size_t CUDPSender::Send(const void* buf_, const size_t len_, const char* ipaddr_) - { - if (!m_socket_impl) return(0); - return(m_socket_impl->Send(buf_, len_, ipaddr_)); - } - - void CUDPSender::SendAsync(const void* buf_, const size_t len_, const char* ipaddr_) - { - if (!m_socket_impl) return; - m_socket_impl->SendAsync(buf_, len_, ipaddr_); - } -} diff --git a/ecal/core/src/mon/ecal_monitoring_impl.cpp b/ecal/core/src/mon/ecal_monitoring_impl.cpp deleted file mode 100644 index b61352b..0000000 --- a/ecal/core/src/mon/ecal_monitoring_impl.cpp +++ /dev/null @@ -1,819 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Global monitoring class (implementation) -**/ - -#include -#include - -#include - -#include "ecal_config_reader_hlp.h" -#include "ecal_monitoring_impl.h" - -#include "ecal_def.h" - -#include -#include - -#include "../ecal_registration_receiver.h" - -namespace eCAL -{ - //////////////////////////////////////////////////////// - // local helper - //////////////////////////////////////////////////////// - static void GetSampleHost(const eCAL::pb::Sample& ecal_sample_, std::string& host_name_) - { - if (ecal_sample_.has_host()) - { - host_name_ = ecal_sample_.host().hname(); - } - if (ecal_sample_.has_process()) - { - host_name_ = ecal_sample_.process().hname(); - } - if (ecal_sample_.has_service()) - { - host_name_ = ecal_sample_.service().hname(); - } - if (ecal_sample_.has_client()) - { - host_name_ = ecal_sample_.client().hname(); - } - if (ecal_sample_.has_topic()) - { - host_name_ = ecal_sample_.topic().hname(); - } - } - - static bool IsLocalHost(const eCAL::pb::Sample& ecal_sample_) - { - std::string host_name; - GetSampleHost(ecal_sample_, host_name); - if (host_name.empty()) return(false); - if (host_name == Process::GetHostName()) return(true); - return(false); - } - - - //////////////////////////////////////// - // Monitoring Implementation - //////////////////////////////////////// - CMonitoringImpl::CMonitoringImpl() : - m_init(false), - m_network (Config::IsNetworkEnabled()), - m_publisher_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_subscriber_map(std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_process_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_server_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_client_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())) - { - } - - CMonitoringImpl::~CMonitoringImpl() - { - } - - void CMonitoringImpl::Create() - { - if (m_init) return; - - // network mode - m_network = Config::IsNetworkEnabled(); - - // get name of this host - m_host_name = Process::GetHostName(); - - // utilize registration receiver to enrich monitor information - g_registration_receiver()->SetCustomApplySampleCallback([this](const auto& ecal_sample_){ApplySample(ecal_sample_, eCAL::pb::tl_none);}); - - // start logging receive thread - CLoggingReceiveThread::LogMessageCallbackT logmsg_cb = std::bind(&CMonitoringImpl::RegisterLogMessage, this, std::placeholders::_1); - m_log_rcv_threadcaller = std::make_shared(logmsg_cb); - m_log_rcv_threadcaller->SetNetworkMode(Config::IsNetworkEnabled()); - - // start monitoring and logging publishing thread - CMonLogPublishingThread::MonitoringCallbackT mon_cb = std::bind(&CMonitoringImpl::GetMonitoringMsg, this, std::placeholders::_1); - CMonLogPublishingThread::LoggingCallbackT log_cb = std::bind(&CMonitoringImpl::GetLoggingMsg, this, std::placeholders::_1); - m_pub_threadcaller = std::make_shared(mon_cb, log_cb); - - // 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); - - m_init = true; - } - - void CMonitoringImpl::Destroy() - { - g_registration_receiver()->RemCustomApplySampleCallback(); - m_init = false; - } - - void CMonitoringImpl::SetExclFilter(const std::string& filter_) - { - m_topic_filter_excl_s = filter_; - } - - void CMonitoringImpl::SetInclFilter(const std::string& filter_) - { - m_topic_filter_incl_s = filter_; - } - - void CMonitoringImpl::SetFilterState(bool state_) - { - if (state_) - { - // create excluding filter list - { - std::lock_guard lock(m_topic_filter_excl_mtx); - Tokenize(m_topic_filter_excl_s, m_topic_filter_excl, ",;", true); - } - - // create including filter list - { - std::lock_guard lock(m_topic_filter_incl_mtx); - Tokenize(m_topic_filter_incl_s, m_topic_filter_incl, ",;", true); - } - } - else - { - { - std::lock_guard lock(m_topic_filter_excl_mtx); - m_topic_filter_excl.clear(); - } - { - std::lock_guard lock(m_topic_filter_incl_mtx); - m_topic_filter_incl.clear(); - } - } - } - - size_t CMonitoringImpl::ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType /*layer_*/) - { - // if sample is from outside and we are in local network mode - // do not process sample - if (!IsLocalHost(ecal_sample_) && !m_network) return 0; - - switch (ecal_sample_.cmd_type()) - { - case eCAL::pb::bct_none: - case eCAL::pb::bct_set_sample: - break; - case eCAL::pb::bct_reg_process: - { - // register process - RegisterProcess(ecal_sample_); - } - break; - case eCAL::pb::bct_reg_service: - { - // register service - RegisterServer(ecal_sample_); - } - break; - case eCAL::pb::bct_reg_client: - { - // register client - RegisterClient(ecal_sample_); - } - break; - case eCAL::pb::bct_reg_publisher: - { - // register publisher - RegisterTopic(ecal_sample_, CMonitoringImpl::publisher); - } - break; - case eCAL::pb::bct_reg_subscriber: - { - // register subscriber - RegisterTopic(ecal_sample_, CMonitoringImpl::subscriber); - } - break; - default: - { - eCAL::Logging::Log(log_level_debug1, "CMonitoringImpl::ApplySample : unknown sample type"); - } - break; - } - return 0; - } - - bool CMonitoringImpl::RegisterTopic(const eCAL::pb::Sample& sample_, enum ePubSub pubsub_type_) - { - auto sample_topic = sample_.topic(); - int process_id = sample_topic.pid(); - std::string topic_name = sample_topic.tname(); - size_t topic_size = static_cast(sample_topic.tsize()); - bool topic_tlayer_ecal_udp_mc(false); - bool topic_tlayer_ecal_shm(false); - bool topic_tlayer_ecal_tcp(false); - bool topic_tlayer_inproc(false); - for (auto layer : sample_topic.tlayer()) - { - topic_tlayer_ecal_udp_mc |= (layer.type() == eCAL::pb::tl_ecal_udp_mc) && layer.confirmed(); - topic_tlayer_ecal_shm |= (layer.type() == eCAL::pb::tl_ecal_shm) && layer.confirmed(); - topic_tlayer_ecal_tcp |= (layer.type() == eCAL::pb::tl_ecal_tcp) && layer.confirmed(); - topic_tlayer_inproc |= (layer.type() == eCAL::pb::tl_inproc) && layer.confirmed(); - } - size_t connections_loc = static_cast(sample_topic.connections_loc()); - size_t connections_ext = static_cast(sample_topic.connections_ext()); - long long did = sample_topic.did(); - long long dclock = sample_topic.dclock(); - long long ddropped = sample_topic.message_drops(); - long dfreq = sample_topic.dfreq(); - - // check blacklist topic filter - { - 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); - } - } - - // check whitelist topic filter - bool is_topic_in_filter(false); - { - 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; - } - } - } - - if (is_topic_in_filter == false) return (false); - - ///////////////////////////////// - // register in topic map - ///////////////////////////////// - STopicMonMap* pTopicMap = GetMap(pubsub_type_); - if (pTopicMap) - { - // acquire access - std::lock_guard lock(pTopicMap->sync); - - // common infos - std::string host_name = sample_topic.hname(); - std::string process_name = sample_topic.pname(); - std::string unit_name = sample_topic.uname(); - std::string topic_id = sample_topic.tid(); - std::string topic_type = sample_topic.ttype(); - std::string topic_desc = sample_topic.tdesc(); - auto attr = sample_topic.attr(); - - // try to get topic info - std::string topic_name_id = topic_name + topic_id; - STopicMon& TopicInfo = (*pTopicMap->map)[topic_name_id]; - - // set static content - TopicInfo.hname = std::move(host_name); - TopicInfo.pid = process_id; - TopicInfo.pname = std::move(process_name); - TopicInfo.uname = std::move(unit_name); - TopicInfo.tname = std::move(topic_name); - TopicInfo.tid = std::move(topic_id); - - // update flexible content - TopicInfo.rclock++; - TopicInfo.ttype = std::move(topic_type); - TopicInfo.tdesc = std::move(topic_desc); - TopicInfo.attr = std::map{attr.begin(), attr.end()}; - TopicInfo.tlayer_ecal_udp_mc = topic_tlayer_ecal_udp_mc; - TopicInfo.tlayer_ecal_shm = topic_tlayer_ecal_shm; - TopicInfo.tlayer_ecal_tcp = topic_tlayer_ecal_tcp; - TopicInfo.tlayer_inproc = topic_tlayer_inproc; - TopicInfo.tsize = static_cast(topic_size); - TopicInfo.connections_loc = static_cast(connections_loc); - TopicInfo.connections_ext = static_cast(connections_ext); - TopicInfo.did = did; - TopicInfo.dclock = dclock; - TopicInfo.ddropped = ddropped; - TopicInfo.dfreq = dfreq; - } - - return(true); - } - - bool CMonitoringImpl::RegisterProcess(const eCAL::pb::Sample& sample_) - { - auto sample_process = sample_.process(); - std::string host_name = sample_process.hname(); - std::string process_name = sample_process.pname(); - int process_id = sample_process.pid(); - std::string process_param = sample_process.pparam(); - std::string unit_name = sample_process.uname(); - long long process_memory = sample_process.pmemory(); - float process_cpu = sample_process.pcpu(); - float process_usrptime = sample_process.usrptime(); - long long process_datawrite = sample_process.datawrite(); - long long process_dataread = sample_process.dataread(); - auto sample_process_state = sample_process.state(); - int process_state_severity = sample_process_state.severity(); - int process_state_severity_level = sample_process_state.severity_level(); - std::string process_state_info = sample_process_state.info(); - int process_tsync_state = sample_process.tsync_state(); - std::string process_tsync_mod_name = sample_process.tsync_mod_name(); - int component_init_state = sample_process.component_init_state(); - std::string component_init_info = sample_process.component_init_info(); - std::string ecal_runtime_version = sample_process.ecal_runtime_version(); - - std::stringstream process_id_ss; - process_id_ss << process_id; - std::string process_name_id = process_name + process_id_ss.str(); - - // acquire access - std::lock_guard lock(m_process_map.sync); - - // try to get process info - SProcessMon& ProcessInfo = (*m_process_map.map)[process_name_id]; - - // set static content - ProcessInfo.hname = std::move(host_name); - ProcessInfo.pname = std::move(process_name); - ProcessInfo.uname = std::move(unit_name); - ProcessInfo.pid = process_id; - ProcessInfo.pparam = std::move(process_param); - - // update flexible content - ProcessInfo.rclock++; - ProcessInfo.pmemory = process_memory; - ProcessInfo.pcpu = process_cpu; - ProcessInfo.usrptime = process_usrptime; - ProcessInfo.datawrite = process_datawrite; - ProcessInfo.dataread = process_dataread; - ProcessInfo.state_severity = process_state_severity; - ProcessInfo.state_severity_level = process_state_severity_level; - ProcessInfo.state_info = std::move(process_state_info); - ProcessInfo.tsync_state = process_tsync_state; - ProcessInfo.tsync_mod_name = std::move(process_tsync_mod_name); - ProcessInfo.component_init_state = component_init_state; - ProcessInfo.component_init_info = std::move(component_init_info); - ProcessInfo.ecal_runtime_version = std::move(ecal_runtime_version); - - return(true); - } - - bool CMonitoringImpl::RegisterServer(const eCAL::pb::Sample& sample_) - { - auto sample_service = sample_.service(); - std::string host_name = sample_service.hname(); - std::string service_name = sample_service.sname(); - std::string service_id = sample_service.sid(); - std::string process_name = sample_service.pname(); - std::string unit_name = sample_service.uname(); - int process_id = sample_service.pid(); - int tcp_port = sample_service.tcp_port(); - - std::stringstream process_id_ss; - process_id_ss << process_id; - std::string service_name_id = service_name + service_id + process_id_ss.str(); - - // acquire access - std::lock_guard lock(m_server_map.sync); - - // try to get service info - SServerMon& ServerInfo = (*m_server_map.map)[service_name_id]; - - // set static content - ServerInfo.hname = std::move(host_name); - ServerInfo.sname = std::move(service_name); - ServerInfo.sid = std::move(service_id); - ServerInfo.pname = std::move(process_name); - ServerInfo.uname = std::move(unit_name); - ServerInfo.pid = process_id; - ServerInfo.tcp_port = tcp_port; - - // update flexible content - ServerInfo.rclock++; - ServerInfo.methods.clear(); - for (int i = 0; i < sample_.service().methods_size(); ++i) - { - struct SMethodMon method; - auto sample_service_methods = sample_.service().methods(i); - method.mname = sample_service_methods.mname(); - method.req_type = sample_service_methods.req_type(); - method.req_desc = sample_service_methods.req_desc(); - method.resp_type = sample_service_methods.resp_type(); - method.resp_desc = sample_service_methods.resp_desc(); - method.call_count = sample_service_methods.call_count(); - ServerInfo.methods.push_back(method); - } - - return(true); - } - - bool CMonitoringImpl::RegisterClient(const eCAL::pb::Sample& sample_) - { - auto sample_client = sample_.client(); - std::string host_name = sample_client.hname(); - std::string service_name = sample_client.sname(); - std::string service_id = sample_client.sid(); - std::string process_name = sample_client.pname(); - std::string unit_name = sample_client.uname(); - int process_id = sample_client.pid(); - - std::stringstream process_id_ss; - process_id_ss << process_id; - std::string service_name_id = service_name + service_id + process_id_ss.str(); - - // acquire access - std::lock_guard lock(m_client_map.sync); - - // try to get service info - SClientMon& ClientInfo = (*m_client_map.map)[service_name_id]; - - // set static content - ClientInfo.hname = std::move(host_name); - ClientInfo.sname = std::move(service_name); - ClientInfo.sid = std::move(service_id); - ClientInfo.pname = std::move(process_name); - ClientInfo.uname = std::move(unit_name); - ClientInfo.pid = process_id; - - // update flexible content - ClientInfo.rclock++; - - return(true); - } - - void CMonitoringImpl::RegisterLogMessage(const eCAL::pb::LogMessage& log_msg_) - { - std::lock_guard lock(m_log_msglist_sync); - m_log_msglist.emplace_back(log_msg_); - } - - CMonitoringImpl::STopicMonMap* CMonitoringImpl::GetMap(enum ePubSub pubsub_type_) - { - STopicMonMap* pHostMap = nullptr; - switch (pubsub_type_) - { - case publisher: - pHostMap = &m_publisher_map; - break; - case subscriber: - pHostMap = &m_subscriber_map; - break; - } - return(pHostMap); - }; - - void CMonitoringImpl::GetMonitoringMsg(eCAL::pb::Monitoring& monitoring_) - { - // clear protobuf object - monitoring_.Clear(); - - // write all registrations to monitoring message object - MonitorProcs(monitoring_); - MonitorServer(monitoring_); - MonitorClients(monitoring_); - MonitorTopics(m_publisher_map, monitoring_, "publisher"); - MonitorTopics(m_subscriber_map, monitoring_, "subscriber"); - } - - void CMonitoringImpl::GetLoggingMsg(eCAL::pb::Logging& logging_) - { - // clear protobuf object - logging_.Clear(); - - // acquire access - std::lock_guard lock(m_log_msglist_sync); - - LogMessageListT::const_iterator siter = m_log_msglist.begin(); - while (siter != m_log_msglist.end()) - { - // add log message - eCAL::pb::LogMessage* pMonLogMessage = logging_.add_logs(); - - // copy content - pMonLogMessage->CopyFrom(*siter); - - ++siter; - } - - // empty message list - m_log_msglist.clear(); - } - - int CMonitoringImpl::PubMonitoring(bool state_, std::string & name_) - { - // (de)activate monitor publisher - m_pub_threadcaller->SetMonState(state_, name_); - return 0; - } - - int CMonitoringImpl::PubLogging(bool state_, std::string & name_) - { - // (de)activate logging publisher - m_pub_threadcaller->SetLogState(state_, name_); - return 0; - } - - void CMonitoringImpl::MonitorProcs(eCAL::pb::Monitoring& monitoring_) - { - // acquire access - std::lock_guard lock(m_process_map.sync); - - // iterate map - m_process_map.map->remove_deprecated(); - for (const auto& process : (*m_process_map.map)) - { - // add host - eCAL::pb::Process* pMonProcs = monitoring_.add_processes(); - - // registration clock - pMonProcs->set_rclock(process.second.rclock); - - // host name - pMonProcs->set_hname(process.second.hname); - - // process name - pMonProcs->set_pname(process.second.pname); - - // unit name - pMonProcs->set_uname(process.second.uname); - - // process id - pMonProcs->set_pid(process.second.pid); - - // process parameter - pMonProcs->set_pparam(process.second.pparam); - - // process memory - pMonProcs->set_pmemory(process.second.pmemory); - - // process cpu - pMonProcs->set_pcpu(process.second.pcpu); - - // process user core time - pMonProcs->set_usrptime(process.second.usrptime); - - // process data write bytes - pMonProcs->set_datawrite(process.second.datawrite); - - // process data read bytes - pMonProcs->set_dataread(process.second.dataread); - - // state - auto state = pMonProcs->mutable_state(); - - // severity state - state->set_severity(eCAL::pb::eProcessSeverity(process.second.state_severity)); - - // severity level - state->set_severity_level(eCAL::pb::eProcessSeverityLevel(process.second.state_severity_level)); - - // severity info - state->set_info(process.second.state_info); - - // time synchronization state - pMonProcs->set_tsync_state(eCAL::pb::eTSyncState(process.second.tsync_state)); - - // time synchronization module name - pMonProcs->set_tsync_mod_name(process.second.tsync_mod_name); - - // eCAL component initialization state - pMonProcs->set_component_init_state(process.second.component_init_state); - - // eCAL component initialization info - pMonProcs->set_component_init_info(process.second.component_init_info); - - // eCAL component runtime version - pMonProcs->set_ecal_runtime_version(process.second.ecal_runtime_version); - } - } - - void CMonitoringImpl::MonitorServer(eCAL::pb::Monitoring& monitoring_) - { - // acquire access - std::lock_guard lock(m_server_map.sync); - - // iterate map - m_server_map.map->remove_deprecated(); - for (const auto& service : (*m_server_map.map)) - { - // add host - eCAL::pb::Service* pMonService = monitoring_.add_services(); - - // registration clock - pMonService->set_rclock(service.second.rclock); - - // host name - pMonService->set_hname(service.second.hname); - - // process name - pMonService->set_pname(service.second.pname); - - // unit name - pMonService->set_uname(service.second.uname); - - // process id - pMonService->set_pid(service.second.pid); - - // service name - pMonService->set_sname(service.second.sname); - - // service id - pMonService->set_sid(service.second.sid); - - // tcp port - pMonService->set_tcp_port(service.second.tcp_port); - - // methods - for (auto method : service.second.methods) - { - eCAL::pb::Method* pMonMethod = pMonService->add_methods(); - pMonMethod->set_mname(method.mname); - pMonMethod->set_req_type(method.req_type); - pMonMethod->set_req_desc(method.req_desc); - pMonMethod->set_resp_type(method.resp_type); - pMonMethod->set_resp_desc(method.resp_desc); - pMonMethod->set_call_count(method.call_count); - } - } - } - - void CMonitoringImpl::MonitorClients(eCAL::pb::Monitoring& monitoring_) - { - // acquire access - std::lock_guard lock(m_client_map.sync); - - // iterate map - m_client_map.map->remove_deprecated(); - for (const auto& service : (*m_client_map.map)) - { - // add host - eCAL::pb::Client* pMonClient = monitoring_.add_clients(); - - // registration clock - pMonClient->set_rclock(service.second.rclock); - - // host name - pMonClient->set_hname(service.second.hname); - - // process name - pMonClient->set_pname(service.second.pname); - - // unit name - pMonClient->set_uname(service.second.uname); - - // process id - pMonClient->set_pid(service.second.pid); - - // service name - pMonClient->set_sname(service.second.sname); - - // service id - pMonClient->set_sid(service.second.sid); - } - } - - void CMonitoringImpl::MonitorTopics(STopicMonMap& map_, eCAL::pb::Monitoring& monitoring_, const std::string& direction_) - { - // acquire access - std::lock_guard lock(map_.sync); - - // iterate map - map_.map->remove_deprecated(); - for (const auto& topic : (*map_.map)) - { - // add topic - eCAL::pb::Topic* pMonTopic = monitoring_.add_topics(); - - // registration clock - pMonTopic->set_rclock(topic.second.rclock); - - // host name - pMonTopic->set_hname(topic.second.hname); - - // process id - pMonTopic->set_pid(topic.second.pid); - - // process name - pMonTopic->set_pname(topic.second.pname); - - // unit name - pMonTopic->set_uname(topic.second.uname); - - // topic id - pMonTopic->set_tid(topic.second.tid); - - // topic name - pMonTopic->set_tname(topic.second.tname); - - // direction - pMonTopic->set_direction(direction_); - - // topic type - pMonTopic->set_ttype(topic.second.ttype); - - // topic transport layers - if (topic.second.tlayer_ecal_udp_mc) - { - auto tlayer = pMonTopic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_ecal_udp_mc); - tlayer->set_confirmed(true); - } - if (topic.second.tlayer_ecal_shm) - { - auto tlayer = pMonTopic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_ecal_shm); - tlayer->set_confirmed(true); - } - if (topic.second.tlayer_ecal_tcp) - { - auto tlayer = pMonTopic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_ecal_tcp); - tlayer->set_confirmed(true); - } - if (topic.second.tlayer_inproc) - { - auto tlayer = pMonTopic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_inproc); - tlayer->set_confirmed(true); - } - - // topic description - pMonTopic->set_tdesc(topic.second.tdesc); - - // topic attributes - *pMonTopic->mutable_attr() = google::protobuf::Map {topic.second.attr.begin(), topic.second.attr.end()}; - - // topic size - pMonTopic->set_tsize(topic.second.tsize); - - // local connections - pMonTopic->set_connections_loc(topic.second.connections_loc); - - // external connections - pMonTopic->set_connections_ext(topic.second.connections_ext); - - // data id (publisher setid) - pMonTopic->set_did(topic.second.did); - - // data clock - pMonTopic->set_dclock(topic.second.dclock); - - // data dropped - pMonTopic->set_message_drops(google::protobuf::int32(topic.second.ddropped)); - - // data frequency - pMonTopic->set_dfreq(topic.second.dfreq); - } - } - - void CMonitoringImpl::Tokenize(const std::string& str, StrICaseSetT& tokens, const std::string& delimiters, bool trimEmpty) - { - std::string::size_type pos, lastPos = 0; - - for (;;) - { - pos = str.find_first_of(delimiters, lastPos); - if (pos == std::string::npos) - { - pos = str.length(); - if (pos != lastPos || !trimEmpty) - { - tokens.emplace(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/mon/ecal_monitoring_impl.h b/ecal/core/src/mon/ecal_monitoring_impl.h deleted file mode 100644 index 28d102b..0000000 --- a/ecal/core/src/mon/ecal_monitoring_impl.h +++ /dev/null @@ -1,303 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Global monitoring class (implementation) -**/ - -#pragma once - -#include "ecal_monitoring_threads.h" - -#include "ecal_expmap.h" -#include "io/rcv_sample.h" - -#include -#include -#include - -namespace eCAL -{ - //////////////////////////////////////// - // Monitoring Declaration - //////////////////////////////////////// - class CMonitoringImpl : public CSampleReceiver - { - public: - CMonitoringImpl(); - ~CMonitoringImpl(); - - void Create(); - void Destroy(); - - void SetExclFilter(const std::string& filter_); - void SetInclFilter(const std::string& filter_); - void SetFilterState(bool state_); - - void GetMonitoringMsg(eCAL::pb::Monitoring& monitoring_); - void GetLoggingMsg(eCAL::pb::Logging& logging_); - - int PubMonitoring(bool state_, std::string& name_); - int PubLogging(bool state_, std::string& name_); - - enum ePubSub - { - publisher = 1, - subscriber = 2, - }; - - bool HasSample(const std::string& /* sample_name_ */) { return(true); }; - size_t ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType /*layer_*/); - - bool RegisterProcess(const eCAL::pb::Sample& sample_); - bool RegisterServer(const eCAL::pb::Sample& sample_); - bool RegisterClient(const eCAL::pb::Sample& sample_); - bool RegisterTopic(const eCAL::pb::Sample& sample_, enum ePubSub pubsub_type_); - void RegisterLogMessage(const eCAL::pb::LogMessage& log_msg_); - - protected: - struct STopicMon - { - STopicMon() - { - rclock = 0; - pid = 0; - tlayer_ecal_udp_mc = false; - tlayer_ecal_shm = false; - tlayer_ecal_tcp = false; - tlayer_inproc = false; - tsize = 0; - connections_loc = 0; - connections_ext = 0; - did = 0; - dclock = 0; - ddropped = 0; - dfreq = 0; - }; - - int rclock; - std::string hname; - int pid; - std::string pname; - std::string uname; - std::string domain; - std::string tname; - std::string tid; - std::string ttype; - std::string tdesc; - std::map attr; - bool tlayer_ecal_udp_mc; - bool tlayer_ecal_shm; - bool tlayer_ecal_tcp; - bool tlayer_inproc; - int tsize; - int connections_loc; - int connections_ext; - long long did; - long long dclock; - long long ddropped; - long dfreq; - }; - typedef eCAL::Util::CExpMap TopicMonMapT; - - struct STopicMonMap - { - explicit STopicMonMap(const std::chrono::milliseconds& timeout_) : - map(new TopicMonMapT(timeout_)) - { - }; - std::mutex sync; - std::unique_ptr map; - }; - - struct SProcessMon - { - SProcessMon() - { - rclock = 0; - pid = 0; - pmemory = 0; - pcpu = 0.0f; - usrptime = 0.0f; - datawrite = 0; - dataread = 0; - state_severity = 0; - state_severity_level = 0; - tsync_state = 0; - component_init_state = 0; - }; - - int rclock; - std::string hname; - std::string pname; - std::string uname; - int pid; - std::string pparam; - long long pmemory; - float pcpu; - float usrptime; - long long datawrite; - long long dataread; - int state_severity; - int state_severity_level; - std::string state_info; - int tsync_state; - std::string tsync_mod_name; - int component_init_state; - std::string component_init_info; - std::string ecal_runtime_version; - }; - typedef eCAL::Util::CExpMap ProcessMonMapT; - - struct SProcessMonMap - { - explicit SProcessMonMap(const std::chrono::milliseconds& timeout_) : - map(new ProcessMonMapT(timeout_)) - { - }; - std::mutex sync; - std::unique_ptr map; - }; - - struct SMethodMon - { - SMethodMon() - { - call_count = 0; - }; - std::string mname; - std::string req_type; - std::string req_desc; - std::string resp_type; - std::string resp_desc; - long long call_count; - }; - - struct SServerMon - { - SServerMon() - { - rclock = 0; - pid = 0; - tcp_port = 0; - }; - - int rclock; - std::string hname; - std::string sname; - std::string sid; - std::string pname; - std::string uname; - int pid; - int tcp_port; - std::vector methods; - }; - typedef eCAL::Util::CExpMap ServerMonMapT; - - struct SServerMonMap - { - explicit SServerMonMap(const std::chrono::milliseconds& timeout_) : - map(new ServerMonMapT(timeout_)) - { - }; - std::mutex sync; - std::unique_ptr map; - }; - - struct SClientMon - { - SClientMon() - { - rclock = 0; - pid = 0; - }; - - int rclock; - std::string hname; - std::string sname; - std::string sid; - std::string pname; - std::string uname; - int pid; - }; - typedef eCAL::Util::CExpMap ClientMonMapT; - - struct SClientMonMap - { - explicit SClientMonMap(const std::chrono::milliseconds& timeout_) : - map(new ClientMonMapT(timeout_)) - { - }; - 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 - } - }; - typedef std::set StrICaseSetT; - - STopicMonMap* GetMap(enum ePubSub pubsub_type_); - - - void MonitorProcs(eCAL::pb::Monitoring& monitoring_); - void MonitorServer(eCAL::pb::Monitoring& monitoring_); - void MonitorClients(eCAL::pb::Monitoring& monitoring_); - void MonitorTopics(STopicMonMap& map_, eCAL::pb::Monitoring& monitoring_, const std::string& direction_); - - void Tokenize(const std::string& str, StrICaseSetT& tokens, const std::string& delimiters, bool trimEmpty); - - bool m_init; - bool m_network; - 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; - - // database - STopicMonMap m_publisher_map; - STopicMonMap m_subscriber_map; - SProcessMonMap m_process_map; - SServerMonMap m_server_map; - SClientMonMap m_client_map; - - // logging - typedef std::list LogMessageListT; - std::mutex m_log_msglist_sync; - LogMessageListT m_log_msglist; - - // worker threads - std::shared_ptr m_log_rcv_threadcaller; - std::shared_ptr m_pub_threadcaller; - }; -} diff --git a/ecal/core/src/mon/ecal_monitoring_threads.cpp b/ecal/core/src/mon/ecal_monitoring_threads.cpp deleted file mode 100644 index 5e2ecb2..0000000 --- a/ecal/core/src/mon/ecal_monitoring_threads.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Global database class -**/ - -#include -#include - -#include "ecal_def.h" -#include "io/msg_type.h" -#include "io/udp_configurations.h" - -#include "ecal_config_reader_hlp.h" -#include "ecal_monitoring_threads.h" -#include "ecal_global_accessors.h" - -namespace eCAL -{ - static bool IsLocalHost(const eCAL::pb::LogMessage& ecal_message_) - { - std::string host_name = ecal_message_.hname(); - if (host_name.empty()) return(false); - if (host_name == Process::GetHostName()) return(true); - return(false); - } - - CLoggingReceiveThread::CLoggingReceiveThread(LogMessageCallbackT log_cb_) : - m_network_mode(false), m_log_cb(log_cb_) - { - SReceiverAttr attr; - bool local_only = !Config::IsNetworkEnabled(); - // for local only communication we switch to local broadcasting to bypass vpn's or firewalls - if (local_only) - { - attr.broadcast = true; - } - else - { - attr.broadcast = false; - } - attr.ipaddr = UDP::GetLoggingMulticastAddress(); - attr.port = Config::GetUdpMulticastPort() + NET_UDP_MULTICAST_PORT_LOG_OFF; - attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); - - m_log_rcv.Create(attr); - m_log_rcv_thread.Start(0, std::bind(&CLoggingReceiveThread::ThreadFun, this)); - m_msg_buffer.resize(MSG_BUFFER_SIZE); - } - - CLoggingReceiveThread::~CLoggingReceiveThread() - { - m_log_rcv_thread.Stop(); - m_log_rcv.Destroy(); - } - - void CLoggingReceiveThread::SetNetworkMode(bool network_mode_) - { - m_network_mode = network_mode_; - } - - int CLoggingReceiveThread::ThreadFun() - { - // wait for any incoming message - size_t recv_len = m_log_rcv.Receive(m_msg_buffer.data(), m_msg_buffer.size(), 10); - if (recv_len > 0) - { - m_log_ecal_msg.Clear(); - if (m_log_ecal_msg.ParseFromArray(m_msg_buffer.data(), static_cast(recv_len))) - { - if (IsLocalHost(m_log_ecal_msg) || m_network_mode) - { - m_log_cb(m_log_ecal_msg); - } - } - } - - return(0); - }; - - CMonLogPublishingThread::CMonLogPublishingThread(MonitoringCallbackT mon_cb_, LoggingCallbackT log_cb_) : - m_mon_cb(mon_cb_), m_log_cb(log_cb_) - { - m_pub_thread.Start(CMN_REGISTRATION_REFRESH, std::bind(&CMonLogPublishingThread::ThreadFun, this)); - }; - - CMonLogPublishingThread::~CMonLogPublishingThread() - { - m_pub_thread.Stop(); - m_mon_pub.pub.Destroy(); - m_log_pub.pub.Destroy(); - }; - - void CMonLogPublishingThread::SetMonState(bool state_, const std::string& name_) - { - m_mon_pub.state = state_; - m_mon_pub.name = name_; - if (state_) - { - m_mon_pub.pub.Create(name_); - } - else - { - m_mon_pub.pub.Destroy(); - } - } - - void CMonLogPublishingThread::SetLogState(bool state_, const std::string& name_) - { - m_log_pub.state = state_; - m_log_pub.name = name_; - if (state_) - { - m_log_pub.pub.Create(name_); - } - else - { - m_log_pub.pub.Destroy(); - } - } - - int CMonLogPublishingThread::ThreadFun() - { - if (m_mon_pub.state) - { - // get monitoring - eCAL::pb::Monitoring monitoring; - m_mon_cb(monitoring); - // publish monitoring - m_mon_pub.pub.Send(monitoring); - } - - if (m_log_pub.state) - { - // get logging - eCAL::pb::Logging logging; - m_log_cb(logging); - // publish logging - m_log_pub.pub.Send(logging); - } - return 0; - } -} diff --git a/ecal/core/src/mon/ecal_monitoring_threads.h b/ecal/core/src/mon/ecal_monitoring_threads.h deleted file mode 100644 index b478f1e..0000000 --- a/ecal/core/src/mon/ecal_monitoring_threads.h +++ /dev/null @@ -1,100 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Monitoring worker threads -**/ - -#pragma once - -#include -#include - -#include "ecal_thread.h" -#include "io/udp_receiver.h" - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include -#include - -namespace eCAL -{ - class CLoggingReceiveThread - { - public: - using LogMessageCallbackT = std::function; - - CLoggingReceiveThread(LogMessageCallbackT log_cb_); - virtual ~CLoggingReceiveThread(); - - void SetNetworkMode(bool network_mode_); - - protected: - int ThreadFun(); - - CUDPReceiver m_log_rcv; - class CThread m_log_rcv_thread; - - bool m_network_mode; - std::vector m_msg_buffer; - eCAL::pb::LogMessage m_log_ecal_msg; - LogMessageCallbackT m_log_cb; - }; - - class CMonLogPublishingThread - { - public: - using MonitoringCallbackT = std::function; - using LoggingCallbackT = std::function; - - CMonLogPublishingThread(MonitoringCallbackT mon_cb_, LoggingCallbackT log_cb_); - virtual ~CMonLogPublishingThread(); - - void SetMonState(bool state_, const std::string& name_); - void SetLogState(bool state_, const std::string& name_); - - protected: - int ThreadFun(); - - template - struct SProtoPub - { - SProtoPub() : state(false) {} - bool state; - std::string name; - - protobuf::CPublisher pub; - }; - - class CThread m_pub_thread; - struct SProtoPub m_mon_pub; - struct SProtoPub m_log_pub; - - MonitoringCallbackT m_mon_cb; - LoggingCallbackT m_log_cb; - }; -} diff --git a/ecal/core/src/pubsub/ecal_proto_dyn_json_sub.cpp b/ecal/core/src/pubsub/ecal_proto_dyn_json_sub.cpp deleted file mode 100644 index 79a28b2..0000000 --- a/ecal/core/src/pubsub/ecal_proto_dyn_json_sub.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * dynamic protobuf message decoder -**/ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif -#include -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -namespace eCAL -{ - namespace protobuf - { - class CDynamicJSONSubscriberImpl - { - public: - CDynamicJSONSubscriberImpl() : - created(false), - msg_decoder(nullptr), - msg_string() - {} - - CDynamicJSONSubscriberImpl(const std::string& topic_name_) : - created(false), - msg_decoder(nullptr), - msg_string() - { - Create(topic_name_); - } - - ~CDynamicJSONSubscriberImpl() - { - Destroy(); - } - - void Create(const std::string& topic_name_) - { - if (created) return; - - // create message decoder - msg_decoder = new eCAL::protobuf::CProtoDynDecoder(); - - // create subscriber - msg_sub.Create(topic_name_); - - // add callback - msg_sub.AddReceiveCallback(std::bind(&CDynamicJSONSubscriberImpl::OnReceive, this, std::placeholders::_1, std::placeholders::_2)); - - created = true; - } - - void Destroy() - { - if (!created) return; - - // remove callback - msg_sub.RemReceiveCallback(); - - // destroy subscriber - msg_sub.Destroy(); - - // delete message decoder - delete msg_decoder; - - created = false; - } - - void AddReceiveCallback(ReceiveCallbackT callback_) - { - msg_callback = callback_; - } - - void RemReceiveCallback() - { - msg_callback = nullptr; - } - - protected: - void OnReceive(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) - { - if (msg_string.empty()) - { - // get topic type - topic_type_full = eCAL::Util::GetTopicTypeName(topic_name_); - topic_type_full = topic_type_full.substr(topic_type_full.find_first_of(':') + 1, topic_type_full.size()); - topic_type = topic_type_full.substr(topic_type_full.find_last_of('.') + 1, topic_type_full.size()); - topic_type_full = "/" + topic_type_full; - - if (topic_type.empty()) - { - std::cout << "could not get type for topic " << topic_name_ << std::endl; - return; - } - - // get topic description - topic_desc = eCAL::Util::GetTopicDescription(topic_name_); - if (topic_desc.empty()) - { - std::cout << "could not get description for topic " << topic_name_ << std::endl; - return; - } - - std::string error_s; - google::protobuf::FileDescriptorSet proto_desc; - proto_desc.ParseFromString(topic_desc); - std::shared_ptr msg(msg_decoder->GetProtoMessageFromDescriptorSet(proto_desc, topic_type, error_s)); - resolver_.reset(google::protobuf::util::NewTypeResolverForDescriptorPool("", msg_decoder->GetDescriptorPool())); - } - - // decode message and execute callback - //if(msg_callback && msg_ptr && msg_ptr->ParseFromArray(data_->buf, data_->size)) - if (msg_callback) - { - - google::protobuf::util::JsonOptions options; - options.always_print_primitive_fields = true; - - std::string binary_input; - binary_input.assign((char*)data_->buf, static_cast(data_->size)); - msg_string.clear(); - auto status = google::protobuf::util::BinaryToJsonString(resolver_.get(), topic_type_full, binary_input, &msg_string, options); - if (status.ok()) - { - SReceiveCallbackData cb_data; - cb_data.buf = (void*)msg_string.c_str(); - cb_data.size = (long)msg_string.size(); - cb_data.time = data_->time; - msg_callback(topic_name_, &cb_data); - } - } - } - - bool created; - eCAL::protobuf::CProtoDynDecoder* msg_decoder; - std::string msg_string; - eCAL::CSubscriber msg_sub; - ReceiveCallbackT msg_callback; - - std::string topic_type; - std::string topic_type_full; - - std::string topic_desc; - std::shared_ptr resolver_; - google::protobuf::DescriptorPool m_descriptor_pool; - - private: - // this object must not be copied. - CDynamicJSONSubscriberImpl(const CDynamicSubscriber&); - CDynamicJSONSubscriberImpl& operator=(const CDynamicSubscriber&); - }; - - CDynamicJSONSubscriber::CDynamicJSONSubscriber() : - created(false), - proto_dyn_sub_impl(nullptr) - { - } - - CDynamicJSONSubscriber::CDynamicJSONSubscriber(const std::string& topic_name_) : - created(false) - { - Create(topic_name_); - } - - CDynamicJSONSubscriber::~CDynamicJSONSubscriber() - { - Destroy(); - }; - - void CDynamicJSONSubscriber::Create(const std::string& topic_name_) - { - if (created) return; - proto_dyn_sub_impl = new CDynamicJSONSubscriberImpl(topic_name_); - proto_dyn_sub_impl->Create(topic_name_); - created = true; - } - - void CDynamicJSONSubscriber::Destroy() - { - if (!created) return; - proto_dyn_sub_impl->Destroy(); - delete proto_dyn_sub_impl; - proto_dyn_sub_impl = nullptr; - created = false; - } - - bool CDynamicJSONSubscriber::AddReceiveCallback(ReceiveCallbackT callback_) - { - if (!created) return false; - proto_dyn_sub_impl->AddReceiveCallback(callback_); - return true; - } - - bool CDynamicJSONSubscriber::RemReceiveCallback() - { - if (!created) return false; - proto_dyn_sub_impl->RemReceiveCallback(); - return true; - } - } -} diff --git a/ecal/core/src/pubsub/ecal_pubgate.cpp b/ecal/core/src/pubsub/ecal_pubgate.cpp deleted file mode 100644 index a4526ed..0000000 --- a/ecal/core/src/pubsub/ecal_pubgate.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL publisher gateway class -**/ - -#include - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" -#include "ecal_pubgate.h" -#include "ecal_descgate.h" - -#include -#include - -namespace eCAL -{ - ////////////////////////////////////////////////////////////////// - // CPubGate - ////////////////////////////////////////////////////////////////// - std::atomic CPubGate::m_created; - CPubGate::CPubGate() : - m_share_type(true), - m_share_desc(true) - { - }; - - CPubGate::~CPubGate() - { - Destroy(); - } - - void CPubGate::Create() - { - if(m_created) return; - - m_created = true; - } - - void CPubGate::Destroy() - { - if(!m_created) return; - - // destroy all remaining publisher - std::unique_lock lock(m_topic_name_datawriter_sync); - for (auto iter = m_topic_name_datawriter_map.begin(); iter != m_topic_name_datawriter_map.end(); ++iter) - { - iter->second->Destroy(); - } - - m_created = false; - } - - void CPubGate::ShareType(bool state_) - { - m_share_type = state_; - } - - void CPubGate::ShareDescription(bool state_) - { - m_share_desc = state_; - } - - bool CPubGate::Register(const std::string& topic_name_, CDataWriter* datawriter_) - { - if(!m_created) return(false); - - // register writer and multicast group - std::unique_lock lock(m_topic_name_datawriter_sync); - m_topic_name_datawriter_map.emplace(std::pair(topic_name_, datawriter_)); - - return(true); - } - - bool CPubGate::Unregister(const std::string& topic_name_, CDataWriter* datawriter_) - { - if(!m_created) return(false); - bool ret_state = false; - - std::unique_lock lock(m_topic_name_datawriter_sync); - auto res = m_topic_name_datawriter_map.equal_range(topic_name_); - for(TopicNameDataWriterMapT::iterator iter = res.first; iter != res.second; ++iter) - { - if(iter->second == datawriter_) - { - m_topic_name_datawriter_map.erase(iter); - break; - } - } - - return(ret_state); - } - - void CPubGate::ApplyLocSubRegistration(const eCAL::pb::Sample& ecal_sample_) - { - if(!m_created) return; - - const auto& ecal_sample_topic = ecal_sample_.topic(); - const std::string& topic_name = ecal_sample_topic.tname(); - const std::string& topic_id = ecal_sample_topic.tid(); - const std::string& topic_type = ecal_sample_topic.ttype(); - const std::string& topic_desc = ecal_sample_topic.tdesc(); - const std::string process_id = std::to_string(ecal_sample_topic.pid()); - - std::string reader_par; - for (const auto& layer : ecal_sample_topic.tlayer()) - { - // layer parameter as protobuf message - // this parameter is not used at all currently - // for local subscriber registrations - reader_par = layer.par_layer().SerializeAsString(); - } - - // store description - if (g_descgate()) - { - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - - g_descgate()->ApplyTopicDescription(topic_name, topic_type, topic_desc, quality); - } - - // register local subscriber - std::shared_lock lock(m_topic_name_datawriter_sync); - auto res = m_topic_name_datawriter_map.equal_range(topic_name); - for(TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) - { - iter->second->ApplyLocSubscription(process_id, topic_id, topic_type, topic_desc, reader_par); - } - } - - void CPubGate::ApplyExtSubRegistration(const eCAL::pb::Sample& ecal_sample_) - { - if(!m_created) return; - - const auto& ecal_sample_topic = ecal_sample_.topic(); - const std::string& host_name = ecal_sample_topic.hname(); - const std::string& topic_name = ecal_sample_topic.tname(); - const std::string& topic_id = ecal_sample_topic.tid(); - const std::string& topic_type = ecal_sample_topic.ttype(); - const std::string& topic_desc = ecal_sample_topic.tdesc(); - const std::string process_id = std::to_string(ecal_sample_topic.pid()); - - std::string reader_par; - for (const auto& layer : ecal_sample_topic.tlayer()) - { - // layer parameter as protobuf message - // this parameter is not used at all currently - // for external subscriber registrations - reader_par = layer.par_layer().SerializeAsString(); - } - - // store description - if (g_descgate()) - { - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - - g_descgate()->ApplyTopicDescription(topic_name, topic_type, topic_desc, quality); - } - - // register external subscriber - std::shared_lock lock(m_topic_name_datawriter_sync); - auto res = m_topic_name_datawriter_map.equal_range(topic_name); - for(TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) - { - iter->second->ApplyExtSubscription(host_name, process_id, topic_id, topic_type, topic_desc, reader_par); - } - } - - void CPubGate::RefreshRegistrations() - { - if (!m_created) return; - - // refresh publisher registrations - std::shared_lock lock(m_topic_name_datawriter_sync); - for (auto iter : m_topic_name_datawriter_map) - { - iter.second->RefreshRegistration(); - } - } -}; diff --git a/ecal/core/src/pubsub/ecal_publisher.cpp b/ecal/core/src/pubsub/ecal_publisher.cpp deleted file mode 100644 index f4a70e7..0000000 --- a/ecal/core/src/pubsub/ecal_publisher.cpp +++ /dev/null @@ -1,462 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief common data publisher based on eCAL -**/ - -#include -#include -#include - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" -#include "ecal_globals.h" -#include "ecal_pubgate.h" -#include "ecal_registration_provider.h" - -#include "readwrite/ecal_writer.h" - -#include -#include - -namespace eCAL -{ - CPublisher::CPublisher() : - m_datawriter(nullptr), - m_id(0), - m_created(false), - m_initialized(false) - { - InitializeQOS(); - InitializeTLayer(); - } - - CPublisher::CPublisher(const std::string& topic_name_, const std::string& topic_type_ /* = "" */, const std::string& topic_desc_ /* = "" */) : - m_datawriter(nullptr), - m_created(false), - m_initialized(false) - { - InitializeQOS(); - InitializeTLayer(); - - Create(topic_name_, topic_type_, topic_desc_); - } - - CPublisher::~CPublisher() - { - Destroy(); - } - - /** - * @brief CPublisher are move-enabled - **/ - CPublisher::CPublisher(CPublisher&& rhs) noexcept : - m_datawriter(std::move(rhs.m_datawriter)), - m_qos(rhs.m_qos), - m_id(rhs.m_id), - m_created(rhs.m_created), - m_initialized(rhs.m_initialized) - { - InitializeQOS(); - InitializeTLayer(); - - rhs.m_created = false; - rhs.m_initialized = false; - } - - /** - * @brief CPublisher are move-enabled - **/ - CPublisher& CPublisher::operator=(CPublisher&& rhs) noexcept - { - m_datawriter = std::move(rhs.m_datawriter); - - m_qos = rhs.m_qos; - m_id = rhs.m_id; - m_created = rhs.m_created; - m_initialized = rhs.m_initialized; - - InitializeQOS(); - InitializeTLayer(); - rhs.m_created = false; - rhs.m_initialized = false; - - return *this; - }; - - bool CPublisher::Create(const std::string& topic_name_, const std::string& topic_type_ /* = "" */, const std::string& topic_desc_ /* = "" */) - { - if(m_created) return(false); - if(topic_name_.size() == 0) return(false); - if(!g_globals()) return(false); - - // initialize globals - if (!g_globals()->IsInitialized(Init::Publisher)) - { - g_globals()->Initialize(Init::Publisher); - m_initialized = true; - } - - // check transport layer initialization - // if layer API was not accessed before creation of this class - // the layer mode will be initialized by the global config - // this can not be done in the constructor because a publisher is allowed - // to construct befor eCAL::Initialize and so global config is not - // existing while construction - if (m_tlayer.sm_udp_mc == TLayer::smode_none) m_tlayer.sm_udp_mc = Config::GetPublisherUdpMulticastMode(); - if (m_tlayer.sm_shm == TLayer::smode_none) m_tlayer.sm_shm = Config::GetPublisherShmMode(); - if (m_tlayer.sm_tcp == TLayer::smode_none) m_tlayer.sm_tcp = Config::GetPublisherTcpMode(); - if (m_tlayer.sm_inproc == TLayer::smode_none) m_tlayer.sm_inproc = Config::GetPublisherInprocMode(); - - // create data writer - m_datawriter = new CDataWriter(); - // set qos - m_datawriter->SetQOS(m_qos); - // set transport layer - m_datawriter->SetLayerMode(TLayer::tlayer_udp_mc, m_tlayer.sm_udp_mc); - m_datawriter->SetLayerMode(TLayer::tlayer_shm, m_tlayer.sm_shm); - m_datawriter->SetLayerMode(TLayer::tlayer_tcp, m_tlayer.sm_tcp); - m_datawriter->SetLayerMode(TLayer::tlayer_inproc, m_tlayer.sm_inproc); - // create it - if (!m_datawriter->Create(topic_name_, topic_type_, topic_desc_)) - { -#ifndef NDEBUG - // log it - if (g_log()) g_log()->Log(log_level_debug1, topic_name_ + "::CPublisher::Create - FAILED"); -#endif - return(false); - } -#ifndef NDEBUG - // log it - if (g_log()) g_log()->Log(log_level_debug1, topic_name_ + "::CPublisher::Create - SUCCESS"); -#endif - // register publisher gateway (for publisher memory file and event name) - g_pubgate()->Register(topic_name_, m_datawriter); - - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type_.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc_.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PUBLISHER; - - // register to description gateway for type / description checking - g_descgate()->ApplyTopicDescription(topic_name_, topic_type_, topic_desc_, quality); - - // we made it :-) - m_created = true; - - return(m_created); - } - - bool CPublisher::Destroy() - { - if(!m_created) return(false); - if(!g_globals()) return(false); - - // destroy data writer - m_datawriter->Destroy(); - - // unregister data writer - if(g_pubgate()) g_pubgate()->Unregister(m_datawriter->GetTopicName(), m_datawriter); - if(g_registration_provider()) g_registration_provider()->UnregisterTopic(m_datawriter->GetTopicName(), m_datawriter->GetTopicID()); -#ifndef NDEBUG - // log it - if (g_log()) g_log()->Log(log_level_debug1, std::string(m_datawriter->GetTopicName() + "::CPublisher::Destroy")); -#endif - - // free datawriter - delete m_datawriter; - m_datawriter = nullptr; - - // we made it :-) - m_created = false; - - // if we initialize the globals then we finalize - // here to decrease reference counter - if (m_initialized) - { - g_globals()->Finalize(Init::Publisher); - m_initialized = false; - } - - return(true); - } - - bool CPublisher::SetTypeName(const std::string& topic_type_name_) - { - if (!m_datawriter) return false; - - if (g_descgate()) - { - const std::string topic_desc = m_datawriter->GetDescription(); - - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type_name_.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PUBLISHER; - - g_descgate()->ApplyTopicDescription(m_datawriter->GetTopicName(), topic_type_name_, topic_desc, quality); - } - - return m_datawriter->SetTypeName(topic_type_name_); - } - - bool CPublisher::SetDescription(const std::string& topic_desc_) - { - if(!m_datawriter) return false; - - if (g_descgate()) - { - const std::string topic_type = m_datawriter->GetTypeName(); - - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc_.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PUBLISHER; - - g_descgate()->ApplyTopicDescription(m_datawriter->GetTopicName(), topic_type, topic_desc_, quality); - } - - return m_datawriter->SetDescription(topic_desc_); - } - - bool CPublisher::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) - { - if(!m_datawriter) return false; - return m_datawriter->SetAttribute(attr_name_, attr_value_); - } - - bool CPublisher::ClearAttribute(const std::string& attr_name_) - { - if(!m_datawriter) return false; - return m_datawriter->ClearAttribute(attr_name_); - } - - bool CPublisher::ShareType(bool state_ /*= true*/) - { - if (!m_datawriter) return false; - m_datawriter->ShareType(state_); - return true; - } - - bool CPublisher::ShareDescription(bool state_ /*= true*/) - { - if (!m_datawriter) return false; - m_datawriter->ShareDescription(state_); - return true; - } - - bool CPublisher::SetQOS(const QOS::SWriterQOS& qos_) - { - if (m_created) return false; - m_qos = qos_; - return true; - } - - QOS::SWriterQOS CPublisher::GetQOS() - { - return m_qos; - } - - bool CPublisher::SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_) - { - switch (layer_) - { - case TLayer::tlayer_udp_mc: - m_tlayer.sm_udp_mc = mode_; - break; - case TLayer::tlayer_shm: - m_tlayer.sm_shm = mode_; - break; - case TLayer::tlayer_tcp: - m_tlayer.sm_tcp = mode_; - break; - case TLayer::tlayer_inproc: - m_tlayer.sm_inproc = mode_; - break; - case TLayer::tlayer_all: - m_tlayer.sm_udp_mc = mode_; - m_tlayer.sm_shm = mode_; - m_tlayer.sm_tcp = mode_; - m_tlayer.sm_inproc = mode_; - break; - default: - break; - } - if (m_created) - { - return m_datawriter->SetLayerMode(layer_, mode_); - } - return true; - } - - bool CPublisher::SetMaxBandwidthUDP(long bandwidth_) - { - if (!m_created) return(false); - return m_datawriter->SetMaxBandwidthUDP(bandwidth_); - } - - bool CPublisher::ShmSetBufferCount(long buffering_) - { - if (!m_created) return(false); - return m_datawriter->ShmSetBufferCount(buffering_); - } - - bool CPublisher::ShmEnableZeroCopy(bool state_) - { - if (!m_created) return(false); - return m_datawriter->ShmEnableZeroCopy(state_); - } - - bool CPublisher::ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_) - { - if (!m_created) return(false); - return m_datawriter->ShmSetAcknowledgeTimeout(acknowledge_timeout_ms_); - } - - bool CPublisher::SetID(long long id_) - { - m_id = id_; - return(true); - } - - size_t CPublisher::Send(const void* const buf_, const size_t len_, const long long time_ /* = -1 */) const - { - if(!m_created) return(0); - - // in an optimization case the - // publisher can send an empty package - // or we do not have any subscription at all - // then the data writer will only do some statistics - // for the monitoring layer and return - if (!IsSubscribed()) - { - m_datawriter->RefreshSendCounter(); - return(len_); - } - - // send content via data writer layer - bool sent(false); - if (time_ == -1) sent = m_datawriter->Write(buf_, len_, eCAL::Time::GetMicroSeconds(), m_id); - else sent = m_datawriter->Write(buf_, len_, time_, m_id); - - // return success - if (sent) return(len_); - else return(0); - } - - size_t CPublisher::Send(const void* const buf_, size_t len_, long long time_, long long acknowledge_timeout_ms_) const - { - if (!m_created) return(0); - - // set new acknowledge timeout - long long current_acknowledge_timeout_ms(m_datawriter->ShmGetAcknowledgeTimeout()); - m_datawriter->ShmSetAcknowledgeTimeout(static_cast(acknowledge_timeout_ms_)); - - // send buffer - size_t len = Send(buf_, len_, time_); - - // reset acknowledge timeout - m_datawriter->ShmSetAcknowledgeTimeout(current_acknowledge_timeout_ms); - - return len; - } - - bool CPublisher::AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_) - { - if (!m_datawriter) return(false); - RemEventCallback(type_); - return(m_datawriter->AddEventCallback(type_, callback_)); - } - - bool CPublisher::RemEventCallback(eCAL_Publisher_Event type_) - { - if (!m_datawriter) return(false); - return(m_datawriter->RemEventCallback(type_)); - } - - bool CPublisher::IsSubscribed() const - { - if(!m_datawriter) return(false); - return(m_datawriter->IsSubscribed()); - } - - size_t CPublisher::GetSubscriberCount() const - { - if(!m_datawriter) return(0); - return(m_datawriter->GetSubscriberCount()); - } - - std::string CPublisher::GetTopicName() const - { - if(!m_datawriter) return(""); - return(m_datawriter->GetTopicName()); - } - - std::string CPublisher::GetTypeName() const - { - if(!m_datawriter) return(""); - return(m_datawriter->GetTypeName()); - } - - std::string CPublisher::GetDescription() const - { - if(!m_datawriter) return(""); - return(m_datawriter->GetDescription()); - } - - void CPublisher::InitializeQOS() - { - m_qos = QOS::SWriterQOS(); - } - - void CPublisher::InitializeTLayer() - { - m_tlayer = TLayer::STLayer(); - } - - std::string CPublisher::Dump(const std::string& indent_ /* = "" */) const - { - std::stringstream out; - - out << indent_ << "----------------------" << std::endl; - out << indent_ << " class CPublisher" << std::endl; - out << indent_ << "----------------------" << std::endl; - out << indent_ << "m_created: " << m_created << std::endl; - if(m_datawriter && m_datawriter->IsCreated()) out << indent_ << m_datawriter->Dump(" "); - out << std::endl; - - return(out.str()); - } -} diff --git a/ecal/core/src/pubsub/ecal_subgate.cpp b/ecal/core/src/pubsub/ecal_subgate.cpp deleted file mode 100644 index 8be9644..0000000 --- a/ecal/core/src/pubsub/ecal_subgate.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL subscriber gateway class -**/ - -#include - -#ifdef ECAL_OS_LINUX -//#include -//#include -#endif - -#include "ecal_def.h" -#include "ecal_descgate.h" - -#include "pubsub/ecal_subgate.h" - -//////////////////////////////////////////////////////// -// local events -//////////////////////////////////////////////////////// -namespace eCAL -{ - ECAL_API const EventHandleT& ShutdownProcEvent() - { - static EventHandleT evt; - static std::string event_name(EVENT_SHUTDOWN_PROC + std::string("_") + std::to_string(Process::GetProcessID())); - if (!gEventIsValid(evt)) - { - gOpenEvent(&evt, event_name); - } - return(evt); - } -} - -namespace eCAL -{ - ////////////////////////////////////////////////////////////////// - // CSubGate - ////////////////////////////////////////////////////////////////// - std::atomic CSubGate::m_created; - - CSubGate::CSubGate() = default; - - CSubGate::~CSubGate() - { - Destroy(); - } - - void CSubGate::Create() - { - if(m_created) return; - - // initialize data reader layers - CDataReader::InitializeLayers(); - - // start timeout thread - m_subtimeout_thread.Start(CMN_DATAREADER_TIMEOUT_DTIME, std::bind(&CSubGate::CheckTimeouts, this)); - m_created = true; - } - - void CSubGate::Destroy() - { - if(!m_created) return; - - // stop timeout thread - m_subtimeout_thread.Stop(); - - // destroy all remaining subscriber - std::unique_lock lock(m_topic_name_datareader_sync); - for (auto iter = m_topic_name_datareader_map.begin(); iter != m_topic_name_datareader_map.end(); ++iter) - { - iter->second->Destroy(); - } - - m_created = false; - } - - bool CSubGate::Register(const std::string& topic_name_, CDataReader* datareader_) - { - if(!m_created) return(false); - - // register reader - std::unique_lock lock(m_topic_name_datareader_sync); - m_topic_name_datareader_map.emplace(std::pair(topic_name_, datareader_)); - - return(true); - } - - bool CSubGate::Unregister(const std::string& topic_name_, CDataReader* datareader_) - { - if(!m_created) return(false); - bool ret_state = false; - - std::unique_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(topic_name_); - for(TopicNameDataReaderMapT::iterator iter = res.first; iter != res.second; ++iter) - { - if(iter->second == datareader_) - { - m_topic_name_datareader_map.erase(iter); - ret_state = true; - break; - } - } - - return(ret_state); - } - - bool CSubGate::HasSample(const std::string& sample_name_) - { - std::shared_lock lock(m_topic_name_datareader_sync); - return(m_topic_name_datareader_map.find(sample_name_) != m_topic_name_datareader_map.end()); - } - - size_t CSubGate::ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_) - { - if(!m_created) return 0; - - size_t sent(0); - switch (ecal_sample_.cmd_type()) - { - case eCAL::pb::bct_set_sample: - { -#ifndef NDEBUG - // check layer - if (layer_ == eCAL::pb::eTLayerType::tl_none) - { - // log it - eCAL::Logging::Log(log_level_error, ecal_sample_.topic().tname() + " : payload received without layer definition !"); - } -#endif - - // update globals - g_process_rclock++; - auto& ecal_sample_content = ecal_sample_.content(); - auto& ecal_sample_content_payload = ecal_sample_content.payload(); - g_process_rbytes_sum += ecal_sample_.content().payload().size(); - - // apply sample to data reader - std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(ecal_sample_.topic().tname()); - for (auto it = res.first; it != res.second; ++it) - { - sent = it->second->AddSample( - ecal_sample_.topic().tid(), - ecal_sample_content_payload.data(), - ecal_sample_content_payload.size(), - ecal_sample_content.id(), - ecal_sample_content.clock(), - ecal_sample_content.time(), - static_cast(ecal_sample_content.hash()), - layer_ - ); - } - } - break; - default: - break; - } - - return sent; - } - - size_t 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_, eCAL::pb::eTLayerType layer_) - { - if(!m_created) return 0; - - // update globals - g_process_rclock++; - g_process_rbytes_sum += len_; - - // apply sample to data reader - size_t sent(0); - std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(topic_name_); - for (auto it = res.first; it != res.second; ++it) - { - sent = it->second->AddSample(topic_id_, buf_, len_, id_, clock_, time_, hash_, layer_); - } - - return sent; - } - - void CSubGate::ApplyLocPubRegistration(const eCAL::pb::Sample& ecal_sample_) - { - if(!m_created) return; - - // check topic name - const auto& ecal_sample_topic = ecal_sample_.topic(); - const std::string& topic_name = ecal_sample_topic.tname(); - if (topic_name.empty()) return; - - const std::string& topic_id = ecal_sample_topic.tid(); - const std::string& topic_type = ecal_sample_topic.ttype(); - const std::string& topic_desc = ecal_sample_topic.tdesc(); - - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PUBLISHER; - - // store description - if (g_descgate()) - g_descgate()->ApplyTopicDescription(topic_name, topic_type, topic_desc, quality); - - // get process id - std::string process_id = std::to_string(ecal_sample_.topic().pid()); - - // handle local publisher connection - std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(topic_name); - for (TopicNameDataReaderMapT::iterator iter = res.first; iter != res.second; ++iter) - { - // apply layer specific parameter - for (auto tlayer : ecal_sample_topic.tlayer()) - { - // layer parameter for local publisher registrations - // --------------------------------------------------------------- - // eCAL version > 5.8.13/5.9.0: - // new layer parameter 'tlayer.par_layer' - // protobuf message is serialized into reader parameter string - // --------------------------------------------------------------- - // eCAL version <= 5.8.13/5.9.0: - // old layer parameter 'tlayer.par_shm' - // contains memory file name for shared memory layer - // the memory file name will be prefixed by '#PAR_SHM#' for - // later check in ecal_reader_shm SetConnectionParameter() - // --------------------------------------------------------------- - - // first check for new behavior - std::string writer_par = tlayer.par_layer().SerializeAsString(); - - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - // if 'tlayer.par_layer' was not used and - // 'tlayer.par_shm' is set - if (writer_par.empty() && !tlayer.par_shm().empty()) - { - writer_par = "#PAR_SHM#" + tlayer.par_shm(); - } - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - - iter->second->ApplyLocLayerParameter(process_id, tlayer.type(), writer_par); - } - // inform for local publisher connection - iter->second->ApplyLocPublication(process_id, topic_id, topic_type, topic_desc); - } - } - - void CSubGate::ApplyExtPubRegistration(const eCAL::pb::Sample& ecal_sample_) - { - if(!m_created) return; - - const auto& sample_topic = ecal_sample_.topic(); - const std::string& host_name = sample_topic.hname(); - const std::string& topic_name = sample_topic.tname(); - const std::string& topic_id = sample_topic.tid(); - const std::string& topic_type = sample_topic.ttype(); - const std::string& topic_desc = sample_topic.tdesc(); - - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PUBLISHER; - - // store description - if (g_descgate()) g_descgate()->ApplyTopicDescription(topic_name, topic_type, topic_desc, quality); - - // handle external publisher connection - std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(topic_name); - for (TopicNameDataReaderMapT::iterator iter = res.first; iter != res.second; ++iter) - { - // apply layer specific parameter - for (auto tlayer : ecal_sample_.topic().tlayer()) - { - // layer parameter as protobuf message - std::string writer_par = tlayer.par_layer().SerializeAsString(); - iter->second->ApplyExtLayerParameter(host_name, tlayer.type(), writer_par); - } - // inform for external publisher connection - iter->second->ApplyExtPublication(host_name, topic_id, topic_type, topic_desc); - } - } - - void CSubGate::RefreshRegistrations() - { - if (!m_created) return; - - // refresh reader registrations - std::shared_lock lock(m_topic_name_datareader_sync); - for (auto iter : m_topic_name_datareader_map) - { - iter.second->RefreshRegistration(); - } - } - - int CSubGate::CheckTimeouts() - { - if (!m_created) return(0); - - // check subscriber timeouts - std::shared_lock lock(m_topic_name_datareader_sync); - for (auto iter = m_topic_name_datareader_map.begin(); iter != m_topic_name_datareader_map.end(); ++iter) - { - iter->second->CheckReceiveTimeout(); - } - - // signal shutdown if eCAL is not okay - bool ecal_is_ok = (g_globals_ctx != nullptr) && !gWaitForEvent(ShutdownProcEvent(), 0); - if (!ecal_is_ok) - { - g_shutdown = 1; - } - - return(0); - } -}; diff --git a/ecal/core/src/readwrite/ecal_reader_iceoryx.cpp b/ecal/core/src/readwrite/ecal_reader_iceoryx.cpp deleted file mode 100644 index d1a366d..0000000 --- a/ecal/core/src/readwrite/ecal_reader_iceoryx.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief shared memory reader and layer (iceoryx) -**/ - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" -#include "ecal_global_accessors.h" -#include "pubsub/ecal_subgate.h" - -#include "ecal/ecal_process.h" -#include "readwrite/ecal_writer_data.h" -#include "readwrite/ecal_reader_iceoryx.h" - -#include - -namespace eCAL -{ - //////////////// - // READER - //////////////// - CDataReaderSHM::CDataReaderSHM() = default; - - bool CDataReaderSHM::Create(const std::string& topic_name_) - { - // store topic name - m_topic_name = topic_name_; - - // subscriber description - const iox::capro::IdString_t service (iox::cxx::TruncateToCapacity, eCALPAR(ICEORYX, SERVICE)); - const iox::capro::IdString_t instance (iox::cxx::TruncateToCapacity, eCALPAR(ICEORYX, INSTANCE)); - const iox::capro::IdString_t event (iox::cxx::TruncateToCapacity, topic_name_); - const iox::capro::ServiceDescription servicedesc(service, instance, event); - - // create subscriber && listener - m_subscriber = std::make_shared(servicedesc); - m_listener = std::make_shared(); - - // attach DATA_RECEIVED event - m_listener - ->attachEvent(*m_subscriber, - iox::popo::SubscriberEvent::DATA_RECEIVED, - iox::popo::createNotificationCallback(onSampleReceivedCallback, *this)) - .or_else([](auto) { - Logging::Log(log_level_fatal, "CDataWriterSHM::CreateIceoryxSub(): Unable to attach subscriber to listener"); - }); - - return true; - } - - bool CDataReaderSHM::Destroy() - { - if(!m_subscriber) return false; - - // detach event and destroy subscriber and listener - m_listener->detachEvent(*m_subscriber, iox::popo::SubscriberEvent::DATA_RECEIVED); - m_subscriber = nullptr; - m_listener = nullptr; - - return true; - } - - void CDataReaderSHM::onSampleReceivedCallback(iox::popo::UntypedSubscriber* subscriber_, CDataReaderSHM* self) - { - subscriber_->take().and_then([subscriber_, self](auto& userPayload) { - // extract data header - auto data_header = static_cast(iox::mepoo::ChunkHeader::fromUserPayload(userPayload)->userHeader()); - // extract data - auto data_payload = static_cast(userPayload); - // apply data to subscriber gate - if (g_subgate()) g_subgate()->ApplySample(self->m_topic_name, /*topic_id_*/ "", data_payload, data_header->len, data_header->id, data_header->clock, data_header->time, data_header->hash, eCAL::pb::eTLayerType::tl_ecal_shm); - // release payload - subscriber_->release(userPayload); - }); - } - - //////////////// - // LAYER - //////////////// - void CSHMReaderLayer::Initialize() - { - // create the runtime for registering with the RouDi daemon - std::string runtime_name = eCAL::Process::GetUnitName() + std::string("_") + std::to_string(eCAL::Process::GetProcessID()); - // replace whitespace characters - std::regex re("[ \t\r\n\f]"); - runtime_name = std::regex_replace(runtime_name, re, "_"); - // initialize runtime - const iox::capro::IdString_t runtime(iox::cxx::TruncateToCapacity, runtime_name); - iox::runtime::PoshRuntime::initRuntime(runtime); - } - - void CSHMReaderLayer::AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& topic_id_, QOS::SReaderQOS /*qos_*/) - { - std::lock_guard lock(m_datareadershm_sync); - if(m_datareadershm_map.find(topic_id_) != m_datareadershm_map.end()) return; - - std::shared_ptr reader = std::make_shared(); - reader->Create(topic_name_); - - m_datareadershm_map.insert(std::pair>(topic_id_, reader)); - } - - void CSHMReaderLayer::RemSubscription(const std::string& /*host_name_*/, const std::string& /*topic_name_*/, const std::string& topic_id_) - { - std::lock_guard lock(m_datareadershm_sync); - DataReaderSHMMapT::iterator iter = m_datareadershm_map.find(topic_id_); - if(iter == m_datareadershm_map.end()) return; - - auto reader = iter->second; - reader->Destroy(); - - m_datareadershm_map.erase(iter); - } -} diff --git a/ecal/core/src/readwrite/ecal_reader_iceoryx.h b/ecal/core/src/readwrite/ecal_reader_iceoryx.h deleted file mode 100644 index 6c2ea51..0000000 --- a/ecal/core/src/readwrite/ecal_reader_iceoryx.h +++ /dev/null @@ -1,77 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief shared memory reader and layer (iceoryx) -**/ - -#pragma once - -#include "readwrite/ecal_reader_layer.h" - -#include -#include - -#include -#include -#include -#include -#include - -namespace eCAL -{ - //////////////// - // READER - //////////////// - class CDataReaderSHM - { - public: - CDataReaderSHM(); - - bool Create(const std::string& topic_name_); - bool Destroy(); - - private: - std::shared_ptr m_subscriber; - std::shared_ptr m_listener; - static void onSampleReceivedCallback(iox::popo::UntypedSubscriber* subscriber_, CDataReaderSHM* self); - - std::string m_topic_name; - }; - - //////////////// - // LAYER - //////////////// - class CSHMReaderLayer : public CReaderLayer - { - public: - CSHMReaderLayer() {} - - void Initialize(); - void AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& topic_id_, QOS::SReaderQOS /*qos_*/); - void RemSubscription(const std::string& /*host_name_*/, const std::string& /*topic_name_*/, const std::string& topic_id_); - - void SetConnectionParameter(SReaderLayerPar& /*par_*/) {} - - private: - typedef std::unordered_map> DataReaderSHMMapT; - std::mutex m_datareadershm_sync; - DataReaderSHMMapT m_datareadershm_map; - }; -} diff --git a/ecal/core/src/readwrite/ecal_reader_shm.cpp b/ecal/core/src/readwrite/ecal_reader_shm.cpp deleted file mode 100644 index a5451a5..0000000 --- a/ecal/core/src/readwrite/ecal_reader_shm.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief shared memory layer -**/ - -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ecal_global_accessors.h" -#include "pubsub/ecal_subgate.h" -#include "io/ecal_memfile_pool.h" -#include "readwrite/ecal_reader_shm.h" - -#include - -namespace eCAL -{ - //////////////// - // LAYER - //////////////// - void CSHMReaderLayer::SetConnectionParameter(SReaderLayerPar& par_) - { - // list of memory file to register - std::vector memfile_names; - - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - // check for old behavior - bool par_shm(false); - const std::string par_shm_prefix("#PAR_SHM#"); - if (par_.parameter.size() > par_shm_prefix.size()) - { - std::string prefix = par_.parameter.substr(0, par_shm_prefix.size()); - if (prefix == par_shm_prefix) - { - std::string memfile_name = par_.parameter.substr(par_shm_prefix.size(), par_.parameter.size()); - memfile_names.push_back(memfile_name); - par_shm = true; - } - } - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - - if (!par_shm) - { - // new behavior (eCAL version > 5.8.13/5.9.0) - // layer parameter google protobuf message - eCAL::pb::ConnnectionPar connection_par; - if (connection_par.ParseFromString(par_.parameter)) - { - for (auto memfile_name : connection_par.layer_par_shm().memory_file_list()) - { - memfile_names.push_back(memfile_name); - } - } - else - { - std::cout << "FATAL ERROR: Could not parse layer connection parameter ! Did you mix up different eCAL versions on the same host ?" << std::endl; - return; - } - } - - for (auto memfile_name : memfile_names) - { - // start memory file receive thread if topic is subscribed in this process - if (g_memfile_pool()) - { - std::string process_id = std::to_string(Process::GetProcessID()); - std::string memfile_event = memfile_name + "_" + process_id; - 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); - } - } - } - - 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 ret_size(0); - if (g_subgate()) ret_size = g_subgate()->ApplySample(topic_name_, topic_id_, buf_, len_, id_, clock_, time_, hash_, eCAL::pb::tl_ecal_shm); - return ret_size; - } -} diff --git a/ecal/core/src/readwrite/ecal_reader_udp_mc.cpp b/ecal/core/src/readwrite/ecal_reader_udp_mc.cpp deleted file mode 100644 index 3446dda..0000000 --- a/ecal/core/src/readwrite/ecal_reader_udp_mc.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief udp multicast reader and layer -**/ - -#include "readwrite/ecal_reader_udp_mc.h" - -#include "ecal_global_accessors.h" -#include "pubsub/ecal_subgate.h" - -#include "io/udp_configurations.h" - -namespace eCAL -{ - //////////////// - // READER - //////////////// - bool CDataReaderUDP::HasSample(const std::string& sample_name_) - { - if (!g_subgate()) return(false); - return(g_subgate()->HasSample(sample_name_)); - } - - size_t CDataReaderUDP::ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_) - { - if (!g_subgate()) return 0; - return g_subgate()->ApplySample(ecal_sample_, layer_); - } - - //////////////// - // LAYER - //////////////// - CUDPReaderLayer::CUDPReaderLayer() : - started(false) - {}; - - CUDPReaderLayer::~CUDPReaderLayer() - { - thread.Stop(); - }; - - void CUDPReaderLayer::Initialize() - { - SReceiverAttr attr; - attr.ipaddr = Config::GetUdpMulticastGroup(); - attr.port = Config::GetUdpMulticastPort() + NET_UDP_MULTICAST_PORT_SAMPLE_OFF; - attr.unicast = false; - attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); - rcv.Create(attr); - } - - void CUDPReaderLayer::AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/, QOS::SReaderQOS /*qos_*/) - { - if (!started) - { - thread.Start(0, std::bind(&CDataReaderUDP::Receive, &reader, &rcv)); - started = true; - } - // add topic name based multicast address - std::string mcast_address = UDP::GetTopicMulticastAddress(topic_name_); - if (topic_name_mcast_map.find(mcast_address) == topic_name_mcast_map.end()) - { - topic_name_mcast_map.emplace(std::pair(mcast_address, 0)); - rcv.AddMultiCastGroup(mcast_address.c_str()); - } - topic_name_mcast_map[mcast_address]++; - } - - void CUDPReaderLayer::RemSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) - { - std::string mcast_address = UDP::GetTopicMulticastAddress(topic_name_); - if (topic_name_mcast_map.find(mcast_address) == topic_name_mcast_map.end()) - { - // this should never happen - } - else - { - topic_name_mcast_map[mcast_address]--; - if (topic_name_mcast_map[mcast_address] == 0) - { - rcv.RemMultiCastGroup(mcast_address.c_str()); - topic_name_mcast_map.erase(mcast_address); - } - } - } -}; diff --git a/ecal/core/src/readwrite/ecal_writer.cpp b/ecal/core/src/readwrite/ecal_writer.cpp deleted file mode 100644 index cc91886..0000000 --- a/ecal/core/src/readwrite/ecal_writer.cpp +++ /dev/null @@ -1,1171 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief common eCAL data writer -**/ - -#include -#include - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" - -#include "ecal_registration_provider.h" -#include "ecal_registration_receiver.h" -#include "pubsub/ecal_pubgate.h" - -#include "ecal_writer.h" -#include "ecal_writer_base.h" -#include "ecal_process.h" - -#include -#include -#include - -struct SSndHash -{ - SSndHash(std::string t, long long c) : topic_id(t), snd_clock(c) {} - std::string topic_id; - long long snd_clock; -}; - -namespace std -{ - template<> - class hash { - public: - size_t operator()(const SSndHash &h) const - { - size_t h1 = std::hash()(h.topic_id); - size_t h2 = std::hash()(h.snd_clock); - return h1 ^ (h2 << 1); - } - }; -} - -namespace eCAL -{ - CDataWriter::CDataWriter() : - m_host_name(Process::GetHostName()), - m_host_id(Process::internal::GetHostID()), - m_pid(Process::GetProcessID()), - m_pname(Process::GetProcessName()), - m_topic_size(0), - m_buffering_shm(PUB_MEMFILE_BUF_COUNT), - m_zero_copy(PUB_MEMFILE_ZERO_COPY), - m_acknowledge_timeout_ms(PUB_MEMFILE_ACK_TO), - m_connected(false), - m_id(0), - m_clock(0), - m_clock_old(0), - m_snd_time(), - m_freq(0), - m_bandwidth_max_udp(NET_BANDWIDTH_MAX_UDP), - m_loc_subscribed(false), - m_ext_subscribed(false), - m_use_udp_mc(Config::GetPublisherUdpMulticastMode()), - m_use_udp_mc_confirmed(false), - m_use_shm(Config::GetPublisherShmMode()), - m_use_shm_confirmed(false), - m_use_tcp(Config::GetPublisherTcpMode()), - m_use_tcp_confirmed(false), - m_use_inproc(Config::GetPublisherInprocMode()), - m_use_inproc_confirmed(false), - m_use_ttype(true), - m_use_tdesc(true), - m_share_ttype(-1), - m_share_tdesc(-1), - m_created(false) - { - } - - CDataWriter::~CDataWriter() - { - Destroy(); - } - - bool CDataWriter::Create(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_) - { - if (m_created) return(false); - - // set defaults - m_topic_name = topic_name_; - m_topic_id.clear(); - m_topic_type = topic_type_; - m_topic_desc = topic_desc_; - m_id = 0; - m_clock = 0; - m_clock_old = 0; - m_snd_time = std::chrono::steady_clock::time_point(); - m_freq = 0; - m_bandwidth_max_udp = Config::GetMaxUdpBandwidthBytesPerSecond(); - m_buffering_shm = Config::GetMemfileBufferCount(); - m_zero_copy = Config::IsMemfileZerocopyEnabled(); - m_acknowledge_timeout_ms = Config::GetMemfileAckTimeoutMs(); - m_connected = false; - m_ext_subscribed = false; - m_created = false; - - // build topic id - std::stringstream counter; - counter << std::chrono::steady_clock::now().time_since_epoch().count(); - m_topic_id = counter.str(); - - // set registration expiration - std::chrono::milliseconds registration_timeout(Config::GetRegistrationTimeoutMs()); - m_loc_sub_map.set_expiration(registration_timeout); - m_ext_sub_map.set_expiration(registration_timeout); - - // allow to share topic type - m_use_ttype = Config::IsTopicTypeSharingEnabled(); - - // allow to share topic description - m_use_tdesc = Config::IsTopicDescriptionSharingEnabled(); - - // register - DoRegister(false); - - // mark as created - m_created = true; - - // create udp multicast layer - SetUseUdpMC(m_use_udp_mc); - - // create shm layer - SetUseShm(m_use_shm); - - // create tcp layer - SetUseTcp(m_use_tcp); - - // create inproc layer - SetUseInProc(m_use_inproc); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Created"); -#endif - - return(true); - } - - bool CDataWriter::Destroy() - { - if (!m_created) return(false); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Destroy"); -#endif - - // destroy udp multicast writer - m_writer_udp_mc.Destroy(); - - // destroy memory file writer - m_writer_shm.Destroy(); - - // destroy inproc writer - m_writer_inproc.Destroy(); - - // reset defaults - m_id = 0; - m_clock = 0; - m_clock_old = 0; - m_snd_time = std::chrono::steady_clock::time_point(); - m_freq = 0; - m_bandwidth_max_udp = Config::GetMaxUdpBandwidthBytesPerSecond(); - m_buffering_shm = Config::GetMemfileBufferCount(); - m_zero_copy = Config::IsMemfileZerocopyEnabled(); - m_acknowledge_timeout_ms = Config::GetMemfileAckTimeoutMs(); - m_connected = false; - - // reset subscriber maps - { - std::lock_guard lock(m_sub_map_sync); - m_loc_sub_map.clear(); - m_ext_sub_map.clear(); - } - - // reset event callback map - { - std::lock_guard lock(m_event_callback_map_sync); - m_event_callback_map.clear(); - } - - m_created = false; - - return(true); - } - - bool CDataWriter::SetTypeName(const std::string& topic_type_name_) - { - bool force = m_topic_type != topic_type_name_; - m_topic_type = topic_type_name_; - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetTypeName"); -#endif - - // register it - DoRegister(force); - - return(true); - } - - bool CDataWriter::SetDescription(const std::string& topic_desc_) - { - bool force = m_topic_desc != topic_desc_; - m_topic_desc = topic_desc_; - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetDescription"); -#endif - - // register it - DoRegister(force); - - return(true); - } - - bool CDataWriter::SetAttribute(const std::string &attr_name_, const std::string& attr_value_) - { - auto current_val = m_attr.find(attr_name_); - - bool force = current_val == m_attr.end() || current_val->second != attr_value_; - m_attr[attr_name_] = attr_value_; - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetAttribute"); -#endif - - // register it - DoRegister(force); - - return(true); - } - - bool CDataWriter::ClearAttribute(const std::string& attr_name_) - { - auto force = m_attr.find(attr_name_) != m_attr.end(); - - m_attr.erase(attr_name_); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ClearAttribute"); -#endif - - // register it - DoRegister(force); - - return(true); - } - - void CDataWriter::ShareType(bool state_) - { - if (state_) - { - m_share_ttype = 1; - } - else - { - m_share_ttype = 0; - } - } - - void CDataWriter::ShareDescription(bool state_) - { - if (state_) - { - m_share_tdesc = 1; - } - else - { - m_share_tdesc = 0; - } - } - - bool CDataWriter::SetQOS(const QOS::SWriterQOS& qos_) - { - m_qos = qos_; - bool ret = true; - ret &= m_writer_shm.SetQOS(qos_); - return ret; - } - - bool CDataWriter::SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_) - { - switch (layer_) - { - case TLayer::tlayer_udp_mc: - SetUseUdpMC(mode_); - break; - case TLayer::tlayer_shm: - SetUseShm(mode_); - break; - case TLayer::tlayer_tcp: - SetUseTcp(mode_); - break; - case TLayer::tlayer_inproc: - SetUseInProc(mode_); - break; - case TLayer::tlayer_all: - SetUseUdpMC (mode_); - SetUseShm (mode_); - SetUseInProc (mode_); - SetUseTcp (mode_); - break; - default: - break; - } - return true; - } - - bool CDataWriter::SetMaxBandwidthUDP(long bandwidth_) - { - m_bandwidth_max_udp = bandwidth_; - return true; - } - - bool CDataWriter::ShmSetBufferCount(long buffering_) - { - if (buffering_ < 1) return false; - m_buffering_shm = static_cast(buffering_); - return true; - } - - bool CDataWriter::ShmEnableZeroCopy(bool state_) - { - m_zero_copy = state_; - return true; - } - - bool CDataWriter::ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_) - { - m_acknowledge_timeout_ms = acknowledge_timeout_ms_; - return true; - } - - long long CDataWriter::ShmGetAcknowledgeTimeout() - { - return m_acknowledge_timeout_ms; - } - - bool CDataWriter::AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_) - { - if (!m_created) return(false); - - // store event callback - { - std::lock_guard lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::AddEventCallback"); -#endif - m_event_callback_map[type_] = callback_; - } - - return(true); - } - - bool CDataWriter::RemEventCallback(eCAL_Publisher_Event type_) - { - if (!m_created) return(false); - - // reset event callback - { - std::lock_guard lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::RemEventCallback"); -#endif - m_event_callback_map[type_] = nullptr; - } - - return(true); - } - - bool CDataWriter::Write(const void* const buf_, size_t len_, long long time_, long long id_) - { - // store id - m_id = id_; - - // handle write counters - RefreshSendCounter(); - - // calculate unique send hash - std::hash hf; - size_t snd_hash = hf(SSndHash(m_topic_id, m_clock)); - - // increase overall sum send - g_process_wbytes_sum += len_; - - // store size for monitoring - m_topic_size = len_; - - // did we write anything - bool written(false); - - // check send modes - TLayer::eSendMode use_udp_mc(m_use_udp_mc); - TLayer::eSendMode use_shm(m_use_shm); - TLayer::eSendMode use_tcp(m_use_tcp); - TLayer::eSendMode use_inproc(m_use_inproc); - if ( (use_udp_mc == TLayer::smode_off) - && (use_shm == TLayer::smode_off) - && (use_tcp == TLayer::smode_off) - && (use_inproc == TLayer::smode_off) - ) - { - // failsafe default mode if - // nothing is activated - use_udp_mc = TLayer::smode_auto; - use_shm = TLayer::smode_auto; - } - - // if we do not have loopback - // enabled we can switch off - // inner process communication - if (g_registration_receiver() && !g_registration_receiver()->LoopBackEnabled()) - { - use_inproc = TLayer::smode_off; - } - - // shared memory transport is on and - // inner process transport is on - // let's check if there is a need for - // shared memory because of external - // process subscription, if not - // let's switch it off - if ((use_shm != TLayer::smode_off) - && (use_inproc != TLayer::smode_off) - ) - { - if (!IsExtSubscribed()) - { - // we have no external subscriptions, - // but we have local ones (otherwise we would have - // no subscriptions and this is checked with !IsSubscribed()) - // so let's check if all local subscriptions are - // "inner process only", that means - // they have all our process id - if (IsInternalSubscribedOnly()) - { - // we can switch shared memory layer off - // it's all subscribed in our process - use_shm = TLayer::smode_off; - } - } - } - - if ( (use_tcp == TLayer::smode_auto) - && (use_udp_mc == TLayer::smode_auto) - ) - { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send: TCP layer and UDP layer are both set to auto mode - Publication failed !"); - return 0; - } - - //////////////////////////////////////////////////////////////////////////// - // LAYER 1 : INPROC - //////////////////////////////////////////////////////////////////////////// - if ((use_inproc == TLayer::smode_auto) - || (use_inproc == TLayer::smode_on) - ) - { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::INPROC"); -#endif - - // send it - bool inproc_sent(false); - { - // fill writer data - struct SWriterData wdata; - wdata.buf = buf_; - wdata.len = len_; - wdata.id = m_id; - wdata.clock = m_clock; - wdata.hash = snd_hash; - wdata.time = time_; - - // prepare send - if (m_writer_inproc.PrepareWrite(wdata)) - { - // register new to update listening subscribers and rematch - DoRegister(true); - Process::SleepMS(5); - } - - // send - inproc_sent = m_writer_inproc.Write(wdata); - m_use_inproc_confirmed = true; - } - written |= inproc_sent; - -#ifndef NDEBUG - // log it - if (inproc_sent) - { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::INPROC - SUCCESS"); - } - else - { - // if "m_writer_inproc.Send" returns 0 it's not a fault, the inner process writer may have no - // subscription and so it will return 0 written bytes - // the other layers will write their bytes in any case on the specific layer - // so we will not handle this as an error - } -#endif - } - - //////////////////////////////////////////////////////////////////////////// - // LAYER 2 : SHM - //////////////////////////////////////////////////////////////////////////// - if (((use_shm == TLayer::smode_auto) && m_loc_subscribed) - || (use_shm == TLayer::smode_on) - ) - { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::SHM"); -#endif - - // send it - bool shm_sent(false); - { - // fill writer data - struct SWriterData wdata; - wdata.buf = buf_; - wdata.len = len_; - wdata.id = m_id; - wdata.clock = m_clock; - wdata.hash = snd_hash; - wdata.time = time_; - wdata.buffering = m_buffering_shm; - wdata.zero_copy = m_zero_copy; - wdata.acknowledge_timeout_ms = m_acknowledge_timeout_ms; - - // prepare send - if (m_writer_shm.PrepareWrite(wdata)) - { - // register new to update listening subscribers and rematch - DoRegister(true); - Process::SleepMS(5); - } - - // send - shm_sent = m_writer_shm.Write(wdata); - m_use_shm_confirmed = true; - } - written |= shm_sent; - -#ifndef NDEBUG - // log it - if (shm_sent) - { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::SHM - SUCCESS"); - } - else - { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::SHM - FAILED"); - } -#endif - } - - //////////////////////////////////////////////////////////////////////////// - // LAYER 3 : UDP (MC) - //////////////////////////////////////////////////////////////////////////// - if (((use_udp_mc == TLayer::smode_auto) && m_ext_subscribed) - || (use_udp_mc == TLayer::smode_on) - ) - { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::UDP_MC"); -#endif - - // send it - bool udp_mc_sent(false); - { - // if shared memory layer for local communication is switched off - // we activate udp message loopback to communicate with local processes too - bool loopback = use_shm == TLayer::smode_off; - - // fill writer data - struct SWriterData wdata; - wdata.buf = buf_; - wdata.len = len_; - wdata.id = m_id; - wdata.clock = m_clock; - wdata.hash = snd_hash; - wdata.time = time_; - wdata.bandwidth = m_bandwidth_max_udp; - wdata.loopback = loopback; - - // prepare send - if (m_writer_udp_mc.PrepareWrite(wdata)) - { - // register new to update listening subscribers and rematch - DoRegister(true); - Process::SleepMS(5); - } - - // send - udp_mc_sent = m_writer_udp_mc.Write(wdata); - m_use_udp_mc_confirmed = true; - } - written |= udp_mc_sent; - -#ifndef NDEBUG - // log it - if (udp_mc_sent) - { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::UDP_MC - SUCCESS"); - } - else - { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::UDP_MC - FAILED"); - } -#endif - } - - //////////////////////////////////////////////////////////////////////////// - // LAYER 4 : TCP - //////////////////////////////////////////////////////////////////////////// - if (((use_tcp == TLayer::smode_auto) && m_ext_subscribed) - || (use_tcp == TLayer::smode_on) - ) - { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::TCP"); -#endif - - // send it - bool tcp_sent(false); - { - // fill writer data - struct SWriterData wdata; - wdata.buf = buf_; - wdata.len = len_; - wdata.id = m_id; - wdata.clock = m_clock; - wdata.hash = snd_hash; - wdata.time = time_; - wdata.buffering = 0; - - // send - tcp_sent = m_writer_tcp.Write(wdata); - m_use_tcp_confirmed = true; - } - written |= tcp_sent; - -#ifndef NDEBUG - // log it - if (tcp_sent) - { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::TCP - SUCCESS"); - } - else - { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::TCP - FAILED"); - } -#endif - } - - // return success - return(written); - } - - void CDataWriter::ApplyLocSubscription(const std::string& process_id_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_, const std::string& reader_par_) - { - Connect(tid_, ttype_, tdesc_); - { - std::lock_guard lock(m_sub_map_sync); - m_loc_sub_map[process_id_] = true; - } - m_loc_subscribed = true; - - // add a new local subscription - m_writer_udp_mc.AddLocConnection (process_id_, reader_par_); - m_writer_shm.AddLocConnection (process_id_, reader_par_); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplyLocSubscription"); -#endif - } - - void CDataWriter::RemoveLocSubscription(const std::string& process_id_) - { - // remove a local subscription - m_writer_udp_mc.RemLocConnection (process_id_); - m_writer_shm.RemLocConnection (process_id_); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::RemoveLocSubscription"); -#endif - } - - void CDataWriter::ApplyExtSubscription(const std::string& host_name_, const std::string& process_id_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_, const std::string& reader_par_) - { - Connect(tid_, ttype_, tdesc_); - { - std::lock_guard lock(m_sub_map_sync); - m_ext_sub_map[host_name_] = true; - } - m_ext_subscribed = true; - - // add a new external subscription - m_writer_udp_mc.AddExtConnection (host_name_, process_id_, reader_par_); - m_writer_shm.AddExtConnection (host_name_, process_id_, reader_par_); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplyExtSubscription"); -#endif - } - - void CDataWriter::RemoveExtSubscription(const std::string& host_name_, const std::string& process_id_) - { - // remove external subscription - m_writer_udp_mc.RemExtConnection (host_name_, process_id_); - m_writer_shm.RemExtConnection (host_name_, process_id_); - } - - void CDataWriter::RefreshRegistration() - { - if (!m_created) return; - - // force to register every second to refresh data clock information - auto curr_time = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast(curr_time - m_snd_time) > std::chrono::milliseconds(0)) - { - // reset clock and time on first call - if (m_clock_old == 0) - { - m_clock_old = m_clock; - m_snd_time = curr_time; - } - - // check for clock difference - if ((m_clock - m_clock_old) > 0) - { - // calculate frequency in mHz - m_freq = static_cast((1000 * 1000 * (m_clock - m_clock_old)) / std::chrono::duration_cast(curr_time - m_snd_time).count()); - // reset clock and time - m_clock_old = m_clock; - m_snd_time = curr_time; - } - else - { - m_freq = 0; - } - } - - // register without send - DoRegister(false); - - // check connection timeouts - std::shared_ptr> loc_timeouts = std::make_shared>(); - { - std::lock_guard lock(m_sub_map_sync); - m_loc_sub_map.remove_deprecated(loc_timeouts.get()); - m_ext_sub_map.remove_deprecated(); - - m_loc_subscribed = !m_loc_sub_map.empty(); - m_ext_subscribed = !m_ext_sub_map.empty(); - } - - for(auto loc_sub : *loc_timeouts) - { - m_writer_shm.RemLocConnection(loc_sub); - } - - if (!m_loc_subscribed && !m_ext_subscribed) - { - Disconnect(); - } - } - - void CDataWriter::RefreshSendCounter() - { - // increase write clock - m_clock++; - - // statistics - g_process_wclock++; - } - - std::string CDataWriter::Dump(const std::string& indent_ /* = "" */) - { - std::stringstream out; - - out << std::endl; - out << indent_ << "--------------------------------" << std::endl; - out << indent_ << " class CDataWriter " << std::endl; - out << indent_ << "--------------------------------" << std::endl; - out << indent_ << "m_host_name: " << m_host_name << std::endl; - out << indent_ << "m_host_id: " << m_host_id << std::endl; - out << indent_ << "m_topic_name: " << m_topic_name << std::endl; - out << indent_ << "m_topic_id: " << m_topic_id << std::endl; - out << indent_ << "m_topic_type: " << m_topic_type << std::endl; - out << indent_ << "m_topic_desc: " << m_topic_desc << std::endl; - out << indent_ << "m_id: " << m_id << std::endl; - out << indent_ << "m_clock: " << m_clock << std::endl; - out << indent_ << "m_created: " << m_created << std::endl; - out << indent_ << "m_loc_subscribed: " << m_loc_subscribed << std::endl; - out << indent_ << "m_ext_subscribed: " << m_ext_subscribed << std::endl; - out << std::endl; - - return(out.str()); - } - - bool CDataWriter::DoRegister(bool force_) - { - if (m_topic_name.empty()) return(false); - - // check share modes - bool share_ttype(m_use_ttype && g_pubgate() && g_pubgate()->TypeShared()); - if (m_share_ttype != -1) - { - share_ttype = m_share_ttype == 1; - } - bool share_tdesc(m_use_tdesc && g_pubgate() && g_pubgate()->DescriptionShared()); - if (m_share_tdesc != -1) - { - share_tdesc = m_share_tdesc == 1; - } - - // create command parameter - eCAL::pb::Sample ecal_reg_sample; - ecal_reg_sample.set_cmd_type(eCAL::pb::bct_reg_publisher); - auto ecal_reg_sample_mutable_topic = ecal_reg_sample.mutable_topic(); - ecal_reg_sample_mutable_topic->set_hname(m_host_name); - ecal_reg_sample_mutable_topic->set_hid(m_host_id); - ecal_reg_sample_mutable_topic->set_tname(m_topic_name); - ecal_reg_sample_mutable_topic->set_tid(m_topic_id); - if (share_ttype) ecal_reg_sample_mutable_topic->set_ttype(m_topic_type); - if (share_tdesc) ecal_reg_sample_mutable_topic->set_tdesc(m_topic_desc); - *ecal_reg_sample_mutable_topic->mutable_attr() = google::protobuf::Map { m_attr.begin(), m_attr.end() }; - ecal_reg_sample_mutable_topic->set_tsize(google::protobuf::int32(m_topic_size)); - // udp multicast layer - { - auto udp_tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - udp_tlayer->set_type(eCAL::pb::tl_ecal_udp_mc); - udp_tlayer->set_version(1); - udp_tlayer->set_confirmed(m_use_udp_mc_confirmed); - udp_tlayer->mutable_par_layer()->ParseFromString(m_writer_udp_mc.GetConnectionParameter()); - } - // shm layer - { - auto shm_tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - shm_tlayer->set_type(eCAL::pb::tl_ecal_shm); - shm_tlayer->set_version(1); - shm_tlayer->set_confirmed(m_use_shm_confirmed); - std::string par_layer_s = m_writer_shm.GetConnectionParameter(); - shm_tlayer->mutable_par_layer()->ParseFromString(par_layer_s); - - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - shm_tlayer->set_par_shm(""); - { - // for downward compatibility eCAL version <= 5.8.13/5.9.0 - // in case of one memory file only we pack the name into 'layer_par_shm()' - eCAL::pb::ConnnectionPar cpar; - cpar.ParseFromString(par_layer_s); - if (cpar.layer_par_shm().memory_file_list_size() == 1) - { - shm_tlayer->set_par_shm(cpar.layer_par_shm().memory_file_list().begin()->c_str()); - } - } - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - - } - // tcp layer - { - auto tcp_tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - tcp_tlayer->set_type(eCAL::pb::tl_ecal_tcp); - tcp_tlayer->set_version(1); - tcp_tlayer->set_confirmed(m_use_tcp_confirmed); - tcp_tlayer->mutable_par_layer()->ParseFromString(m_writer_tcp.GetConnectionParameter()); - } - // inproc layer - { - auto inproc_tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - inproc_tlayer->set_type(eCAL::pb::tl_inproc); - inproc_tlayer->set_version(1); - inproc_tlayer->set_confirmed(m_use_inproc_confirmed); - inproc_tlayer->mutable_par_layer()->ParseFromString(m_writer_inproc.GetConnectionParameter()); - } - ecal_reg_sample_mutable_topic->set_pid(m_pid); - ecal_reg_sample_mutable_topic->set_pname(m_pname); - ecal_reg_sample_mutable_topic->set_uname(Process::GetUnitName()); - ecal_reg_sample_mutable_topic->set_did(m_id); - ecal_reg_sample_mutable_topic->set_dclock(m_clock); - ecal_reg_sample_mutable_topic->set_dfreq(m_freq); - - size_t loc_connections(0); - size_t ext_connections(0); - { - std::lock_guard lock(m_sub_map_sync); - loc_connections = m_loc_sub_map.size(); - ext_connections = m_ext_sub_map.size(); - } - ecal_reg_sample_mutable_topic->set_connections_loc(google::protobuf::int32(loc_connections)); - ecal_reg_sample_mutable_topic->set_connections_ext(google::protobuf::int32(ext_connections)); - - // qos HistoryKind - switch (m_qos.history_kind) - { - case QOS::keep_last_history_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_history(eCAL::pb::QOS::keep_last_history_qos); - break; - case QOS::keep_all_history_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_history(eCAL::pb::QOS::keep_all_history_qos); - break; - default: - break; - } - ecal_reg_sample_mutable_topic->mutable_tqos()->set_history_depth(m_qos.history_kind_depth); - // qos Reliability - switch (m_qos.reliability) - { - case QOS::best_effort_reliability_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_reliability(eCAL::pb::QOS::best_effort_reliability_qos); - break; - case QOS::reliable_reliability_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_reliability(eCAL::pb::QOS::reliable_reliability_qos); - break; - default: - break; - } - - // register publisher - if (g_registration_provider()) g_registration_provider()->RegisterTopic(m_topic_name, m_topic_id, ecal_reg_sample, force_); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::DoRegister"); -#endif - return(true); - } - - void CDataWriter::Connect(const std::string& tid_, const std::string& ttype_, const std::string& tdesc_) - { - SPubEventCallbackData 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 pub_event_connected - auto iter = m_event_callback_map.find(pub_event_connected); - if (iter != m_event_callback_map.end()) - { - data.type = pub_event_connected; - (iter->second)(m_topic_name.c_str(), &data); - } - } - - // fire pub_event_update_connection - auto iter = m_event_callback_map.find(pub_event_update_connection); - if (iter != m_event_callback_map.end()) - { - data.type = pub_event_update_connection; - data.tid = tid_; - data.ttype = ttype_; - data.tdesc = tdesc_; - (iter->second)(m_topic_name.c_str(), &data); - } - } - - void CDataWriter::Disconnect() - { - if (m_connected) - { - m_connected = false; - - // fire pub_event_disconnected - auto iter = m_event_callback_map.find(pub_event_disconnected); - if (iter != m_event_callback_map.end()) - { - 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); - } - } - } - - bool CDataWriter::SetUseUdpMC(TLayer::eSendMode mode_) - { - m_use_udp_mc = mode_; - if (!m_created) return true; - - // log send mode - LogSendMode(m_use_udp_mc, m_topic_name + "::CDataWriter::Create::UDP_MC_SENDMODE::"); - - switch (m_use_udp_mc) - { - case TLayer::eSendMode::smode_auto: - case TLayer::eSendMode::smode_on: - m_writer_udp_mc.Create(m_host_name, m_topic_name, m_topic_id); -#ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::UDP_MC_WRITER"); -#endif - break; - case TLayer::eSendMode::smode_none: - case TLayer::eSendMode::smode_off: - m_writer_udp_mc.Destroy(); - break; - } - - return(true); - } - - bool CDataWriter::SetUseShm(TLayer::eSendMode mode_) - { - m_use_shm = mode_; - if (!m_created) return true; - - // log send mode - LogSendMode(m_use_shm, m_topic_name + "::CDataWriter::Create::SHM_SENDMODE::"); - - switch (m_use_shm) - { - case TLayer::eSendMode::smode_auto: - case TLayer::eSendMode::smode_on: - m_writer_shm.Create(m_host_name, m_topic_name, m_topic_id); -#ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::SHM_WRITER"); -#endif - break; - case TLayer::eSendMode::smode_none: - case TLayer::eSendMode::smode_off: - m_writer_shm.Destroy(); - break; - } - - return(true); - } - - bool CDataWriter::SetUseTcp(TLayer::eSendMode mode_) - { - m_use_tcp = mode_; - if (!m_created) return true; - - // log send mode - LogSendMode(m_use_tcp, m_topic_name + "::CDataWriter::Create::TCP_SENDMODE::"); - - switch (m_use_tcp) - { - case TLayer::eSendMode::smode_auto: - case TLayer::eSendMode::smode_on: - m_writer_tcp.Create(m_host_name, m_topic_name, m_topic_id); -#ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::TCP_WRITER"); -#endif - break; - case TLayer::eSendMode::smode_none: - case TLayer::eSendMode::smode_off: - m_writer_tcp.Destroy(); - break; - } - - return(true); - } - - bool CDataWriter::SetUseInProc(TLayer::eSendMode mode_) - { - m_use_inproc = mode_; - if (!m_created) return true; - - // log send mode - LogSendMode(m_use_inproc, m_topic_name + "::CDataWriter::Create::INPROC_SENDMODE::"); - - switch (m_use_inproc) - { - case TLayer::eSendMode::smode_auto: - case TLayer::eSendMode::smode_on: - m_writer_inproc.Create(m_host_name, m_topic_name, m_topic_id); -#ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::INPROC_WRITER"); -#endif - break; - default: - m_writer_inproc.Destroy(); - break; - } - - return(true); - } - - bool CDataWriter::IsInternalSubscribedOnly() - { - std::string process_id = Process::GetProcessIDAsString(); - bool is_internal_only(true); - std::lock_guard lock(m_sub_map_sync); - for (auto sub : m_loc_sub_map) - { - if (sub.first != process_id) - { - is_internal_only = false; - break; - } - } - return is_internal_only; - } - - void CDataWriter::LogSendMode(TLayer::eSendMode smode_, const std::string& base_msg_) - { -#ifndef NDEBUG - switch (smode_) - { - case TLayer::eSendMode::smode_none: - Logging::Log(log_level_debug4, base_msg_ + "NONE"); - break; - case TLayer::eSendMode::smode_auto: - Logging::Log(log_level_debug4, base_msg_ + "AUTO"); - break; - case TLayer::eSendMode::smode_on: - Logging::Log(log_level_debug4, base_msg_ + "ON"); - break; - case TLayer::eSendMode::smode_off: - Logging::Log(log_level_debug4, base_msg_ + "OFF"); - break; - } -#else - (void)smode_; - (void)base_msg_; -#endif - } -} diff --git a/ecal/core/src/readwrite/ecal_writer.h b/ecal/core/src/readwrite/ecal_writer.h deleted file mode 100644 index 2c8c24d..0000000 --- a/ecal/core/src/readwrite/ecal_writer.h +++ /dev/null @@ -1,185 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief common eCAL data writer -**/ - -#pragma once - -#include -#include - -#include "ecal_def.h" -#include "ecal_expmap.h" - -#include "ecal_writer_udp_mc.h" - -#ifdef ECAL_LAYER_ICEORYX -#include "ecal_writer_iceoryx.h" -#else /* ECAL_LAYER_ICEORYX */ -#include "ecal_writer_shm.h" -#endif /* ECAL_LAYER_ICEORYX */ - -#include "ecal_writer_tcp.h" -#include "ecal_writer_inproc.h" - -#include -#include -#include -#include - -namespace eCAL -{ - class CDataWriter - { - public: - CDataWriter(); - ~CDataWriter(); - - bool Create(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_); - bool Destroy(); - - bool SetTypeName(const std::string& topic_type_name_); - bool SetDescription(const std::string& topic_desc_); - - bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); - bool ClearAttribute(const std::string& attr_name_); - - void ShareType(bool state_); - void ShareDescription(bool state_); - - bool SetQOS(const QOS::SWriterQOS& qos_); - - bool SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_); - bool SetMaxBandwidthUDP(long bandwidth_); - - bool ShmSetBufferCount(long buffering_); - bool ShmEnableZeroCopy(bool state_); - - bool ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_); - long long ShmGetAcknowledgeTimeout(); - - bool AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_); - bool RemEventCallback(eCAL_Publisher_Event type_); - - bool Write(const void* const buf_, size_t len_, long long time_, long long id_); - - void ApplyLocSubscription(const std::string& process_id_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_, const std::string& reader_par_); - void RemoveLocSubscription(const std::string & process_id_); - - void ApplyExtSubscription(const std::string& host_name_, const std::string& process_id_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_, const std::string& reader_par_); - void RemoveExtSubscription(const std::string & host_name_, const std::string & process_id_); - - void RefreshRegistration(); - void RefreshSendCounter(); - - std::string Dump(const std::string& indent_ = ""); - - bool IsCreated() const {return(m_created);} - bool IsSubscribed() const {return(m_loc_subscribed || m_ext_subscribed);} - bool IsExtSubscribed() const {return(m_ext_subscribed);} - size_t GetSubscriberCount() const - { - std::lock_guard lock(m_sub_map_sync); - return(m_loc_sub_map.size() + m_ext_sub_map.size()); - } - - const std::string& GetTopicName() const {return(m_topic_name);} - const std::string& GetTopicID() const {return(m_topic_id);} - const std::string& GetTypeName() const {return(m_topic_type);} - const std::string& GetDescription() const {return(m_topic_desc);} - long long GetClock() const {return(m_clock);} - long GetFrequency() const {return(m_freq);} - - protected: - bool DoRegister(bool force_); - void Connect(const std::string& tid_, const std::string& ttype_, const std::string& tdesc_); - void Disconnect(); - - bool SetUseUdpMC(TLayer::eSendMode mode_); - bool SetUseShm(TLayer::eSendMode mode_); - bool SetUseTcp(TLayer::eSendMode mode_); - bool SetUseInProc(TLayer::eSendMode mode_); - - bool IsInternalSubscribedOnly(); - - void LogSendMode(TLayer::eSendMode smode_, const std::string & base_msg_); - - std::string m_host_name; - int m_host_id; - int m_pid; - std::string m_pname; - std::string m_topic_name; - std::string m_topic_id; - std::string m_topic_type; - std::string m_topic_desc; - std::map m_attr; - size_t m_topic_size; - - QOS::SWriterQOS m_qos; - - size_t m_buffering_shm; - bool m_zero_copy; - long long m_acknowledge_timeout_ms; - - std::atomic m_connected; - typedef Util::CExpMap ConnectedMapT; - mutable std::mutex m_sub_map_sync; - ConnectedMapT m_loc_sub_map; - ConnectedMapT m_ext_sub_map; - - std::mutex m_event_callback_map_sync; - typedef std::map EventCallbackMapT; - EventCallbackMapT m_event_callback_map; - - long long m_id; - long long m_clock; - long long m_clock_old; - std::chrono::steady_clock::time_point m_snd_time; - long m_freq; - - long m_bandwidth_max_udp; - - std::atomic m_loc_subscribed; - std::atomic m_ext_subscribed; - - TLayer::eSendMode m_use_udp_mc; - CDataWriterUdpMC m_writer_udp_mc; - bool m_use_udp_mc_confirmed; - - TLayer::eSendMode m_use_shm; - CDataWriterSHM m_writer_shm; - bool m_use_shm_confirmed; - - TLayer::eSendMode m_use_tcp; - CDataWriterTCP m_writer_tcp; - bool m_use_tcp_confirmed; - - TLayer::eSendMode m_use_inproc; - CDataWriterInProc m_writer_inproc; - bool m_use_inproc_confirmed; - - bool m_use_ttype; - bool m_use_tdesc; - int m_share_ttype; - int m_share_tdesc; - bool m_created; - }; -} diff --git a/ecal/core/src/readwrite/ecal_writer_iceoryx.cpp b/ecal/core/src/readwrite/ecal_writer_iceoryx.cpp deleted file mode 100644 index 46af664..0000000 --- a/ecal/core/src/readwrite/ecal_writer_iceoryx.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief shared memory (iceoryx) writer -**/ - -#include -#include - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" -#include -#include "ecal/ecal_process.h" -#include "readwrite/ecal_writer_iceoryx.h" - -#include - -namespace eCAL -{ - CDataWriterSHM::CDataWriterSHM() - { - // create the runtime for registering with the RouDi daemon - std::string runtime_name = eCAL::Process::GetUnitName() + std::string("_") + std::to_string(eCAL::Process::GetProcessID()); - // replace whitespace characters - std::regex re("[ \t\r\n\f]"); - runtime_name = std::regex_replace(runtime_name, re, "_"); - // initialize runtime - const iox::capro::IdString_t runtime (iox::cxx::TruncateToCapacity, runtime_name); - iox::runtime::PoshRuntime::initRuntime(runtime); - } - - CDataWriterSHM::~CDataWriterSHM() - { - Destroy(); - } - - SWriterInfo CDataWriterSHM::GetInfo() - { - SWriterInfo info_; - - info_.name = "iceoryx"; - info_.description = "Iceoryx data writer"; - - info_.has_mode_local = true; - info_.has_mode_cloud = false; - - info_.has_qos_history_kind = false; - info_.has_qos_reliability = false; - - info_.send_size_max = -1; - - return info_; - } - - bool CDataWriterSHM::Create(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) - { - // publisher description - const iox::capro::IdString_t service (iox::cxx::TruncateToCapacity, eCALPAR(ICEORYX, SERVICE)); - const iox::capro::IdString_t instance (iox::cxx::TruncateToCapacity, eCALPAR(ICEORYX, INSTANCE)); - const iox::capro::IdString_t event (iox::cxx::TruncateToCapacity, topic_name_); - const iox::capro::ServiceDescription servicedesc(service, instance, event); - - // create publisher - m_publisher = std::make_shared(servicedesc); - - return true; - } - - bool CDataWriterSHM::Destroy() - { - if(!m_publisher) return false; - - // destroy publisher - m_publisher = nullptr; - - return true; - } - - bool CDataWriterSHM::Write(const SWriterData& data_) - { - if (!m_publisher) return false; - bool ret(false); - - uint32_t payload_size(static_cast(data_.len)); - uint32_t payload_alignment(static_cast(alignof(void*))); - uint32_t header_size(static_cast(sizeof(data_))); - uint32_t header_alignment(static_cast(alignof(SWriterData))); - - m_publisher->loan(payload_size, payload_alignment, header_size, header_alignment) - .and_then([&](auto& userPayload) { - // loan successful - // copy payload header - std::memcpy(iox::mepoo::ChunkHeader::fromUserPayload(userPayload)->userHeader(), &data_, sizeof(SWriterData)); - // copy payload data - std::memcpy(static_cast(userPayload), data_.buf, data_.len); - // publish all - m_publisher->publish(userPayload); - ret = true; - }).or_else([&](auto& error) { - // loan failed - std::stringstream ss; - ss << "CDataWriterSHM::Send(): Loan of iceoryx chunk failed ! Error code: " << static_cast(error); - Logging::Log(log_level_fatal, ss.str()); - }); - - return ret; - } -} diff --git a/ecal/core/src/readwrite/ecal_writer_inproc.cpp b/ecal/core/src/readwrite/ecal_writer_inproc.cpp deleted file mode 100644 index 5885542..0000000 --- a/ecal/core/src/readwrite/ecal_writer_inproc.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief inproc data writer -**/ - -#include -#include - -#include "ecal_global_accessors.h" -#include "pubsub/ecal_subgate.h" -#include "readwrite/ecal_writer_inproc.h" - -namespace eCAL -{ - CDataWriterInProc::~CDataWriterInProc() - { - Destroy(); - } - - SWriterInfo CDataWriterInProc::GetInfo() - { - SWriterInfo info_; - - info_.name = "inproc"; - info_.description = "InProc data writer"; - - info_.has_mode_local = true; - info_.has_mode_cloud = false; - - info_.has_qos_history_kind = false; - info_.has_qos_reliability = true; - - info_.send_size_max = -1; - - return info_; - } - - bool CDataWriterInProc::Create(const std::string & host_name_, const std::string & topic_name_, const std::string & topic_id_) - { - if (m_created) return false; - - m_host_name = host_name_; - m_topic_name = topic_name_; - m_topic_id = topic_id_; - - m_created = true; - return true; - } - - bool CDataWriterInProc::Destroy() - { - if (!m_created) return false; - m_created = false; - - return true; - } - - ///////////////////////////////////////////////////////////////// - // apply the data straight to the subscriber gate - ///////////////////////////////////////////////////////////////// - bool CDataWriterInProc::Write(const SWriterData& data_) - { - if (!m_created) return(false); - if (!g_subgate()) return(false); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriterInProc::Send"); -#endif - - // send it - // no need to interpret return value 0 as error - // maybe no one is subscribing in the current process - size_t sent = g_subgate()->ApplySample(m_topic_name, m_topic_id, static_cast(data_.buf), data_.len, data_.id, data_.clock, data_.time, data_.hash, eCAL::pb::tl_inproc); - - return (sent != 0); - } -} diff --git a/ecal/core/src/readwrite/ecal_writer_shm.cpp b/ecal/core/src/readwrite/ecal_writer_shm.cpp deleted file mode 100644 index 5883660..0000000 --- a/ecal/core/src/readwrite/ecal_writer_shm.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief memory file data writer -**/ - -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" -#include "ecal_writer.h" -#include "ecal_writer_shm.h" - -namespace eCAL -{ - const std::string CDataWriterSHM::m_memfile_base_name = "ecal_"; - - CDataWriterSHM::CDataWriterSHM() - { - } - - CDataWriterSHM::~CDataWriterSHM() - { - Destroy(); - } - - SWriterInfo CDataWriterSHM::GetInfo() - { - SWriterInfo info_; - - info_.name = "shm"; - info_.description = "Local shared memory data writer"; - - info_.has_mode_local = true; - info_.has_mode_cloud = false; - - info_.has_qos_history_kind = false; - info_.has_qos_reliability = true; - - info_.send_size_max = -1; - - return info_; - } - - bool CDataWriterSHM::Create(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string & /*topic_id_*/) - { - if (m_created) return false; - m_topic_name = topic_name_; - - // init write index and create memory files - m_write_idx = 0; - - // set attributes - m_memory_file_attr.min_size = Config::GetMemfileMinsizeBytes(); - m_memory_file_attr.reserve = Config::GetMemfileOverprovisioningPercentage(); - m_memory_file_attr.timeout_open_ms = PUB_MEMFILE_OPEN_TO; - m_memory_file_attr.timeout_ack_ms = Config::GetMemfileAckTimeoutMs(); - - // create the files - for (size_t num(0); num < m_buffer_count; ++num) - { - auto sync_memfile = std::make_shared(m_memfile_base_name, 0, m_memory_file_attr); - m_memory_file_vec.push_back(sync_memfile); - } - - m_created = true; - return m_created; - } - - bool CDataWriterSHM::Destroy() - { - if (!m_created) return false; - m_created = false; - - m_memory_file_vec.clear(); - - return true; - } - - bool CDataWriterSHM::SetQOS(const QOS::SWriterQOS& qos_) - { - m_qos = qos_; - return true; - } - - bool CDataWriterSHM::PrepareWrite(const SWriterData& data_) - { - if (!m_created) return false; - if (data_.len == 0) return false; - - // false signals no rematching / exchanging of - // connection parameters needed - bool ret_state(false); - - // check number of requested memory file buffer - if (data_.buffering != m_buffer_count) - { - // store new size and flag change - m_buffer_count = data_.buffering; - ret_state |= true; - - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - // recreate memory buffer list to stay compatible to older versions - // for the case that we have ONE existing buffer - // and that single buffer is communicated with an older shm datareader - // in this case we need to invalidate (destroy) the existing buffer - // and the old datareader will get blind (fail safe) - // otherwise it would still receive every n-th write - // this state change will lead to some lost samples - if ((m_memory_file_vec.size() == 1) && (m_memory_file_vec.size() < m_buffer_count)) - { - m_memory_file_vec.clear(); - } - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - - // increase buffer count - while (m_memory_file_vec.size() < m_buffer_count) - { - auto sync_memfile = std::make_shared(m_memfile_base_name, data_.len, m_memory_file_attr); - m_memory_file_vec.push_back(sync_memfile); - } - // decrease buffer count - while (m_memory_file_vec.size() > m_buffer_count) - { - m_memory_file_vec.pop_back(); - } - } - - // adapt write index if needed - m_write_idx %= m_memory_file_vec.size(); - - // check size and reserve new if needed - ret_state |= m_memory_file_vec[m_write_idx]->CheckSize(data_.len); - - return ret_state; - } - - bool CDataWriterSHM::Write(const SWriterData& data_) - { - if (!m_created) return 0; - - // write content - bool sent = m_memory_file_vec[m_write_idx]->Write(data_); - - // and increment file index - m_write_idx++; - m_write_idx %= m_memory_file_vec.size(); - - return sent; - } - - bool CDataWriterSHM::AddLocConnection(const std::string& process_id_, const std::string& /*conn_par_*/) - { - if (!m_created) return false; - - for (auto& memory_file : m_memory_file_vec) - { - if (!memory_file->Connect(process_id_)) - { - return false; - } - } - - return true; - } - - bool CDataWriterSHM::RemLocConnection(const std::string& process_id_) - { - if (!m_created) return false; - - for (auto& memory_file : m_memory_file_vec) - { - if (!memory_file->Disconnect(process_id_)) - { - return false; - } - } - - return true; - } - - std::string CDataWriterSHM::GetConnectionParameter() - { - // starting from eCAL version > 5.8.13/5.9.0 the ConnectionParameter is defined as google protobuf - eCAL::pb::ConnnectionPar connection_par; - for (auto& memory_file : m_memory_file_vec) - { - connection_par.mutable_layer_par_shm()->add_memory_file_list(memory_file->GetName()); - } - return connection_par.SerializeAsString(); - } -} diff --git a/ecal/core/src/readwrite/ecal_writer_udp_mc.cpp b/ecal/core/src/readwrite/ecal_writer_udp_mc.cpp deleted file mode 100644 index e5a7c9f..0000000 --- a/ecal/core/src/readwrite/ecal_writer_udp_mc.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief udp data writer -**/ - -#include -#include -#include - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" -#include "ecal_writer_udp_mc.h" - -#include "io/udp_configurations.h" -#include "io/snd_sample.h" - -namespace eCAL -{ - CDataWriterUdpMC::~CDataWriterUdpMC() - { - Destroy(); - } - - SWriterInfo CDataWriterUdpMC::GetInfo() - { - SWriterInfo info_; - - info_.name = "udp"; - info_.description = "UDP multicast data writer"; - - info_.has_mode_local = true; - info_.has_mode_cloud = true; - - info_.has_qos_history_kind = false; - info_.has_qos_reliability = false; - - info_.send_size_max = -1; - - return info_; - } - - bool CDataWriterUdpMC::Create(const std::string & host_name_, const std::string & topic_name_, const std::string & topic_id_) - { - if (m_created) return false; - - m_host_name = host_name_; - m_topic_name = topic_name_; - m_topic_id = topic_id_; - - m_udp_ipaddr = UDP::GetTopicMulticastAddress(topic_name_); - - SSenderAttr attr; - attr.ipaddr = m_udp_ipaddr; - attr.port = Config::GetUdpMulticastPort() + NET_UDP_MULTICAST_PORT_SAMPLE_OFF; - attr.unicast = false; - attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); - - // create sample sender without activated loopback - attr.loopback = false; - attr.ttl = Config::GetUdpMulticastTtl(); - m_sample_snd_no_loopback.Create(attr); - - // create sample sender with activated loopback - int ttl(0); - if (Config::IsNetworkEnabled()) ttl = Config::GetUdpMulticastTtl(); - attr.loopback = true; - attr.ttl = ttl; - m_sample_snd_loopback.Create(attr); - - m_created = true; - return true; - } - - bool CDataWriterUdpMC::Destroy() - { - if (!m_created) return false; - - m_created = false; - return true; - } - - bool CDataWriterUdpMC::Write(const SWriterData& data_) - { - if (!m_created) return false; - - // create new sample - m_ecal_sample.Clear(); - m_ecal_sample.set_cmd_type(eCAL::pb::bct_set_sample); - auto ecal_sample_mutable_topic = m_ecal_sample.mutable_topic(); - ecal_sample_mutable_topic->set_hname(m_host_name); - ecal_sample_mutable_topic->set_tname(m_topic_name); - ecal_sample_mutable_topic->set_tid(m_topic_id); - - // set layer - auto layer = ecal_sample_mutable_topic->add_tlayer(); - layer->set_type(eCAL::pb::eTLayerType::tl_ecal_udp_mc); - layer->set_confirmed(true); - - // append content - auto ecal_sample_mutable_content = m_ecal_sample.mutable_content(); - ecal_sample_mutable_content->set_id(data_.id); - ecal_sample_mutable_content->set_clock(data_.clock); - ecal_sample_mutable_content->set_time(data_.time); - ecal_sample_mutable_content->set_hash(data_.hash); - ecal_sample_mutable_content->set_size((google::protobuf::int32)data_.len); - ecal_sample_mutable_content->set_payload(data_.buf, data_.len); - - // send it - size_t sent = 0; - if (data_.loopback) - { - sent = eCAL::SendSample(&m_sample_snd_loopback, m_ecal_sample.topic().tname(), m_ecal_sample, m_udp_ipaddr, data_.bandwidth); - } - else - { - sent = eCAL::SendSample(&m_sample_snd_no_loopback, m_ecal_sample.topic().tname(), m_ecal_sample, m_udp_ipaddr, data_.bandwidth); - } - - // log it - if (sent == 0) - { - Logging::Log(log_level_fatal, "CDataWriterUDP::Send failed to send message !"); - } - - return(sent > 0); - } -} diff --git a/ecal/core/src/readwrite/ecal_writer_udp_mc.h b/ecal/core/src/readwrite/ecal_writer_udp_mc.h deleted file mode 100644 index 985768f..0000000 --- a/ecal/core/src/readwrite/ecal_writer_udp_mc.h +++ /dev/null @@ -1,64 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief udp data writer -**/ - -#pragma once - -#include "ecal_def.h" - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "io/udp_sender.h" -#include "readwrite/ecal_writer_base.h" - -#include - -namespace eCAL -{ - class CDataWriterUdpMC : public CDataWriterBase - { - public: - ~CDataWriterUdpMC(); - - SWriterInfo GetInfo() override; - - bool Create(const std::string& host_name_, const std::string& topic_name_, const std::string & topic_id_) override; - // this virtual function is called during construction/destruction, - // so, mark it as final to ensure that no derived classes override it. - bool Destroy() final override; - - bool Write(const SWriterData& data_) override; - - protected: - std::string m_udp_ipaddr; - eCAL::pb::Sample m_ecal_sample; - - CUDPSender m_sample_snd_loopback; - CUDPSender m_sample_snd_no_loopback; - }; -} diff --git a/ecal/core/src/service/asio_server.h b/ecal/core/src/service/asio_server.h deleted file mode 100644 index 5b83dbc..0000000 --- a/ecal/core/src/service/asio_server.h +++ /dev/null @@ -1,276 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4834) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ecal_tcpheader.h" -#include "ecal/cimpl/ecal_callback_cimpl.h" - -typedef std::function RequestCallbackT; -typedef std::function EventCallbackT; - -class CAsioSession -{ -public: - CAsioSession(asio::io_service& io_service) - : socket_(io_service), data_{} - { - } - - asio::ip::tcp::socket& socket() - { - return socket_; - } - - void start() - { - socket_.async_read_some(asio::buffer(data_, max_length), - std::bind(&CAsioSession::handle_read, this, - std::placeholders::_1, - std::placeholders::_2)); - } - - void add_request_callback1(RequestCallbackT callback_) - { - request_callback_ = callback_; - } - - void add_event_callback(EventCallbackT callback_) - { - event_callback_ = callback_; - } - -private: - void handle_read(const asio::error_code& ec, - size_t bytes_transferred) - { - if (!ec) - { - if (request_callback_) - { - // collect request - //std::cout << "CAsioSession::handle_read read bytes " << bytes_transferred << std::endl; - request_ += std::string(data_, bytes_transferred); - // are there some more data on the socket ? - if (socket_.available()) - { - // read some more bytes - socket_.async_read_some(asio::buffer(data_, max_length), - std::bind(&CAsioSession::handle_read, this, - std::placeholders::_1, - std::placeholders::_2)); - } - // no more data - else - { - // execute service callback - //std::cout << "CAsioSession::handle_read final request size " << request_.size() << std::endl; - response_.clear(); - request_callback_(request_, response_); - request_.clear(); - //std::cout << "CAsioSession::handle_read server callback executed - reponse size " << response_.size() << std::endl; - - // write response back - packed_response_.clear(); - packed_response_ = pack_write(response_); - asio::async_write(socket_, - asio::buffer(packed_response_.data(), packed_response_.size()), - bind(&CAsioSession::handle_write, this, - std::placeholders::_1, - std::placeholders::_2)); - } - } - } - else - { - if ((ec == asio::error::eof) || - (ec == asio::error::connection_reset)) - { - // handle the disconnect - if (event_callback_) - { - event_callback_(server_event_disconnected, "CAsioSession disconnected on read"); - } - } - delete this; - } - } - - std::vector pack_write(const std::string& response) - { - // create header - eCAL::STcpHeader tcp_header; - // set up package size - const size_t psize = response.size(); - tcp_header.psize_n = htonl(static_cast(psize)); - // repack - std::vector packed_response(sizeof(tcp_header) + psize); - memcpy(packed_response.data(), &tcp_header, sizeof(tcp_header)); - memcpy(packed_response.data() + sizeof(tcp_header), response.data(), psize); - return packed_response; - } - - void handle_write(const asio::error_code& ec, std::size_t /*bytes_transferred*/) - { - if (!ec) - { - //std::cout << "CAsioSession::handle_write bytes sent " << bytes_transferred << std::endl; - socket_.async_read_some(asio::buffer(data_, max_length), - std::bind(&CAsioSession::handle_read, this, - std::placeholders::_1, - std::placeholders::_2)); - } - else - { - if ((ec == asio::error::eof) || - (ec == asio::error::connection_reset)) - { - // handle the disconnect - if (event_callback_) - { - event_callback_(server_event_disconnected, "CAsioSession disconnected on write"); - } - } - delete this; - } - } - - asio::ip::tcp::socket socket_; - RequestCallbackT request_callback_; - EventCallbackT event_callback_; - std::string request_; - std::string response_; - std::vector packed_response_; - - enum { max_length = 64 * 1024 }; - char data_[max_length]; -}; - -class CAsioServer -{ -public: - CAsioServer(asio::io_service& io_service, unsigned short port) - : io_service_(io_service), - acceptor_(io_service, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)), - connect_cnt_(0) - { - start_accept(); - } - - bool is_connected() - { - return (connect_cnt_ > 0); - } - - void add_request_callback1(RequestCallbackT callback) - { - request_cb_ = callback; - } - - void add_event_callback(EventCallbackT callback) - { - event_cb_ = callback; - } - - uint16_t get_port() - { - return acceptor_.local_endpoint().port(); - } - -private: - void start_accept() - { - CAsioSession* new_session = new CAsioSession(io_service_); - acceptor_.async_accept(new_session->socket(), - std::bind(&CAsioServer::handle_accept, this, new_session, - std::placeholders::_1)); - } - - void handle_accept(CAsioSession* new_session, - const asio::error_code& ec) - { - if (!ec) - { - // handle the connect - connect_cnt_++; - if (event_cb_) - { - event_cb_(server_event_connected, "CAsioSession connected"); - } - - new_session->start(); - new_session->add_request_callback1(std::bind(&CAsioServer::on_request, this, std::placeholders::_1, std::placeholders::_2)); - new_session->add_event_callback(std::bind(&CAsioServer::on_event, this, std::placeholders::_1, std::placeholders::_2)); - } - else - { - delete new_session; - } - - start_accept(); - } - - int on_request(const std::string& request, std::string& response) - { - if (request_cb_) - { - return request_cb_(request, response); - } - return 0; - } - - void on_event(eCAL_Server_Event event, const std::string& message) - { - switch (event) - { - case server_event_connected: - connect_cnt_++; - break; - case server_event_disconnected: - connect_cnt_--; - break; - default: - break; - } - - if (event_cb_) - { - event_cb_(event, message); - } - } - - asio::io_service& io_service_; - asio::ip::tcp::acceptor acceptor_; - RequestCallbackT request_cb_; - EventCallbackT event_cb_; - int connect_cnt_; -}; diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp deleted file mode 100644 index d8e091a..0000000 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ /dev/null @@ -1,628 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL service client implementation -**/ - -#include "ecal_global_accessors.h" - -#include "ecal_registration_provider.h" -#include "ecal_clientgate.h" -#include "ecal_service_client_impl.h" - -#include -#include - -namespace eCAL -{ - /** - * @brief Service client implementation class. - **/ - CServiceClientImpl::CServiceClientImpl() : - m_response_callback(nullptr), - m_created(false) - { - } - - CServiceClientImpl::CServiceClientImpl(const std::string& service_name_) : - m_response_callback(nullptr), - m_created(false) - { - Create(service_name_); - } - - CServiceClientImpl::~CServiceClientImpl() - { - Destroy(); - } - - bool CServiceClientImpl::Create(const std::string& service_name_) - { - if (m_created) return(false); - - // set service name - m_service_name = service_name_; - - // create service id - std::stringstream counter; - counter << std::chrono::steady_clock::now().time_since_epoch().count(); - m_service_id = counter.str(); - - if (g_clientgate()) g_clientgate()->Register(this); - - m_created = true; - - return(true); - } - - bool CServiceClientImpl::Destroy() - { - if (!m_created) return(false); - - if (g_clientgate()) g_clientgate()->Unregister(this); - if (g_registration_provider()) g_registration_provider()->UnregisterClient(m_service_name, m_service_id); - - // reset client map - { - std::lock_guard lock(m_client_map_sync); - m_client_map.clear(); - } - - // reset method callback map - { - std::lock_guard lock(m_response_callback_sync); - m_response_callback = nullptr; - } - - // reset event callback map - { - std::lock_guard lock(m_event_callback_map_sync); - m_event_callback_map.clear(); - } - - m_service_name.clear(); - m_service_id.clear(); - m_host_name.clear(); - - m_created = false; - - return(true); - } - - bool CServiceClientImpl::SetHostName(const std::string& host_name_) - { - if (host_name_ == "*") m_host_name.clear(); - else m_host_name = host_name_; - return(true); - } - - // add callback function for service response - bool CServiceClientImpl::AddResponseCallback(const ResponseCallbackT& callback_) - { - std::lock_guard lock(m_response_callback_sync); - m_response_callback = callback_; - return true; - } - - // remove callback function for service response - bool CServiceClientImpl::RemResponseCallback() - { - std::lock_guard lock(m_response_callback_sync); - m_response_callback = nullptr; - return true; - } - - // add callback function for client events - bool CServiceClientImpl::AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_) - { - if (!m_created) return false; - - // store event callback - { - std::lock_guard lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceClientImpl::AddEventCallback"); -#endif - m_event_callback_map[type_] = callback_; - } - - return true; - } - - // remove callback function for client events - bool CServiceClientImpl::RemEventCallback(eCAL_Client_Event type_) - { - if (!m_created) return false; - - // reset event callback - { - std::lock_guard lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceClientImpl::RemEventCallback"); -#endif - m_event_callback_map[type_] = nullptr; - } - - return true; - } - - // blocking call, no broadcast, first matching service only, response will be returned in service_response_ - [[deprecated]] - bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, struct SServiceResponse& service_response_) - { - if (!g_clientgate()) return false; - if (!m_created) return false; - - if (m_service_name.empty() - || method_name_.empty() - ) - return false; - - // check for new server - CheckForNewServices(); - - std::vector service_vec = g_clientgate()->GetServiceAttr(m_service_name); - for (auto& iter : service_vec) - { - if (m_host_name.empty() || (m_host_name == iter.hname)) - { - std::lock_guard lock(m_client_map_sync); - auto client = m_client_map.find(iter.key); - if (client != m_client_map.end()) - { - if (SendRequest(client->second, method_name_, request_, -1, service_response_)) - { - return true; - } - } - } - } - return false; - } - - // blocking call, all responses will be returned in service_response_vec_ - bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_, ServiceResponseVecT* service_response_vec_) - { - if (!g_clientgate()) return false; - if (!m_created) return false; - - if (m_service_name.empty() - || method_name_.empty() - ) - return false; - - // reset response - if(service_response_vec_) service_response_vec_->clear(); - - // check for new server - CheckForNewServices(); - - bool called(false); - std::vector service_vec = g_clientgate()->GetServiceAttr(m_service_name); - for (auto& iter : service_vec) - { - if (m_host_name.empty() || (m_host_name == iter.hname)) - { - std::lock_guard lock(m_client_map_sync); - auto client = m_client_map.find(iter.key); - if (client != m_client_map.end()) - { - struct SServiceResponse service_response; - if (SendRequest(client->second, method_name_, request_, timeout_, service_response)) - { - if(service_response_vec_) service_response_vec_->push_back(service_response); - called = true; - } - } - } - } - return called; - } - - // blocking call, using callback - bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_) - { - if (!g_clientgate()) return false; - if (!m_created) return false; - - if (m_service_name.empty() - || method_name_.empty() - ) - return false; - - // check for new server - CheckForNewServices(); - - // send request to every single service - return SendRequests(m_host_name, method_name_, request_, timeout_); - } - - // asynchronously call, using callback - bool CServiceClientImpl::CallAsync(const std::string& method_name_, const std::string& request_ /*, int timeout_*/) - { - // TODO: implement timeout - - if (!g_clientgate()) - { - ErrorCallback(method_name_, "Clientgate error."); - return false; - } - - if (!m_created) - { - ErrorCallback(method_name_, "Client hasn't been created yet."); - return false; - } - - if (m_service_name.empty() - || method_name_.empty()) - { - ErrorCallback(method_name_, "Invalid service or method name."); - return false; - } - - // check for new server - CheckForNewServices(); - - bool called(false); - std::vector service_vec = g_clientgate()->GetServiceAttr(m_service_name); - for (auto& iter : service_vec) - { - if (m_host_name.empty() || (m_host_name == iter.hname)) - { - std::lock_guard lock(m_client_map_sync); - auto client = m_client_map.find(iter.key); - if (client != m_client_map.end()) - { - SendRequestAsync(client->second, method_name_, request_ /*, timeout_*/, -1); - called = true; - } - } - } - return(called); - } - - // check connection state - bool CServiceClientImpl::IsConnected() - { - if (!m_created) return false; - - // check for connected clients - std::lock_guard lock(m_connected_services_map_sync); - return !m_connected_services_map.empty(); - } - - // called by the eCAL::CClientGate to register a service - void CServiceClientImpl::RegisterService(const std::string& key_, const SServiceAttr& service_) - { - // check connections - std::lock_guard lock(m_connected_services_map_sync); - - // is this a new connection ? - if (m_connected_services_map.find(key_) == m_connected_services_map.end()) - { - // call connect event - std::lock_guard lock_eb(m_event_callback_map_sync); - auto e_iter = m_event_callback_map.find(client_event_connected); - if (e_iter != m_event_callback_map.end()) - { - SClientEventCallbackData sdata; - sdata.type = client_event_connected; - sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - sdata.attr = service_; - (e_iter->second)(m_service_name.c_str(), &sdata); - } - // add service - m_connected_services_map[key_] = service_; - } - } - - // called by eCAL:CClientGate every second to update registration layer - void CServiceClientImpl::RefreshRegistration() - { - if (!m_created) return; - if (m_service_name.empty()) return; - - eCAL::pb::Sample sample; - sample.set_cmd_type(eCAL::pb::bct_reg_client); - auto service_mutable_client = sample.mutable_client(); - service_mutable_client->set_hname(Process::GetHostName()); - service_mutable_client->set_pname(Process::GetProcessName()); - service_mutable_client->set_uname(Process::GetUnitName()); - service_mutable_client->set_pid(Process::GetProcessID()); - service_mutable_client->set_sname(m_service_name); - service_mutable_client->set_sid(m_service_id); - - // register entity - if (g_registration_provider()) g_registration_provider()->RegisterClient(m_service_name, m_service_id, sample, false); - - // refresh connected services map - CheckForNewServices(); - - // check for disconnected services - { - std::lock_guard lock(m_client_map_sync); - for (auto& client : m_client_map) - { - if (!client.second->IsConnected()) - { - std::string service_key = client.first; - - // is the service still in the connecting map ? - auto iter = m_connected_services_map.find(service_key); - if (iter != m_connected_services_map.end()) - { - // call disconnect event - std::lock_guard lock_cb(m_event_callback_map_sync); - auto e_iter = m_event_callback_map.find(client_event_disconnected); - if (e_iter != m_event_callback_map.end()) - { - SClientEventCallbackData sdata; - sdata.type = client_event_disconnected; - sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - sdata.attr = iter->second; - (e_iter->second)(m_service_name.c_str(), &sdata); - } - // remove service - m_connected_services_map.erase(iter); - } - } - } - } - } - - void CServiceClientImpl::CheckForNewServices() - { - if (!g_clientgate()) return; - - // check for new services - std::vector service_vec = g_clientgate()->GetServiceAttr(m_service_name); - for (auto& iter : service_vec) - { - std::lock_guard lock(m_client_map_sync); - auto client = m_client_map.find(iter.key); - if (client == m_client_map.end()) - { - // create new client for that service - std::shared_ptr new_client = std::make_shared(iter.hname, iter.tcp_port); - m_client_map[iter.key] = new_client; - } - } - } - - bool CServiceClientImpl::SendRequests(const std::string& host_name_, const std::string& method_name_, const std::string& request_, int timeout_) - { - if (!g_clientgate()) return false; - - bool ret_state(false); - - std::lock_guard lock(m_client_map_sync); - for (auto& client : m_client_map) - { - if (client.second->IsConnected()) - { - if (host_name_.empty() || (host_name_ == client.second->GetHostName())) - { - // execute request - SServiceResponse service_response; - ret_state = SendRequest(client.second, method_name_, request_, timeout_, service_response); - if (ret_state == false) - { - std::cerr << "CServiceClientImpl::SendRequests failed." << std::endl; - } - - // call response callback - if (service_response.call_state != call_state_none) - { - std::lock_guard lock_cb(m_response_callback_sync); - if (m_response_callback) m_response_callback(service_response); - } - else - { - // call_state_none means service no more available - // we destroy the client here - client.second->Destroy(); - } - // collect return state - ret_state = true; - } - } - } - return ret_state; - } - - bool CServiceClientImpl::SendRequest(std::shared_ptr client_, const std::string& method_name_, const std::string& request_, int timeout_, struct SServiceResponse& service_response_) - { - // create request protocol buffer - eCAL::pb::Request request_pb; - request_pb.mutable_header()->set_mname(method_name_); - request_pb.set_request(request_); - std::string request_s = request_pb.SerializeAsString(); - - // catch events - client_->AddEventCallback([&](eCAL_Client_Event event, const std::string& /*message*/) - { - switch (event) - { - case client_event_timeout: - { - std::lock_guard lock_eb(m_event_callback_map_sync); - auto e_iter = m_event_callback_map.find(client_event_timeout); - if (e_iter != m_event_callback_map.end()) - { - SClientEventCallbackData sdata; - sdata.type = client_event_timeout; - sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - (e_iter->second)(m_service_name.c_str(), &sdata); - } - } - break; - default: - break; - } - }); - - // execute request - std::string response_s; - size_t sent = client_->ExecuteRequest(request_s, timeout_, response_s); - if (sent == 0) return false; - - // parse response protocol buffer - eCAL::pb::Response response_pb; - if (!response_pb.ParseFromString(response_s)) - { - std::cerr << "CServiceClientImpl::SendRequest Could not parse server response !" << std::endl; - return false; - } - - auto& response_pb_header = response_pb.header(); - service_response_.host_name = response_pb_header.hname(); - service_response_.service_name = response_pb_header.sname(); - service_response_.service_id = response_pb_header.sid(); - service_response_.method_name = response_pb_header.mname(); - service_response_.error_msg = response_pb_header.error(); - service_response_.ret_state = static_cast(response_pb.ret_state()); - switch (response_pb_header.state()) - { - case eCAL::pb::ServiceHeader_eCallState_executed: - service_response_.call_state = call_state_executed; - break; - case eCAL::pb::ServiceHeader_eCallState_failed: - service_response_.call_state = call_state_failed; - break; - default: - break; - } - service_response_.response = response_pb.response(); - - return (service_response_.call_state == call_state_executed); - } - - void CServiceClientImpl::SendRequestsAsync(const std::string& host_name_, const std::string& method_name_, const std::string& request_, int timeout_) - { - if (!g_clientgate()) - { - ErrorCallback(method_name_, "Clientgate error."); - return; - } - - std::lock_guard lock(m_client_map_sync); - - if (m_client_map.empty()) - { - ErrorCallback(method_name_, "Service not available."); - return; - } - - for (auto& client : m_client_map) - { - if (client.second->IsConnected()) - { - if (host_name_.empty() || (host_name_ == client.second->GetHostName())) - { - // execute request - SendRequestAsync(client.second, method_name_, request_, timeout_); - } - } - } - } - - void CServiceClientImpl::SendRequestAsync(std::shared_ptr client_, const std::string& method_name_, const std::string& request_, int timeout_) - { - // create request protocol buffer - eCAL::pb::Request request_pb; - request_pb.mutable_header()->set_mname(method_name_); - request_pb.set_request(request_); - std::string request_s = request_pb.SerializeAsString(); - - client_->ExecuteRequestAsync(request_s, timeout_, [this, client_, method_name_](const std::string& response, bool success) - { - std::lock_guard lock(m_response_callback_sync); - if (m_response_callback) - { - SServiceResponse service_response; - if (!success) - { - auto error_msg = "CServiceClientImpl::SendRequestAsync failed !"; - service_response.call_state = call_state_failed; - service_response.error_msg = error_msg; - service_response.ret_state = 0; - service_response.method_name = method_name_; - service_response.response.clear(); - m_response_callback(service_response); - return; - } - - eCAL::pb::Response response_pb; - if (!response_pb.ParseFromString(response)) - { - auto error_msg = "CServiceClientImpl::SendRequestAsync could not parse server response !"; - std::cerr << error_msg << "\n"; - service_response.call_state = call_state_failed; - service_response.error_msg = error_msg; - service_response.ret_state = 0; - service_response.method_name = method_name_; - service_response.response.clear(); - m_response_callback(service_response); - return; - } - - auto& response_pb_header = response_pb.header(); - service_response.host_name = response_pb_header.hname(); - service_response.service_name = response_pb_header.sname(); - service_response.service_id = response_pb_header.sid(); - service_response.method_name = response_pb_header.mname(); - service_response.error_msg = response_pb_header.error(); - service_response.ret_state = static_cast(response_pb.ret_state()); - switch (response_pb_header.state()) - { - case eCAL::pb::ServiceHeader_eCallState_executed: - service_response.call_state = call_state_executed; - break; - case eCAL::pb::ServiceHeader_eCallState_failed: - service_response.call_state = call_state_failed; - break; - default: - break; - } - service_response.response = response_pb.response(); - - m_response_callback(service_response); - } - }); - } - - void CServiceClientImpl::ErrorCallback(const std::string& method_name_, const std::string& error_message_) - { - std::lock_guard lock(m_response_callback_sync); - if (m_response_callback) - { - SServiceResponse service_response; - service_response.call_state = call_state_failed; - service_response.error_msg = error_message_; - service_response.ret_state = 0; - service_response.method_name = method_name_; - service_response.response.clear(); - m_response_callback(service_response); - } - } -} diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp deleted file mode 100644 index abd3772..0000000 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL service server implementation -**/ - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" - -#include "ecal_descgate.h" -#include "ecal_registration_provider.h" -#include "ecal_servicegate.h" -#include "ecal_global_accessors.h" -#include "ecal_service_server_impl.h" - -#include -#include - -namespace eCAL -{ - /** - * @brief Service server implementation class. - **/ - CServiceServerImpl::CServiceServerImpl() : - m_connected(false), m_created(false) - { - } - - CServiceServerImpl::CServiceServerImpl(const std::string& service_name_) : - m_connected(false), m_created(false) - { - Create(service_name_); - } - - CServiceServerImpl::~CServiceServerImpl() - { - Destroy(); - } - - bool CServiceServerImpl::Create(const std::string& service_name_) - { - if (m_created) return(false); - - // set service name - m_service_name = service_name_; - - // create service id - std::stringstream counter; - counter << std::chrono::steady_clock::now().time_since_epoch().count(); - m_service_id = counter.str(); - - m_tcp_server.Create(); - m_tcp_server.Start(std::bind(&CServiceServerImpl::RequestCallback, this, std::placeholders::_1, std::placeholders::_2), - std::bind(&CServiceServerImpl::EventCallback, this, std::placeholders::_1, std::placeholders::_2)); - - if (g_servicegate()) g_servicegate()->Register(this); - - m_created = true; - - return(true); - } - - bool CServiceServerImpl::Destroy() - { - if (!m_created) return(false); - - m_tcp_server.Stop(); - m_tcp_server.Destroy(); - - if (g_servicegate()) g_servicegate()->Unregister(this); - if (g_registration_provider()) g_registration_provider()->UnregisterServer(m_service_name, m_service_id); - - // reset method callback map - { - std::lock_guard lock(m_method_map_sync); - m_method_map.clear(); - } - - // reset event callback map - { - std::lock_guard lock(m_event_callback_map_sync); - m_event_callback_map.clear(); - } - - m_service_name.clear(); - m_service_id.clear(); - - m_connected = false; - m_created = false; - - return(true); - } - - bool CServiceServerImpl::AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_) - { - { - std::lock_guard lock(m_method_map_sync); - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - iter->second.method_pb.set_mname(method_); - iter->second.method_pb.set_req_type(req_type_); - iter->second.method_pb.set_req_desc(req_desc_); - iter->second.method_pb.set_resp_type(resp_type_); - iter->second.method_pb.set_resp_desc(resp_desc_); - } - else - { - SMethod method; - method.method_pb.set_mname(method_); - method.method_pb.set_req_type(req_type_); - method.method_pb.set_req_desc(req_desc_); - method.method_pb.set_resp_type(resp_type_); - method.method_pb.set_resp_desc(resp_desc_); - m_method_map[method_] = method; - } - } - - if (!g_descgate()) return false; - - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!(req_type_.empty() && resp_type_.empty())) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!(req_desc_.empty() && resp_desc_.empty())) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PUBLISHER; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; - - g_descgate()->ApplyServiceDescription(m_service_name, method_, req_type_, req_desc_, resp_type_, resp_desc_, quality); - - return true; - } - - // add callback function for server method calls - bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_) - { - { - std::lock_guard lock(m_method_map_sync); - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - // should we overwrite this ? - iter->second.method_pb.set_mname(method_); - iter->second.method_pb.set_req_type(req_type_); - iter->second.method_pb.set_resp_type(resp_type_); - // set callback - iter->second.callback = callback_; - } - else - { - SMethod method; - method.method_pb.set_mname(method_); - method.method_pb.set_req_type(req_type_); - method.method_pb.set_resp_type(resp_type_); - method.callback = callback_; - m_method_map[method_] = method; - } - } - - return true; - } - - // remove callback function for server method calls - bool CServiceServerImpl::RemMethodCallback(const std::string& method_) - { - std::lock_guard lock(m_method_map_sync); - - auto iter = m_method_map.find(method_); - if (iter != m_method_map.end()) - { - m_method_map.erase(iter); - return true; - } - return false; - } - - // add callback function for server events - bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) - { - if (!m_created) return false; - - // store event callback - { - std::lock_guard lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); -#endif - m_event_callback_map[type_] = callback_; - } - - return true; - } - - // remove callback function for server events - bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) - { - if (!m_created) return false; - - // reset event callback - { - std::lock_guard lock(m_event_callback_map_sync); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); -#endif - m_event_callback_map[type_] = nullptr; - } - - return true; - } - - // check connection state - bool CServiceServerImpl::IsConnected() - { - if (!m_created) return false; - return m_tcp_server.IsConnected(); - } - - // called by the eCAL::CServiceGate to register a client - void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) - { - // not used yet - } - - // called by eCAL:CServiceGate every second to update registration layer - void CServiceServerImpl::RefreshRegistration() - { - if (!m_created) return; - if (m_service_name.empty()) return; - - // might be zero in contruction phase - unsigned short server_tcp_port(m_tcp_server.GetTcpPort()); - if (server_tcp_port == 0) return; - - // create service registration sample - eCAL::pb::Sample sample; - sample.set_cmd_type(eCAL::pb::bct_reg_service); - auto service_mutable_service = sample.mutable_service(); - service_mutable_service->set_hname(Process::GetHostName()); - service_mutable_service->set_pname(Process::GetProcessName()); - service_mutable_service->set_uname(Process::GetUnitName()); - service_mutable_service->set_pid(Process::GetProcessID()); - service_mutable_service->set_sname(m_service_name); - service_mutable_service->set_sid(m_service_id); - service_mutable_service->set_tcp_port(server_tcp_port); - - // add methods - { - std::lock_guard lock(m_method_map_sync); - for (auto iter : m_method_map) - { - auto method = service_mutable_service->add_methods(); - method->set_mname(iter.first); - method->set_req_type(iter.second.method_pb.req_type()); - method->set_req_desc(iter.second.method_pb.req_desc()); - method->set_resp_type(iter.second.method_pb.resp_type()); - method->set_resp_desc(iter.second.method_pb.resp_desc()); - method->set_call_count(iter.second.method_pb.call_count()); - } - } - - // register entity - if (g_registration_provider()) g_registration_provider()->RegisterServer(m_service_name, m_service_id, sample, false); - } - - int CServiceServerImpl::RequestCallback(const std::string& request_, std::string& response_) - { - // prepare response - eCAL::pb::Response response_pb; - auto* response_pb_mutable_header = response_pb.mutable_header(); - response_pb_mutable_header->set_hname(eCAL::Process::GetHostName()); - response_pb_mutable_header->set_sname(m_service_name); - response_pb_mutable_header->set_sid(m_service_id); - - // try to parse request - eCAL::pb::Request request_pb; - if (!request_pb.ParseFromString(request_)) - { - Logging::Log(log_level_error, m_service_name + "::CServiceServerImpl::RequestCallback failed to parse request message"); - - response_pb_mutable_header->set_state(eCAL::pb::ServiceHeader_eCallState_failed); - std::string emsg = "Service '" + m_service_name + "' request message could not be parsed."; - response_pb_mutable_header->set_error(emsg); - - // serialize response and return "request message could not be parsed" - response_ = response_pb.SerializeAsString(); - - // return value is not propagated to the remote caller. - return 0; - } - - // get method - SMethod method; - auto& request_pb_header = request_pb.header(); - response_pb_mutable_header->set_mname(request_pb_header.mname()); - { - std::lock_guard lock(m_method_map_sync); - - auto requested_method_iterator = m_method_map.find(request_pb_header.mname()); - if (requested_method_iterator == m_method_map.end()) - { - // set method call state 'failed' - response_pb_mutable_header->set_state(eCAL::pb::ServiceHeader_eCallState_failed); - // set error message - std::string emsg = "Service '" + m_service_name + "' has no method named '" + request_pb_header.mname() + "'"; - response_pb_mutable_header->set_error(emsg); - - // serialize response and return "method not found" - response_ = response_pb.SerializeAsString(); - - // Return Success (error_code = 0), as parsing the request worked. The - // return value is not propagated to the remote caller. - return 0; - } - else - { - // increase call count - auto call_count = requested_method_iterator->second.method_pb.call_count(); - requested_method_iterator->second.method_pb.set_call_count(++call_count); - - // store (copy) the method object, so we can release the mutex before calling the function - method = requested_method_iterator->second; - } - } - - // execute method (outside lock guard) - const std::string& request_s = request_pb.request(); - std::string response_s; - int service_return_state = method.callback(method.method_pb.mname(), method.method_pb.req_type(), method.method_pb.resp_type(), request_s, response_s); - - // set method call state 'executed' - response_pb_mutable_header->set_state(eCAL::pb::ServiceHeader_eCallState_executed); - // set method response and return state - response_pb.set_response(response_s); - response_pb.set_ret_state(service_return_state); - - // serialize response and return - response_ = response_pb.SerializeAsString(); - - // return success (error code 0) - return 0; - } - - void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, const std::string& /*message_*/) - { - bool mode_changed(false); - if (m_connected) - { - if (!m_tcp_server.IsConnected()) - { - mode_changed = true; - m_connected = false; - } - } - else - { - if (m_tcp_server.IsConnected()) - { - mode_changed = true; - m_connected = true; - } - } - - if (mode_changed) - { - // call event - std::lock_guard lock_cb(m_event_callback_map_sync); - auto e_iter = m_event_callback_map.find(event_); - if (e_iter != m_event_callback_map.end()) - { - SServerEventCallbackData sdata; - sdata.type = event_; - sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - (e_iter->second)(m_service_name.c_str(), &sdata); - } - } - } -}; diff --git a/ecal/core/src/service/ecal_tcpclient.cpp b/ecal/core/src/service/ecal_tcpclient.cpp deleted file mode 100644 index e4189b9..0000000 --- a/ecal/core/src/service/ecal_tcpclient.cpp +++ /dev/null @@ -1,367 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL tcp server based on asio c++ -**/ - -#include "ecal_process.h" -#include "ecal_tcpclient.h" -#include "ecal_tcpheader.h" - -#include -#include -#include - -namespace eCAL -{ - ////////////////////////////////////////////////////////////////// - // CTcpClient - ////////////////////////////////////////////////////////////////// - CTcpClient::CTcpClient() : m_created(false), m_connected(false), m_async_request_in_progress(false) - { - } - - CTcpClient::CTcpClient(const std::string& host_name_, unsigned short port_) : m_created(false), m_connected(false), m_async_request_in_progress(false) - { - Create(host_name_, port_); - } - - CTcpClient::~CTcpClient() - { - Destroy(); - } - - void CTcpClient::Create(const std::string& host_name_, unsigned short port_) - { - if (m_created) return; - - m_host_name = host_name_; - m_io_service = std::make_shared(); - m_socket = std::make_shared(*m_io_service); - asio::ip::tcp::resolver resolver(*m_io_service); - - try - { - // create idle work object, so the io_service doesn't shut down - m_idle_work = std::make_shared(*m_io_service); - - // execute the io_sevice in 1 thread - m_async_worker = std::thread( - [this] - { - m_io_service->run(); - }); - - // resolve and connect - asio::connect(*m_socket, resolver.resolve({ host_name_, std::to_string(port_) })); - - // set TCP no delay, so Nagle's algorithm will not stuff multiple messages in one TCP segment - asio::ip::tcp::no_delay no_delay_option(true); - m_socket->set_option(no_delay_option); - - // mark as connected - m_connected = true; - - // fire connect event - if (m_event_callback) - { - m_event_callback(client_event_connected, "CTcpClient connected"); - } - } - catch (std::exception& e) - { - // log error message - std::cerr << "CTcpClient::Connect exception: " << e.what() << "\n"; - // mark as not connected - m_connected = false; - } - - // return success - m_created = true; - } - - void CTcpClient::Destroy() - { - if (!m_created) return; - - m_socket->close(); - m_io_service->stop(); - - m_async_worker.join(); - - m_idle_work = nullptr; - m_socket = nullptr; - m_io_service = nullptr; - - m_connected = false; - m_async_request_in_progress = false; - m_created = false; - } - - bool CTcpClient::IsConnected() - { - return m_connected; - } - - bool CTcpClient::AddEventCallback(EventCallbackT callback_) - { - m_event_callback = callback_; - return true; - } - - bool CTcpClient::RemEventCallback() - { - m_event_callback = nullptr; - return true; - } - - size_t CTcpClient::ExecuteRequest(const std::string& request_, int timeout_, std::string& response_) - { - std::lock_guard lock(m_socket_write_mutex); - - if (!m_created) return 0; - - if (!SendRequest(request_)) return 0; - - return ReceiveResponse(response_, timeout_); - } - - void CTcpClient::ExecuteRequestAsync(const std::string& request_, int timeout_, AsyncCallbackT callback) - { - std::unique_lock lock(m_socket_write_mutex); - if (!m_async_request_in_progress) - { - m_async_request_in_progress.store(true); - - if (!m_created) - ExecuteCallback(callback, "", false); - - // start waiting for response - ReceiveResponseAsync(callback, timeout_); - - if (!SendRequest(request_)) - ExecuteCallback(callback, "", false); - } - else - { - std::cerr << "CTcpClient::ExecuteRequestAsync failed: Another request is already in progress" << std::endl; - ExecuteCallback(callback, "", false); - } - } - - bool CTcpClient::SendRequest(const std::string& request_) - { - size_t written(0); - try - { - // check for old (timeouted ?) reponses - const size_t resp_size = m_socket->available(); - if (resp_size > 0) - { - std::vector resp_buffer(resp_size); - m_socket->read_some(asio::buffer(resp_buffer)); - } - - // send payload to server - while (written != request_.size()) - { - auto bytes_written = m_socket->write_some(asio::buffer(request_.c_str() + written, request_.size() - written)); - written += bytes_written; - } - } - catch (std::exception& e) - { - std::cerr << "CTcpClient::SendRequest: Failed to send request: " << e.what() << "\n"; - m_connected = false; - return false; - } - - return true; - } - - size_t CTcpClient::ReceiveResponse(std::string& response_, int timeout_) - { - try - { - assert((timeout_ == -1) || (timeout_ > 0)); - - STcpHeader tcp_header; - bool read_done(false); - bool read_failed(false); - bool time_expired(false); - - // read stream header (async) - if (timeout_ != -1) - { - // start timer - asio::steady_timer timer(*m_io_service); - timer.expires_from_now(asio::chrono::milliseconds(timeout_)); - timer.async_wait( - [&](const asio::error_code& ec) - { - if (!ec) time_expired = true; - } - ); - - // async read - m_socket->async_read_some(asio::buffer(&tcp_header, sizeof(tcp_header)), - [&](const asio::error_code& /*ec*/, std::size_t bytes_read) - { - if (bytes_read != sizeof(tcp_header)) read_failed = true; - else read_done = true; - } - ); - - // idle operations - while (!read_done && !read_failed && !time_expired) - { - eCAL::Process::SleepFor(std::chrono::milliseconds(1)); - } - - // stop timer - timer.cancel(); - } - // read stream header (sync) - else - { - size_t bytes_read = m_socket->read_some(asio::buffer(&tcp_header, sizeof(tcp_header))); - if (bytes_read != sizeof(tcp_header)) read_failed = true; - else read_done = true; - } - - // check for expired timer - if (time_expired) - { - // fire event - if (m_event_callback) - { - m_event_callback(client_event_timeout, "ReceiveResponse timeouted"); - } - - // cleanup the socket and return - m_socket->cancel(); - return 0; - } - - // check for failed read - if (!read_done || read_failed) - { - // cleanup the socket and return - m_socket->cancel(); - return 0; - } - - // extract data size - const size_t rsize = static_cast(ntohl(tcp_header.psize_n)); - - // prepare response buffer - response_.clear(); - response_.reserve(rsize); - - // read stream data - do - { - const size_t buffer_size(1024); - char buffer[buffer_size]; - size_t bytes_left = rsize - response_.size(); - size_t bytes_to_read = std::min(buffer_size, bytes_left); - size_t bytes_read = m_socket->read_some(asio::buffer(buffer, bytes_to_read)); - response_ += std::string(buffer, bytes_read); - - //std::cout << "CTcpClient::ReceiveResponse read response bytes " << bytes_read << " to " << response_.size() << std::endl; - } - while (response_.size() < rsize); - - return response_.size(); - } - catch (std::exception& e) - { - std::cerr << "CTcpClient::ReceiveResponse: Failed to recieve response: " << e.what() << std::endl; - m_connected = false; - return 0; - } - } - - void CTcpClient::ReceiveResponseAsync(AsyncCallbackT callback_, int /*timeout_*/) - { - // TODO: Actually implement this function - - //assert((timeout_ == -1) || (timeout_ > 0)); - - // start timer - //asio::steady_timer timer(*m_io_service); - //timer.expires_from_now(asio::chrono::milliseconds(timeout_)); - //timer.async_wait( - // [&](const asio::error_code& ec) - // { - // // timer expired - // if (!ec) - // { - // // fire event - // if (m_event_callback) - // { - // m_event_callback(client_event_timeout, "ReceiveResponseAsync timeouted"); - // } - - // // cleanup the socket - // m_socket->cancel(); - // } - // } - //); - - std::shared_ptr tcp_header = std::make_shared(); - //std::unique_lock lock(m_socket_read_mutex); - - m_socket->async_read_some(asio::buffer(tcp_header.get(), sizeof(tcp_header)), - [this, tcp_header, callback_/*, lock = std::move(lock)*/](auto ec, auto bytes_transferred) - { - if (ec) this->ExecuteCallback(callback_, "", false); - - if (bytes_transferred == sizeof(tcp_header)) - { - const auto resp_size = static_cast(ntohl(tcp_header->psize_n)); - this->ReceiveResponseData(resp_size, callback_); - } - else - { - std::cerr << "CTcpClient::ReceiveResponseAsync: Failed to receive response: " << "tcp_header size is invalid." << "\n"; - this->ExecuteCallback(callback_, "", false); - } - }); - } - - void CTcpClient::ReceiveResponseData(const size_t size_, AsyncCallbackT callback_) - { - std::string data(size_, ' '); - asio::error_code ec; - - // we are already in io_worker_thread - asio::read(*m_socket, asio::buffer(&data[0], data.size()), ec); - - if (ec) ExecuteCallback(callback_, "", false); - else ExecuteCallback(callback_, data, true); - } - - void CTcpClient::ExecuteCallback(AsyncCallbackT callback_, const std::string &data_, bool success_) - { - m_async_request_in_progress = false; - callback_(data_, success_); - } -}; diff --git a/ecal/core/src/service/ecal_tcpclient.h b/ecal/core/src/service/ecal_tcpclient.h deleted file mode 100644 index 32fa6be..0000000 --- a/ecal/core/src/service/ecal_tcpclient.h +++ /dev/null @@ -1,98 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL tcp server based on asio c++ -**/ - -#pragma once - -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4834) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include - -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_socket.h" -#endif -#include "ecal/cimpl/ecal_callback_cimpl.h" - -#ifdef ECAL_OS_LINUX -#include -#include -#endif - -namespace eCAL -{ - class CTcpClient - { - public: - typedef std::function AsyncCallbackT; - typedef std::function EventCallbackT; - - CTcpClient(); - CTcpClient(const std::string& host_name_, unsigned short port_); - - ~CTcpClient(); - - void Create(const std::string& host_name_, unsigned short port_); - void Destroy(); - - bool IsConnected(); - - std::string GetHostName() { return m_host_name; } - - bool AddEventCallback(EventCallbackT callback_); - bool RemEventCallback(); - - size_t ExecuteRequest(const std::string& request_, int timeout_, std::string& response_); - void ExecuteRequestAsync(const std::string& request_, int timeout_, AsyncCallbackT callback); - - protected: - std::string m_host_name; - std::mutex m_socket_write_mutex; - std::mutex m_socket_read_mutex; - std::thread m_async_worker; - std::shared_ptr m_io_service; - std::shared_ptr m_idle_work; - std::shared_ptr m_socket; - EventCallbackT m_event_callback; - bool m_created; - bool m_connected; - std::atomic m_async_request_in_progress; - - private: - bool SendRequest(const std::string &request_); - size_t ReceiveResponse(std::string &response_, int timeout_); - void ReceiveResponseAsync(AsyncCallbackT callback_, int timeout_); - void ReceiveResponseData(const size_t size, AsyncCallbackT callback_); - void ExecuteCallback(AsyncCallbackT callback_, const std::string &data_, bool success_); - }; -}; diff --git a/ecal/core/src/service/ecal_tcpheader.h b/ecal/core/src/service/ecal_tcpheader.h deleted file mode 100644 index 7666f7b..0000000 --- a/ecal/core/src/service/ecal_tcpheader.h +++ /dev/null @@ -1,33 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#include - -namespace eCAL -{ - struct STcpHeader - { - uint32_t psize_n = 0; // package size in network byte order - uint32_t reserved1 = 0; // reserved - uint32_t reserved2 = 0; // reserved - uint32_t reserved3 = 0; // reserved - }; -} diff --git a/ecal/core/src/service/ecal_tcpserver.cpp b/ecal/core/src/service/ecal_tcpserver.cpp deleted file mode 100644 index 3877f35..0000000 --- a/ecal/core/src/service/ecal_tcpserver.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL tcp server based on asio c++ -**/ - -#include "ecal_tcpserver.h" - -namespace eCAL -{ - ////////////////////////////////////////////////////////////////// - // CTcpServer - ////////////////////////////////////////////////////////////////// - CTcpServer::CTcpServer() : m_started(false) - { - } - - CTcpServer::~CTcpServer() - { - Destroy(); - } - - void CTcpServer::Create() - { - } - - void CTcpServer::Destroy() - { - Stop(); - } - - void CTcpServer::Start(RequestCallbackT request_callback_, EventCallbackT event_callback_) - { - if (m_started) return; - if (m_server != nullptr) return; - - m_server_thread = std::thread(&CTcpServer::ServerThread, this, 0, request_callback_, event_callback_); - - m_started = true; - } - - void CTcpServer::Stop() - { - if (!m_started) return; - - if (m_server == nullptr) return; - if (m_io_service != nullptr) m_io_service->stop(); - m_server_thread.join(); - - m_started = false; - } - - bool CTcpServer::IsConnected() - { - if (!m_started) return false; - - if (m_server == nullptr) return false; - - return m_server->is_connected(); - } - - void CTcpServer::ServerThread(std::uint32_t port_, RequestCallbackT request_callback_, EventCallbackT event_callback_) - { - m_io_service = std::make_shared(); - m_server = std::make_shared(*m_io_service, static_cast(port_)); - - m_server->add_request_callback1(request_callback_); - m_server->add_event_callback(event_callback_); - - m_io_service->run(); - - m_server = nullptr; - } -}; diff --git a/ecal/core/src/service/ecal_tcpserver.h b/ecal/core/src/service/ecal_tcpserver.h deleted file mode 100644 index 95249ec..0000000 --- a/ecal/core/src/service/ecal_tcpserver.h +++ /dev/null @@ -1,70 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCAL tcp server based on asio c++ -**/ - -#pragma once - -#include -#include - -#include - -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_socket.h" -#endif - -#ifdef ECAL_OS_LINUX -#include -#include -#endif - -#include "asio_server.h" - -namespace eCAL -{ - class CTcpServer - { - public: - CTcpServer(); - ~CTcpServer(); - - void Create(); - void Destroy(); - - void Start(RequestCallbackT request_callback_, EventCallbackT event_callback_); - void Stop(); - - bool IsConnected(); - - std::shared_ptr GetIOService() { return m_io_service; }; - unsigned short GetTcpPort() { return (m_server ? m_server->get_port() : 0); } - - protected: - void ServerThread(std::uint32_t port_, RequestCallbackT request_callback_, EventCallbackT event_callback_); - - bool m_started; - - std::shared_ptr m_io_service; - std::shared_ptr m_server; - std::thread m_server_thread; - }; -}; diff --git a/ecal/core/src/sys_usage.cpp b/ecal/core/src/sys_usage.cpp deleted file mode 100644 index d6fb87c..0000000 --- a/ecal/core/src/sys_usage.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief System usage monitoring -**/ - -#include - -#ifdef ECAL_OS_WINDOWS - -#include "ecal_win_main.h" - -class CpuUsage -{ -public: - CpuUsage(void); - - short GetUsage(); -private: - ULONGLONG SubtractTimes(const FILETIME& ftA, const FILETIME& ftB); - bool EnoughTimePassed(); - inline bool IsFirstRun() const { return (m_dwLastRun == 0); } - - short m_nCpuUsage; - ULONGLONG m_dwLastRun; - volatile LONG m_lRunCount; - - // system total times - FILETIME m_ftPrevSysKernel; - FILETIME m_ftPrevSysUser; - - // process times - FILETIME m_ftPrevProcKernel; - FILETIME m_ftPrevProcUser; -}; - - -CpuUsage::CpuUsage(void) - :m_nCpuUsage(-1) - ,m_dwLastRun(0) - ,m_lRunCount(0) -{ - ZeroMemory(&m_ftPrevSysKernel, sizeof(FILETIME)); - ZeroMemory(&m_ftPrevSysUser, sizeof(FILETIME)); - - ZeroMemory(&m_ftPrevProcKernel, sizeof(FILETIME)); - ZeroMemory(&m_ftPrevProcUser, sizeof(FILETIME)); - -} - -/********************************************** -* CpuUsage::GetUsage -* returns the percent of the CPU that this process -* has used since the last time the method was called. -* If there is not enough information, -1 is returned. -* If the method is recalled to quickly, the previous value -* is returned. -***********************************************/ -short CpuUsage::GetUsage() -{ - //create a local copy to protect against race conditions in setting the - //member variable - short nCpuCopy = m_nCpuUsage; - if (::InterlockedIncrement(&m_lRunCount) == 1) - { - /* - If this is called too often, the measurement itself will greatly affect the - results. - */ - - if (!EnoughTimePassed()) - { - ::InterlockedDecrement(&m_lRunCount); - return nCpuCopy; - } - - FILETIME ftSysIdle, ftSysKernel, ftSysUser; - FILETIME ftProcCreation, ftProcExit, ftProcKernel, ftProcUser; - - if (!GetSystemTimes(&ftSysIdle, &ftSysKernel, &ftSysUser) || - !GetProcessTimes(GetCurrentProcess(), &ftProcCreation, &ftProcExit, &ftProcKernel, &ftProcUser)) - { - ::InterlockedDecrement(&m_lRunCount); - return nCpuCopy; - } - - if (!IsFirstRun()) - { - /* - CPU usage is calculated by getting the total amount of time the system has operated - since the last measurement (made up of kernel + user) and the total - amount of time the process has run (kernel + user). - */ - ULONGLONG ftSysKernelDiff = SubtractTimes(ftSysKernel, m_ftPrevSysKernel); - ULONGLONG ftSysUserDiff = SubtractTimes(ftSysUser, m_ftPrevSysUser); - - ULONGLONG ftProcKernelDiff = SubtractTimes(ftProcKernel, m_ftPrevProcKernel); - ULONGLONG ftProcUserDiff = SubtractTimes(ftProcUser, m_ftPrevProcUser); - - ULONGLONG nTotalSys = ftSysKernelDiff + ftSysUserDiff; - ULONGLONG nTotalProc = ftProcKernelDiff + ftProcUserDiff; - - if (nTotalSys > 0) - { - m_nCpuUsage = (short)((100.0 * nTotalProc) / nTotalSys); - } - } - - m_ftPrevSysKernel = ftSysKernel; - m_ftPrevSysUser = ftSysUser; - m_ftPrevProcKernel = ftProcKernel; - m_ftPrevProcUser = ftProcUser; - -#if defined(__MINGW32__) - m_dwLastRun = GetTickCount(); -#else - #if defined(_WIN64) - m_dwLastRun = GetTickCount64(); - #else - m_dwLastRun = GetTickCount(); - #endif -#endif - - nCpuCopy = m_nCpuUsage; - } - - ::InterlockedDecrement(&m_lRunCount); - - return nCpuCopy; -} - -ULONGLONG CpuUsage::SubtractTimes(const FILETIME& ftA, const FILETIME& ftB) -{ - LARGE_INTEGER a, b; - a.LowPart = ftA.dwLowDateTime; - a.HighPart = ftA.dwHighDateTime; - - b.LowPart = ftB.dwLowDateTime; - b.HighPart = ftB.dwHighDateTime; - - return a.QuadPart - b.QuadPart; -} - -bool CpuUsage::EnoughTimePassed() -{ - const int minElapsedMS = 250; //milliseconds - -#if defined(__MINGW32__) - ULONGLONG dwCurrentTickCount = GetTickCount(); -#else - #if defined(_WIN64) - ULONGLONG dwCurrentTickCount = GetTickCount64(); - #else - ULONGLONG dwCurrentTickCount = GetTickCount(); - #endif -#endif - - return (dwCurrentTickCount - m_dwLastRun) > minElapsedMS; -} - -static CpuUsage g_cpu_usage; - -float GetCPULoad() -{ - static float usage = 0.0f; - short cpu_usage = g_cpu_usage.GetUsage(); - // -1 means could not be evaluated yet, do not use that value .. - if (cpu_usage != -1) usage = 0.98f * usage + 0.02f * static_cast(cpu_usage); - return(usage * 0.01f); -} - -#endif /* ECAL_OS_WINDOWS */ - -#ifdef ECAL_OS_LINUX - -float GetCPULoad() -{ - return(0.0f); -} - -#endif /* ECAL_OS_LINUX */ diff --git a/ecal/core_pb/CMakeLists.txt b/ecal/core_pb/CMakeLists.txt deleted file mode 100644 index e8f0704..0000000 --- a/ecal/core_pb/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -project(core_pb) - -find_package(Protobuf REQUIRED) - -set(ProtoFiles - ${CMAKE_CURRENT_SOURCE_DIR}/src/ecal/core/pb/ecal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/ecal/core/pb/host.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/ecal/core/pb/layer.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/ecal/core/pb/monitoring.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/ecal/core/pb/process.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/ecal/core/pb/service.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/ecal/core/pb/topic.proto -) - -# Compile statically on Windows and shared for all other systems. -# -# We compile shared on Linux etc., as we must only load the proto descriptors -# once; otherwise, protobuf would throw an exception on runtime. A shared object -# achieves that, as it is only loaded into memory once, even when being linked -# against from different libraries. -# We don't have to do that on Windows, as we compile against protobuf statically -# and therefore each .dll has it's own descriptor pool. Having a static lib here -# also has the advantage that we need to export the symbols, which is default -# disabled on Windows. -# -# TODO: Having code like that probably isn't the best solution ever. We should -# maybe always link against protobuf statically. Currently the reason why we -# don't do that is, that the default Ubuntu libprotobuf.a doesn't contain -# position independent code and can therefore not be statically compiled into a -# shared object library. -if (WIN32) - ecal_add_static_library(${PROJECT_NAME} src/core_pb.cpp) -else() - ecal_add_shared_library(${PROJECT_NAME} src/core_pb.cpp) -endif() - -add_library(eCAL::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) - -protobuf_target_cpp(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src INSTALL_FOLDER include ${ProtoFiles}) - -target_compile_options(${PROJECT_NAME} - PRIVATE - $<$:/wd4505 /wd4592 /wd4189> - $<$:-Wno-unused-parameter>) - -set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) - -target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf) -target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) - -ecal_install_library(${PROJECT_NAME}) - -if(BUILD_PY_BINDING) - protobuf_generate_python_ext(python_sources ${PYTHON_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src ${ProtoFiles}) - target_sources(${PROJECT_NAME} PRIVATE ${python_sources}) - set_source_files_properties(${python_sources} PROPERTIES HEADER_FILE_ONLY TRUE) -endif() - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER ecal/core_pb) diff --git a/ecal/core_pb/src/core_pb.cpp b/ecal/core_pb/src/core_pb.cpp deleted file mode 100644 index 0989ba9..0000000 --- a/ecal/core_pb/src/core_pb.cpp +++ /dev/null @@ -1 +0,0 @@ -// dummy file to force VS to create a library \ No newline at end of file diff --git a/ecal/core_pb/src/ecal/core/pb/topic.proto b/ecal/core_pb/src/ecal/core/pb/topic.proto deleted file mode 100644 index bf396e6..0000000 --- a/ecal/core_pb/src/ecal/core/pb/topic.proto +++ /dev/null @@ -1,72 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -import "ecal/core/pb/layer.proto"; - -package eCAL.pb; - -message QOS // quality of service -{ - enum eQOSPolicy_Reliability - { - best_effort_reliability_qos = 0; // best effort reliability (default for Subscribers) - reliable_reliability_qos = 1; // reliable reliability (default for Publishers) - } - - enum eQOSPolicy_HistoryKind - { - keep_last_history_qos = 0; // keep only a number of samples, default value - keep_all_history_qos = 1; // keep all samples until the ResourceLimitsQosPolicy are exhausted - } - - eQOSPolicy_Reliability reliability = 1; // qos reliability (reliable / best effort) - eQOSPolicy_HistoryKind history = 2; // qos history kind (keep last / all) - int32 history_depth = 3; // number of samples for history kind "keep last" -} - -message Topic // eCAL topic -{ - int32 rclock = 1; // registration clock (heart beat) - int32 hid = 26; // host id - string hname = 2; // host name - int32 pid = 3; // process id - string pname = 4; // process name - string uname = 5; // unit name - string tid = 6; // topic id - string tname = 7; // topic name - string direction = 8; // direction (publisher, subscriber) - string ttype = 9; // topic type (protocol) - bytes tdesc = 10; // topic description (protocol descriptor) - - QOS tqos = 11; // topic quality of service - repeated TLayer tlayer = 12; // active topic transport layers and it's specific parameter - int32 tsize = 13; // topic size - - int32 connections_loc = 16; // number of local connected entities - int32 connections_ext = 17; // number of external connected entities - int32 message_drops = 18; // dropped messages - - int64 did = 19; // data send id (publisher setid) - int64 dclock = 20; // data clock (send / receive action) - int32 dfreq = 21; // data frequency (send / receive samples per second) [mHz] - - map attr = 27; // generic topic description -} diff --git a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_dyn.h b/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_dyn.h deleted file mode 100644 index f19d45e..0000000 --- a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_dyn.h +++ /dev/null @@ -1,177 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief dynamic protobuf message decoder -**/ - -#pragma once - -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4100 4127 4146 4505 4800 4189 4592) // disable proto warnings -#endif -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif -#include -#include -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -#include - -namespace eCAL -{ -namespace protobuf -{ - /** - * @brief eCAL dynamic protobuf decoder. - * - * The CProtoDynDecoder class is used to decode a protobuf message using protobuf reflection interface. The returned - * google message objects are owned by CProtoDynDecoder and freed on destruction. - * - * @code - * // create dynamic decoder - * std::string error_s; - * eCAL::CProtoDynDecoder decoder; - * std::shared_ptr msg_obj(decoder.GetProtoMessageFromFile("foo.proto", "FooMessage", error_s)); - * - * // receive a message - * std::string msg_s = ReceiveMessageFromAnyWhere("foo"); - * - * // decode message - * msg_obj->ParseFromString(msg_s); - * - * // print message - * std::cout << msg_obj->DebugString() << std::endl; - * @endcode - **/ - class CProtoDynDecoder - { - public: - /** - * @brief Create message from proto file. - * - * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. - * - * @param proto_filename_ Proto file name. - * @param msg_type_ Type name. - * @param [out] error_s_ Error string. - * - * @return google message object or nullptr if generation failed (details see error_s_) - **/ - google::protobuf::Message* GetProtoMessageFromFile(const std::string& proto_filename_, const std::string& msg_type_, std::string& error_s_); - - /** - * @brief Create message from proto string. - * - * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. - * - * @param proto_string_ Proto string. - * @param msg_type_ Type name. - * @param [out] error_s_ Error string. - * - * @return google message object or nullptr if generation failed (details see error_s_) - **/ - google::protobuf::Message* GetProtoMessageFromString(const std::string& proto_string_, const std::string& msg_type_, std::string& error_s_); - - /** - * @brief Create message from proto descriptor. - * - * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. - * - * @param proto_desc_ Proto descriptor. - * @param msg_type_ Type name. - * @param [out] error_s_ Error string. - * - * @return google message object or nullptr if generation failed (details see error_s_) - **/ - google::protobuf::Message* GetProtoMessageFromDescriptor(const google::protobuf::FileDescriptorProto& proto_desc_, const std::string& msg_type_, std::string& error_s_); - - /** - * @brief Create message from serialized proto descriptor string. - * - * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. - * - * @param msg_desc_ Serialized message descriptor. - * @param msg_type_ Type name. - * @param [out] error_s_ Error string. - * - * @return google message object or nullptr if generation failed (details see error_s_) - **/ - google::protobuf::Message* GetProtoMessageFromDescriptor(const std::string& msg_desc_, const std::string& msg_type_, std::string& error_s_); - - /** - * @brief Create message from proto descriptor set. - * - * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. - * - * @param proto_desc_set_ Proto descriptor set. - * @param msg_type_ Type name. - * @param [out] error_s_ Error string. - * - * @return google message object or nullptr if generation failed (details see error_s_) - **/ - google::protobuf::Message* GetProtoMessageFromDescriptorSet(const google::protobuf::FileDescriptorSet& proto_desc_set_, const std::string& msg_type_, std::string& error_s_); - - /** - * @brief Returns the DescriptorPool member variable - **/ - google::protobuf::DescriptorPool* GetDescriptorPool(); - - /** - * @brief Create proto descriptor from proto file. - * - * @param proto_filename_ Proto file name. - * @param [out] file_desc_proto_ Type name. - * @param [out] error_s_ Error string. - * - * @return true if succeeded otherwise false (details see error_s_) - **/ - static bool GetFileDescriptorFromFile(const std::string& proto_filename_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_); - - /** - * @brief Create proto descriptor from proto string. - * - * @param proto_string_ Proto string. - * @param [out] file_desc_proto_ Type name. - * @param [out] error_s_ Error string. - * - * @return true if succeeded otherwise false (details see error_s_) - **/ - static bool GetFileDescriptorFromString(const std::string& proto_string_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_); - - protected: - google::protobuf::DescriptorPool m_descriptor_pool; - google::protobuf::DynamicMessageFactory m_message_factory; - }; -} -} diff --git a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_message_filter.h b/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_message_filter.h deleted file mode 100644 index 55222d7..0000000 --- a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_message_filter.h +++ /dev/null @@ -1,96 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#include -#include -#include -#include - -namespace eCAL -{ - namespace protobuf - { - - /* - * Hierarchy: - * - * MessageFilter (abstract) - * - NoFilter - * - BaseIncludeFilter (abstract) - * - SimpleIncludeFilter - * - ComplexIncludeFilter - */ - - - class MessageFilter - { - public: - - virtual ~MessageFilter() {} - virtual bool Filter(const std::string& message_name_) = 0; - - }; - - class NoFilter : public MessageFilter - { - public: - bool Filter(const std::string&) override - { - return true; - } - }; - - /* This filter includes the given strings */ - class BaseIncludeFilter : public MessageFilter - { - public: - BaseIncludeFilter() {}; - bool Filter(const std::string& message_name_) override = 0; - void Clear(); - void Insert(const std::string& include_); - void Erase(const std::string& include_); - - protected: - std::set elements; - std::mutex elements_mutex; - }; - - /* This filter includes the given strings */ - class SimpleIncludeFilter : public BaseIncludeFilter - { - public: - SimpleIncludeFilter(); - bool Filter(const std::string& message_name_) override; - }; - - /* This filter includes the given strings */ - class ComplexIncludeFilter : public BaseIncludeFilter - { - public: - ComplexIncludeFilter(); - bool Filter(const std::string& message_name_) override; - - private: - std::regex filter_regex; - }; - - } -} \ No newline at end of file diff --git a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_visitor.h b/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_visitor.h deleted file mode 100644 index 64a4240..0000000 --- a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_visitor.h +++ /dev/null @@ -1,156 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4100 4127 4146 4505 4800 4189 4592) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER - -// disable the unreferenced formal parameter warning, because these are prototype functions with an empty body -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning( disable : 4100 ) -#endif // _MSC_VER - -namespace eCAL -{ - namespace protobuf - { - class MessageFilter; - - struct MessageInfo - { - std::string field_name; - std::string group_name; - int id; - std::string type; - }; - - class MessageVisitor - { - friend class CProtoDecoder; - public: - virtual ~MessageVisitor() {}; - - protected: - virtual void ArrayStart(const MessageInfo& /*info*/, const google::protobuf::FieldDescriptor::Type& /*type_*/, size_t /*size_*/) {}; - virtual void ArrayValueIntegral(const MessageInfo& /*info_*/, google::protobuf::int32 /*value_*/) {}; - virtual void ArrayValueIntegral(const MessageInfo& /*info_*/, google::protobuf::uint32 /*value_*/) {}; - virtual void ArrayValueIntegral(const MessageInfo& /*info_*/, google::protobuf::int64 /*value_*/) {}; - virtual void ArrayValueIntegral(const MessageInfo& /*info_*/, google::protobuf::uint64 /*value_*/) {}; - virtual void ArrayValueIntegral(const MessageInfo& /*info_*/, float /*value_*/) {}; - virtual void ArrayValueIntegral(const MessageInfo& /*info_*/, double /*value_*/) {}; - virtual void ArrayValueIntegral(const MessageInfo& /*info_*/, bool /*value_*/) {}; - virtual void ArrayValueString (const MessageInfo& /*info_*/, const std::string& /*value_*/) {}; - virtual void ArrayValueBytes (const MessageInfo& /*info_*/, const std::string& /*value_*/) {}; - virtual void ArrayValueEnum (const MessageInfo& /*info_*/, int /*value_*/, const std::string& /*name_*/) {}; - virtual void ArrayEnd() {}; - - virtual void ScalarValueIntegral(const MessageInfo& /*info_*/, google::protobuf::int32 /*value_*/) {}; - virtual void ScalarValueIntegral(const MessageInfo& /*info_*/, google::protobuf::uint32 /*value_*/) {}; - virtual void ScalarValueIntegral(const MessageInfo& /*info_*/, google::protobuf::int64 /*value_*/) {}; - virtual void ScalarValueIntegral(const MessageInfo& /*info_*/, google::protobuf::uint64 /*value_*/) {}; - virtual void ScalarValueIntegral(const MessageInfo& /*info_*/, float /*value_*/) {}; - virtual void ScalarValueIntegral(const MessageInfo& /*info_*/, double /*value_*/) {}; - virtual void ScalarValueIntegral(const MessageInfo& /*info_*/, bool /*value_*/) {}; - virtual void ScalarValueString (const MessageInfo& /*info_*/, const std::string& /*value_*/) {}; - virtual void ScalarValueBytes (const MessageInfo& /*info_*/, const std::string& /*value_*/) {}; - virtual void ScalarValueEnum (const MessageInfo& /*info_*/, int /*value_*/, const std::string& /*name*/) {}; - - virtual void ScalarMessageStart(const MessageInfo& /*info*/, const std::vector& /*fields_*/) {}; - virtual void ScalarMessageEnd() {}; - virtual void ArrayMessageStart(const MessageInfo& /*info*/, const std::vector& /*fields_*/){}; - virtual void ArrayMessageEnd(){}; - - virtual bool AcceptMessage(const std::string& /*name*/) { return true; }; - }; - - - // This is a convenience class - // You can inherit from this class if you'd like to only use double values, e.g. saves you the hassle of implementing - // all other array / scalar ValueIntegral functions - class MessageVisitorDoubleIntegral : public MessageVisitor - { - friend class CProtoDecoder; - protected: - using MessageVisitor::ArrayValueIntegral; - using MessageVisitor::ScalarValueIntegral; - - void ArrayValueIntegral(const eCAL::protobuf::MessageInfo& info_, google::protobuf::int32 value_) override - { - ArrayValueIntegral(info_, static_cast(value_)); - }; - void ArrayValueIntegral(const eCAL::protobuf::MessageInfo& info_, google::protobuf::uint32 value_) override - { - ArrayValueIntegral(info_, static_cast(value_)); - } - void ArrayValueIntegral(const eCAL::protobuf::MessageInfo& info_, google::protobuf::int64 value_) override - { - ArrayValueIntegral(info_, static_cast(value_)); - }; - void ArrayValueIntegral(const eCAL::protobuf::MessageInfo& info_, google::protobuf::uint64 value_) override - { - ArrayValueIntegral(info_, static_cast(value_)); - }; - void ArrayValueIntegral(const eCAL::protobuf::MessageInfo& info_, float value_) override - { - ArrayValueIntegral(info_, static_cast(value_)); - }; - void ArrayValueIntegral(const eCAL::protobuf::MessageInfo& info_, bool value_) override - { - ArrayValueIntegral(info_, static_cast(value_)); - }; - - void ScalarValueIntegral(const MessageInfo& info_, google::protobuf::int32 value_) override - { - ScalarValueIntegral(info_, static_cast(value_)); - }; - void ScalarValueIntegral(const MessageInfo& info_, google::protobuf::uint32 value_) override - { - ScalarValueIntegral(info_, static_cast(value_)); - }; - void ScalarValueIntegral(const MessageInfo& info_, google::protobuf::int64 value_) override - { - ScalarValueIntegral(info_, static_cast(value_)); - }; - void ScalarValueIntegral(const MessageInfo& info_, google::protobuf::uint64 value_) override - { - ScalarValueIntegral(info_, static_cast(value_)); - }; - void ScalarValueIntegral(const MessageInfo& info_, float value_) override - { - ScalarValueIntegral(info_, static_cast(value_)); - }; - void ScalarValueIntegral(const MessageInfo& info_, bool value_) override - { - ScalarValueIntegral(info_, static_cast(value_)); - }; - }; - } -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER \ No newline at end of file diff --git a/lib/ecal_protobuf/src/ecal_proto_decoder.cpp b/lib/ecal_protobuf/src/ecal_proto_decoder.cpp deleted file mode 100644 index d5012e8..0000000 --- a/lib/ecal_protobuf/src/ecal_proto_decoder.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief eCALMonitor proto message decoding class -**/ - -#include - -#include -#include -#include - -#include - -namespace eCAL -{ -namespace protobuf -{ - CProtoDecoder::CProtoDecoder() - : visitor(std::make_shared()) - {} - - bool CProtoDecoder::ProcProtoMsg(const google::protobuf::Message& msg_, const std::string& name_, const std::string& prefix_, bool is_Array_, size_t index_) - { - const google::protobuf::Reflection* ref_ptr = msg_.GetReflection(); - if (ref_ptr == nullptr) return false; - - auto descriptor = msg_.GetDescriptor(); - - int count = descriptor->field_count(); - - if (!is_Array_) - { - visitor->ScalarMessageStart({ name_, prefix_, (int)index_, msg_.GetTypeName()}, GetProtoMessageFieldNames(descriptor)); - } - else - { - visitor->ArrayMessageStart({ name_, prefix_, (int)index_, msg_.GetTypeName()}, GetProtoMessageFieldNames(descriptor)); - } - - std::string complete_message_name = CreateCompleteMessageName(name_, prefix_); - - - for (int i = 0; i < count; ++i) - { - auto field = descriptor->field(i); - std::string child_message_name = CreateCompleteMessageName(field->name(), complete_message_name); - - if (visitor->AcceptMessage(child_message_name)) - { - // only required in case of repeated fields - bool accept_complete_array(false); - int fsize = 0; - - if (field->is_repeated()) - { - accept_complete_array = visitor->AcceptMessage(child_message_name + "[*]"); - fsize = ref_ptr->FieldSize(msg_, field); - visitor->ArrayStart({ field->name(), complete_message_name, field->number(), field->type_name() }, field->type(), fsize); - } - - const google::protobuf::FieldDescriptor::CppType fdt = field->cpp_type(); - switch (fdt) - { - case google::protobuf::FieldDescriptor::CPPTYPE_INT32: // TYPE_INT32, TYPE_SINT32, TYPE_SFIXED32 - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) +"]")) - { - info.id = fnum; - visitor->ArrayValueIntegral(info, ref_ptr->GetRepeatedInt32(msg_, field, fnum)); - } - } - } - else - { - visitor->ScalarValueIntegral({ field->name(), complete_message_name, field->number(), field->type_name() }, ref_ptr->GetInt32(msg_, field)); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: // TYPE_UINT32, TYPE_FIXED32 - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - visitor->ArrayValueIntegral(info, ref_ptr->GetRepeatedUInt32(msg_, field, fnum)); - } - } - } - else - { - visitor->ScalarValueIntegral({field->name(), complete_message_name, field->number(), field->type_name()}, ref_ptr->GetUInt32(msg_, field)); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_INT64: // TYPE_INT64, TYPE_SINT64, TYPE_SFIXED64 - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - visitor->ArrayValueIntegral(info, ref_ptr->GetRepeatedInt64(msg_, field, fnum)); - } - } - } - else - { - visitor->ScalarValueIntegral({field->name(), complete_message_name, field->number(), field->type_name() }, ref_ptr->GetInt64(msg_, field)); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: // TYPE_UINT64, TYPE_FIXED64 - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - visitor->ArrayValueIntegral(info, ref_ptr->GetRepeatedUInt64(msg_, field, fnum)); - } - } - } - else - { - visitor->ScalarValueIntegral({field->name(), complete_message_name, field->number(), field->type_name() }, ref_ptr->GetUInt64(msg_, field)); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: // TYPE_FLOAT - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - visitor->ArrayValueIntegral(info, ref_ptr->GetRepeatedFloat(msg_, field, fnum)); - } - } - } - else - { - visitor->ScalarValueIntegral({field->name(), complete_message_name, field->number(), field->type_name() }, ref_ptr->GetFloat(msg_, field)); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: // TYPE_DOUBLE - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - visitor->ArrayValueIntegral(info, ref_ptr->GetRepeatedDouble(msg_, field, fnum)); - } - } - } - else - { - visitor->ScalarValueIntegral({field->name(), complete_message_name, field->number(), field->type_name() }, ref_ptr->GetDouble(msg_, field)); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: // TYPE_BOOL - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - visitor->ArrayValueIntegral(info, ref_ptr->GetRepeatedBool(msg_, field, fnum)); - } - } - } - else - { - visitor->ScalarValueIntegral({field->name(), complete_message_name, field->number() , field->type_name()}, ref_ptr->GetBool(msg_, field)); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TYPE_ENUM - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - visitor->ArrayValueEnum(info, ref_ptr->GetRepeatedEnum(msg_, field, fnum)->number(), ref_ptr->GetRepeatedEnum(msg_, field, fnum)->name()); - } - } - } - else - { - visitor->ScalarValueEnum({field->name(), complete_message_name, field->number(), field->type_name() }, ref_ptr->GetEnum(msg_, field)->number(), ref_ptr->GetEnum(msg_, field)->name()); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_STRING: // TYPE_STRING, TYPE_BYTES - if (field->is_repeated()) - { - MessageInfo info{field->name(), complete_message_name, 0, field->type_name()}; - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - info.id = fnum; - if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING) - { - visitor->ArrayValueString(info, ref_ptr->GetRepeatedString(msg_, field, fnum)); - } - else // TYPE_BYTES - { - visitor->ArrayValueBytes(info, ref_ptr->GetRepeatedString(msg_, field, fnum)); - } - } - } - } - else - { - if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING) - { - visitor->ScalarValueString({field->name(), complete_message_name, field->number(), field->type_name()}, ref_ptr->GetString(msg_, field)); - } - else // TYPE_BYTES - { - visitor->ScalarValueBytes({field->name(), complete_message_name, field->number(), field->type_name() }, ref_ptr->GetString(msg_, field)); - } - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: // TYPE_MESSAGE, TYPE_GROUP - { - if (field->is_repeated()) - { - for (int fnum = 0; fnum < fsize; ++fnum) - { - if (accept_complete_array || visitor->AcceptMessage(child_message_name + "[" + std::to_string(fnum) + "]")) - { - const google::protobuf::Message& msg = ref_ptr->GetRepeatedMessage(msg_, field, fnum); - std::stringstream name; - name << field->name() << "[" << fnum << "]"; - - // do not process default messages to avoid infinite recursions. - std::vector msg_fields; - msg.GetReflection()->ListFields(msg, &msg_fields); - if (msg_fields.size() > 0) - { - ProcProtoMsg(msg, name.str(), complete_message_name, true, fnum); - } - } - } - } - else - { - const google::protobuf::Message& msg = ref_ptr->GetMessage(msg_, field); - - // do not process default messages to avoid infinite recursions. - std::vector msg_fields; - msg.GetReflection()->ListFields(msg, &msg_fields); - if (msg_fields.size() > 0) - { - ProcProtoMsg(msg, field->name(), complete_message_name, false, field->number()); - } - } - } - break; - default: - break; - } - - if (field->is_repeated()) - { - visitor->ArrayEnd(); - } - } - } - if (!is_Array_) - { - visitor->ScalarMessageEnd(); - } - else - { - visitor->ArrayMessageEnd(); - } - return true; - } - -} -} diff --git a/lib/ecal_protobuf/src/ecal_proto_dyn.cpp b/lib/ecal_protobuf/src/ecal_proto_dyn.cpp deleted file mode 100644 index 0e53dec..0000000 --- a/lib/ecal_protobuf/src/ecal_proto_dyn.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * dynamic protobuf message decoder -**/ - -#include - -#include -#include -#include -#include - -namespace eCAL -{ -namespace protobuf -{ - - class ParserErrorCollector : public google::protobuf::io::ErrorCollector - { - public: - ParserErrorCollector() {} - ~ParserErrorCollector() {} - - std::string Get() { return(m_ss.str()); } - - // Indicates that there was an error in the input at the given line and - // column numbers. The numbers are zero-based, so you may want to add - // 1 to each before printing them. - void AddError(int line_, int column_, const std::string& msg_) - { - Add(line_, column_, "ERROR: " + msg_); - } - - // Indicates that there was a warning in the input at the given line and - // column numbers. The numbers are zero-based, so you may want to add - // 1 to each before printing them. - void AddWarning(int line_, int column_, const std::string& msg_) - { - Add(line_, column_, "WARNING: " + msg_); - } - - private: - void Add(int line_, int column_, const std::string& msg_) - { - m_ss << line_ << ":" << column_ << " " << msg_ << std::endl; - } - - std::stringstream m_ss; - }; - - class DescriptorErrorCollector : public google::protobuf::DescriptorPool::ErrorCollector - { - public: - DescriptorErrorCollector() {} - ~DescriptorErrorCollector() {} - - std::string Get() { return(m_ss.str()); } - - void AddError( - const std::string& filename, // File name in which the error occurred. - const std::string& element_name, // Full name of the erroneous element. - const google::protobuf::Message* descriptor, // Descriptor of the erroneous element. - ErrorLocation location, // One of the location constants, above. - const std::string& message // Human-readable error message. - ) - { - Add(filename, element_name, descriptor, location, "ERROR: " + message); - } - - void AddWarning( - const std::string& filename, // File name in which the error occurred. - const std::string& element_name, // Full name of the erroneous element. - const google::protobuf::Message* descriptor, // Descriptor of the erroneous element. - ErrorLocation location, // One of the location constants, above. - const std::string& message // Human-readable error message. - ) - { - Add(filename, element_name, descriptor, location, "WARNING: " + message); - } - - private: - void Add( - const std::string& filename, - const std::string& element_name, - const google::protobuf::Message* /*descriptor*/, - ErrorLocation location, - const std::string& message - ) - { - m_ss << filename << " " << element_name << " " << location << " " << message << std::endl; - } - - std::stringstream m_ss; - }; - - google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromFile(const std::string& proto_filename_, const std::string& msg_type_, std::string& error_s_) - { - google::protobuf::FileDescriptorProto proto; - if (!GetFileDescriptorFromFile(proto_filename_, &proto, error_s_)) return(nullptr); - google::protobuf::FileDescriptorSet pset; - google::protobuf::FileDescriptorProto* pdesc = pset.add_file(); - pdesc->CopyFrom(proto); - return(GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_)); - } - - google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromString(const std::string& proto_string_, const std::string& msg_type_, std::string& error_s_) - { - google::protobuf::FileDescriptorProto proto; - if (!GetFileDescriptorFromString(proto_string_, &proto, error_s_)) return(nullptr); - google::protobuf::FileDescriptorSet pset; - google::protobuf::FileDescriptorProto* pdesc = pset.add_file(); - pdesc->CopyFrom(proto); - return(GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_)); - } - - google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromDescriptor(const google::protobuf::FileDescriptorProto& proto_desc_, const std::string& msg_type_, std::string& error_s_) - { - google::protobuf::FileDescriptorSet pset; - google::protobuf::FileDescriptorProto* pdesc = pset.add_file(); - pdesc->CopyFrom(proto_desc_); - return(GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_)); - } - - google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromDescriptor(const std::string& msg_desc_, const std::string& msg_type_, std::string& error_s_) - { - // create file descriptor set - google::protobuf::FileDescriptorSet pset; - if (!pset.ParseFromString(msg_desc_)) - { - error_s_ = "Cannot get file descriptor of message: " + msg_type_; - return(nullptr); - } - - // create message object - google::protobuf::Message* proto_msg = GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_); - if (!proto_msg) - { - return(nullptr); - } - - return(proto_msg); - } - - google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromDescriptorSet(const google::protobuf::FileDescriptorSet& proto_desc_set_, const std::string& msg_type_, std::string& error_s_) - { - // check if msg_type_ is available in descriptor pool - const google::protobuf::Descriptor* desc = m_descriptor_pool.FindMessageTypeByName(msg_type_); - if (desc != nullptr) - { - const google::protobuf::Message* prototype_msg = m_message_factory.GetPrototype(desc); - if (prototype_msg != nullptr) - { - // ownership passed to caller here ! - google::protobuf::Message* proto_msg = prototype_msg->New(); - if (proto_msg == nullptr) - { - error_s_ = "Failed in prototype_msg->New(); to create mutable message"; - return(nullptr); - } - return(proto_msg); - } - } - - // suppose you want to parse a message type with a specific type name. - DescriptorErrorCollector error_collector; - const google::protobuf::FileDescriptor* file_desc = nullptr; - for (auto it = 0; it < proto_desc_set_.file_size(); ++it) - { - file_desc = m_descriptor_pool.BuildFileCollectingErrors(proto_desc_set_.file(it), &error_collector); - if (file_desc == nullptr) - { - error_s_ = error_collector.Get(); - return(nullptr); - } - if ((file_desc->message_type_count() > 0) && - (file_desc->message_type(0)->name() == msg_type_)) - { - break; - } - } - if (file_desc == nullptr) - { - error_s_ = "Cannot get file descriptor of message: " + msg_type_; - return(nullptr); - } - const google::protobuf::Descriptor* message_desc = file_desc->FindMessageTypeByName(msg_type_); - if (message_desc == nullptr) - { - error_s_ = "Cannot get message descriptor of message: " + msg_type_; - return(nullptr); - } - - const google::protobuf::Message* prototype_msg = m_message_factory.GetPrototype(message_desc); - if (prototype_msg == nullptr) - { - error_s_ = "Cannot create prototype message from message descriptor"; - return(nullptr); - } - - // ownership passed to caller here ! - google::protobuf::Message* proto_msg = prototype_msg->New(); - if (proto_msg == nullptr) - { - error_s_ = "Failed in prototype_msg->New(); to create mutable message"; - return(nullptr); - } - return(proto_msg); - } - - google::protobuf::DescriptorPool* CProtoDynDecoder::GetDescriptorPool() - { - return &m_descriptor_pool; - } - - bool CProtoDynDecoder::GetFileDescriptorFromFile(const std::string& proto_filename_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_) - { - using namespace google::protobuf; - using namespace google::protobuf::io; - using namespace google::protobuf::compiler; - - std::ifstream fs(proto_filename_); - if (!fs.is_open()) - { - std::cout << "Cannot open .proto file: " << proto_filename_; - return(false); - } - std::stringstream ss; - ss << fs.rdbuf(); - - std::string proto_str = ss.str(); - - return(GetFileDescriptorFromString(proto_str, file_desc_proto_, error_s_)); - } - - bool CProtoDynDecoder::GetFileDescriptorFromString(const std::string& proto_string_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_) - { - using namespace google::protobuf; - using namespace google::protobuf::io; - using namespace google::protobuf::compiler; - - std::stringstream ss; - ss << proto_string_; - IstreamInputStream proto_string_input_stream(&ss); - - Tokenizer tokenizer(&proto_string_input_stream, nullptr); - Parser parser; - ParserErrorCollector error_collector; - parser.RecordErrorsTo(&error_collector); - if (!parser.Parse(&tokenizer, file_desc_proto_)) - { - error_s_ = error_collector.Get(); - return(false); - } - - // here we walk around a bug in protocol buffers that - // |Parser::Parse| does not set name (.proto filename) in - // file_desc_proto. - if (!file_desc_proto_->has_name()) - { - file_desc_proto_->set_name("foo.proto"); - } - - return(true); - } -} -} diff --git a/lib/ecal_protobuf/src/ecal_proto_maximum_array_dimensions.cpp b/lib/ecal_protobuf/src/ecal_proto_maximum_array_dimensions.cpp deleted file mode 100644 index 37b81a2..0000000 --- a/lib/ecal_protobuf/src/ecal_proto_maximum_array_dimensions.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include - -namespace eCAL -{ - namespace protobuf - { - // returns the max size of the element, or 0 if the element had not been registered. - size_t MaximumArrayDimensionsVisitor::MaxSize(const std::string & element) - { - auto it_size = max_sizes.find(element); - if (it_size != max_sizes.end()) - { - return it_size->second; - } - else - { - return 0; - } - } - - void MaximumArrayDimensionsVisitor::ArrayStart(const MessageInfo& info_, const google::protobuf::FieldDescriptor::Type & /*type_*/, size_t size_) - { - std::string complete_name = CreateCompleteMessageName(info_.field_name, info_.group_name); - auto& size = max_sizes[complete_name]; - if (size < size_) - { - size = size_; - } - } - } -} \ No newline at end of file diff --git a/lib/ecal_protobuf/src/ecal_proto_message_filter.cpp b/lib/ecal_protobuf/src/ecal_proto_message_filter.cpp deleted file mode 100644 index ff46e91..0000000 --- a/lib/ecal_protobuf/src/ecal_proto_message_filter.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include -#include - -namespace eCAL -{ - namespace protobuf - { - bool StartsWith(const std::string& haystack, const std::string& needle) { - return needle.length() <= haystack.length() - && equal(needle.begin(), needle.end(), haystack.begin()); - } - - /* - * Creates a set that includes all sub - elements: - * create_set("a.b.c[1].d") - * returns a set with the following elements: - * - a - * - a.b - * - a.b.c - * - a.b.c[1] - * - a.b.c[1].d - */ - std::set CreateSet(const std::string& input_) - { - std::set elements; - std::string item; // individual item - std::string include; // item to be put in set - std::stringstream input(input_); - while (std::getline(input, item, '.')) - { - if (!include.empty()) - { - include += '.'; - } - - // If adding array info, we need to add both the - // first part and the whole part to the elements vector - // include = a.b. , item = c[1] - // add a.b.c - // and add a.b.c[1] - auto found = item.find('['); - if (found != std::string::npos) - { - auto extra = item.substr(0, found); - elements.insert(extra); - } - - include += item; - elements.insert(include); - } - return elements; - } - - void BaseIncludeFilter::Clear() - { - std::lock_guard lock(elements_mutex); - elements.clear(); - } - - void BaseIncludeFilter::Insert(const std::string& include_) - { - std::lock_guard lock(elements_mutex); - // Compute elements to add - auto new_elements = CreateSet(include_); - // make a union between the existing set and the new_elements - std::set_union(elements.begin(), elements.end(), - new_elements.begin(), new_elements.end(), - std::inserter(elements, elements.end())); - } - - - void BaseIncludeFilter::Erase(const std::string& include_) - { - std::lock_guard lock(elements_mutex); - // This function is a little more complicated - auto all_identifiers = CreateSet(include_); - // Now move backwards and remove all elements if the following element is not in the namespace - // example - // a, a.b, a.b.c, a.d, a.d.c, a.d.e, a.e - // removing a.b.c would remove a.b. and a.b.c - // removing a.d.c would remove only a.d.c - for (auto id = all_identifiers.rbegin(); id != all_identifiers.rend(); ++id) - { - // Find the element and the element afterwards - auto elem = elements.find(*id); - auto following_item = elem; - ++following_item; - - // If the following element starts with the same string, we cannot remove our element - // nor any of the shorter prefixes, thus we break the loop. - if ((following_item != elements.end()) && StartsWith(*following_item, *elem)) - break; - elements.erase(elem); - } - } - - SimpleIncludeFilter::SimpleIncludeFilter() - { - } - - bool SimpleIncludeFilter::Filter(const std::string & message_name_) - { - std::lock_guard lock(elements_mutex); - return elements.find(message_name_) != elements.end(); - } - - ComplexIncludeFilter::ComplexIncludeFilter() - : filter_regex("([^\\[]+)(?:\\[(\\d+|\\*)\\])?") - { - } - - bool ComplexIncludeFilter::Filter(const std::string& message_name_) - { - // Ok, what's the problem here? - // In case we get a request a[1].b.c[2].d[3] - // Our Map might contain a[1].b.c[*].d[3] - // Or even a[*].b.c[*].d[*], - // so any combination of indices and stars lead to a valid request. - // So we need an algorithm to figure out if the request is actually valid. - // Proposed algo: - std::lock_guard lock(elements_mutex); - std::string elem_to_find; - std::smatch match; - - auto message_name = message_name_; - - while (std::regex_search(message_name, match, filter_regex)) - { - if (match[2].matched) // did the regex match brackets - { - bool is_star(match[1].str() == "*"); - std::string number_try = elem_to_find + match[1].str() + "[" + match[2].str() + "]"; - std::string star_try = elem_to_find + match[1].str() + "[*]"; - - // The elements set contains a[1] - if (!is_star && elements.find(number_try) != elements.end()) - { - elem_to_find += number_try; - } - // The elements set contains a[*] - else if (elements.find(star_try) != elements.end()) - { - elem_to_find += star_try; - } - else - { - return false; - } - - } - else // The last match might only have one match - { - elem_to_find += match[0].str(); - return (elements.find(elem_to_find) != elements.end()); - } - message_name = match.suffix().str(); - } - - return true; - } -} -} \ No newline at end of file diff --git a/lib/ecal_protobuf/src/ecal_proto_visitor.cpp b/lib/ecal_protobuf/src/ecal_proto_visitor.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/licenses/capnproto/LICENSE b/licenses/capnproto/LICENSE deleted file mode 100644 index 1eabc94..0000000 --- a/licenses/capnproto/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2013-2017 Sandstorm Development Group, Inc.; Cloudflare, Inc.; -and other contributors. Each commit is copyright by its respective author or -author's employer. - -Licensed under the MIT License: - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/licenses/google-flatbuffers/LICENSE.txt b/licenses/google-flatbuffers/LICENSE.txt deleted file mode 100644 index a4c5efd..0000000 --- a/licenses/google-flatbuffers/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2014 Google Inc. - - 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. diff --git a/logos/ecal-cop-image.png b/logos/ecal-cop-image.png new file mode 100644 index 0000000000000000000000000000000000000000..8ba13d7642b2dae0645046811c2804caa22b9854 GIT binary patch literal 314250 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

-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

`EecIIL$noy;jWm6@XE(Ug#HUrrrjqWi$o}S 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πfVfg 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 z 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�i=u6k38O#=;3ov#Rumhi16qT`t|j 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 z -#include - -int main(int argc, char **argv) -{ - // the client handle - ECAL_HANDLE hclient = 0; - - // initialize eCAL API - eCAL_Initialize(argc, argv, "minimal client c", eCAL_Init_Default); - - // create client for "service1" - hclient = eCAL_Client_Create("service1"); - - // call service method - while (eCAL_Ok()) - { - struct SServiceResponseC service_response; - char request[] = "HELLO"; - char response[sizeof(request)] = { 0 }; - // call method "echo" - printf("Calling service1:echo ..\n"); - if (eCAL_Client_Call_Wait(hclient, "echo", request, sizeof(request), -1, &service_response, &response, sizeof(response))) - { - // process response - switch (service_response.call_state) - { - case call_state_executed: - printf("Method 'echo' executed. Response : "); - printf("%s", response); - printf("\n\n"); - break; - case call_state_failed: - printf("Method 'echo' failed. Error : "); - printf("%s", service_response.error_msg); - printf("\n\n"); - break; - default: - break; - } - } - else - { - printf("Service / method not found :-(\n\n"); - } - - // sleep a second - eCAL_Process_SleepMS(1000); - } - - // destroy client for "service1" - eCAL_Client_Destroy(hclient); - - // finalize eCAL API - eCAL_Finalize(eCAL_Init_All); - - return(0); -} diff --git a/samples/c/services/minimal_server_c/CMakeLists.txt b/samples/c/services/minimal_server_c/CMakeLists.txt deleted file mode 100644 index acc620b..0000000 --- a/samples/c/services/minimal_server_c/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(minimal_server_c) - -find_package(eCAL REQUIRED) - -remove_definitions(-std=c++14) - -set(minimal_server_c_src - src/minimal_server_c.c -) - -ecal_add_sample(${PROJECT_NAME} ${minimal_server_c_src}) -target_include_directories(${PROJECT_NAME} PRIVATE .) - -target_link_libraries(${PROJECT_NAME} eCAL::core_c) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/c/services) diff --git a/samples/c/services/minimal_server_c/src/minimal_server_c.c b/samples/c/services/minimal_server_c/src/minimal_server_c.c deleted file mode 100644 index 26a07c6..0000000 --- a/samples/c/services/minimal_server_c/src/minimal_server_c.c +++ /dev/null @@ -1,77 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include - -int OnMethodCallback(const char* method_, const char* req_type_, const char* resp_type_, const char* request_, int request_len_, void** response_, int* response_len_, void* par_) -{ - // unused param - (void)req_type_; - (void)resp_type_; - (void)par_; - - static char response_buf[1024]; - if ((unsigned int)request_len_ > sizeof(response_buf)) return 0; - - // echo request to response - memcpy(response_buf, request_, request_len_); - - *response_ = response_buf; - *response_len_ = request_len_; - - printf("Method : %s called\n", method_); - printf("Request : %s\n", request_); - printf("Response : %s\n", (char*)(*response_)); - printf("\n"); - - // return success - return 42; -} - -int main(int argc, char **argv) -{ - ECAL_HANDLE hserver = 0; - - // initialize eCAL API - eCAL_Initialize(argc, argv, "minimal server c", eCAL_Init_Default); - - // create server "service1" - hserver = eCAL_Server_Create("service1"); - - // add method callback for method "echo" - eCAL_Server_AddMethodCallback(hserver, "echo", "", "", OnMethodCallback, 0); - - // idle - while (eCAL_Ok()) - { - // sleep 100 ms - eCAL_Process_SleepMS(100); - } - - // destroy server "service1" - eCAL_Server_Destroy(hserver); - - // finalize eCAL API - eCAL_Finalize(eCAL_Init_All); - - return(0); -} diff --git a/samples/cpp/binary/binary_rec_cb/CMakeLists.txt b/samples/cpp/benchmarks/counter_rec/CMakeLists.txt similarity index 88% rename from samples/cpp/binary/binary_rec_cb/CMakeLists.txt rename to samples/cpp/benchmarks/counter_rec/CMakeLists.txt index 97905c3..be32ff2 100644 --- a/samples/cpp/binary/binary_rec_cb/CMakeLists.txt +++ b/samples/cpp/benchmarks/counter_rec/CMakeLists.txt @@ -20,15 +20,15 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(binary_rec_cb) +project(counter_rec) find_package(eCAL REQUIRED) -set(binary_rec_cb_src - src/binary_rec_cb.cpp +set(counter_rec_src + src/counter_rec.cpp ) -ecal_add_sample(${PROJECT_NAME} ${binary_rec_cb_src}) +ecal_add_sample(${PROJECT_NAME} ${counter_rec_src}) target_link_libraries(${PROJECT_NAME} eCAL::core) @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/binary) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/counter) diff --git a/samples/cpp/counter/counter_rec_cb/src/counter_rec_cb.cpp b/samples/cpp/benchmarks/counter_rec/src/counter_rec.cpp similarity index 64% rename from samples/cpp/counter/counter_rec_cb/src/counter_rec_cb.cpp rename to samples/cpp/benchmarks/counter_rec/src/counter_rec.cpp index d15df85..3a323f1 100644 --- a/samples/cpp/counter/counter_rec_cb/src/counter_rec_cb.cpp +++ b/samples/cpp/benchmarks/counter_rec/src/counter_rec.cpp @@ -21,41 +21,38 @@ #include -// globals -long long g_clock(0); -long long g_first_clock(-1); - -// subscriber callback function -void OnReceive(const char* /* topic_name_ */, const struct eCAL::SReceiveCallbackData* data_) -{ - long long clock = ((long long*)data_->buf)[0]; - if(g_first_clock < 0) - { - g_first_clock = clock; - } - - if(g_clock != clock - g_first_clock) - { - std::cout << "Out of sync : " << (clock - g_first_clock) - g_clock << std::endl; - g_first_clock = -1; - g_clock = 0; - } - else - { - g_clock++; - } -} - int main(int argc, char **argv) { // initialize eCAL API eCAL::Initialize(argc, argv, "counter_rec_cb"); // create subscriber for topic "Counter" - eCAL::CSubscriber sub("Counter", "long long"); + eCAL::CSubscriber sub("Counter", { "", "long long", "" }); + + // counter + long long g_clock(0); + long long g_first_clock(-1); + + // add callback + auto on_receive = [&](const struct eCAL::SReceiveCallbackData* data_) { + long long const clock = reinterpret_cast(data_->buf)[0]; + if(g_first_clock < 0) + { + g_first_clock = clock; + } - // setup receive callback function - sub.AddReceiveCallback(OnReceive); + if(g_clock != clock - g_first_clock) + { + std::cout << "Out of sync : " << (clock - g_first_clock) - g_clock << std::endl; + g_first_clock = -1; + g_clock = 0; + } + else + { + g_clock++; + } + }; + sub.AddReceiveCallback(std::bind(on_receive, std::placeholders::_2)); // idle main thread while(eCAL::Ok()) diff --git a/samples/cpp/counter/counter_snd/CMakeLists.txt b/samples/cpp/benchmarks/counter_snd/CMakeLists.txt similarity index 98% rename from samples/cpp/counter/counter_snd/CMakeLists.txt rename to samples/cpp/benchmarks/counter_snd/CMakeLists.txt index e5a46e3..ec05a75 100644 --- a/samples/cpp/counter/counter_snd/CMakeLists.txt +++ b/samples/cpp/benchmarks/counter_snd/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/counter) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/counter) diff --git a/samples/cpp/counter/counter_snd/src/counter_snd.cpp b/samples/cpp/benchmarks/counter_snd/src/counter_snd.cpp similarity index 67% rename from samples/cpp/counter/counter_snd/src/counter_snd.cpp rename to samples/cpp/benchmarks/counter_snd/src/counter_snd.cpp index 8549db2..9f372db 100644 --- a/samples/cpp/counter/counter_snd/src/counter_snd.cpp +++ b/samples/cpp/benchmarks/counter_snd/src/counter_snd.cpp @@ -34,23 +34,20 @@ int main(int argc, char **argv) eCAL::Initialize(argc, argv, "counter_snd"); // create publisher for topic "Counter" - eCAL::CPublisher pub("Counter", "long long"); + eCAL::CPublisher pub("Counter", { "", "long long", "" }); // timer - std::chrono::steady_clock::time_point start_time(std::chrono::nanoseconds(0)); - long long clock (0); - int msgs (0); - unsigned long long bytes (0); - size_t slen (0); + long long clock(0); + long long msgs (0); + long long bytes(0); // default send string std::vector send_a(payload_size); std::cout << "Message size = " << int(payload_size) << " Byte = " << int(payload_size/1024) << " kByte = " << int(payload_size/1024/1024) << " MByte" << std::endl << std::endl; - slen = payload_size; // send updates - start_time = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); while(eCAL::Ok()) { // send content @@ -59,24 +56,24 @@ int main(int argc, char **argv) // collect data clock++; msgs++; - bytes += slen; + bytes += payload_size; // put the clock into the send buffer - ((long long*)send_a.data())[0] = clock; + reinterpret_cast(send_a.data())[0] = clock; // check timer and print results every second if(clock%10000 == 0) { - std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; + const std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; if (diff_time >= std::chrono::duration(1.0)) { start_time = std::chrono::steady_clock::now(); std::stringstream out; - out << "Message size (kByte): " << int(slen / 1024) << std::endl; - out << "kByte/s: " << int(bytes / 1024 / diff_time.count()) << std::endl; - out << "MByte/s: " << int(bytes / 1024 / 1024 / diff_time.count()) << std::endl; - out << "Messages/s: " << int(msgs / diff_time.count()) << std::endl; - out << "Clock: " << clock << std::endl; + out << "Message size (kByte): " << (unsigned int)(payload_size / 1024.0) << std::endl; + out << "kByte/s: " << (unsigned int)(bytes / 1024.0 / diff_time.count()) << std::endl; + out << "MByte/s: " << (unsigned int)(bytes / 1024.0 / 1024.0 / diff_time.count()) << std::endl; + out << "Messages/s: " << (unsigned int)(msgs / diff_time.count()) << std::endl; + out << "Clock: " << clock << std::endl; std::cout << out.str() << std::endl; msgs = 0; bytes = 0; diff --git a/samples/cpp/datarate/datarate_rec/CMakeLists.txt b/samples/cpp/benchmarks/datarate_rec/CMakeLists.txt similarity index 98% rename from samples/cpp/datarate/datarate_rec/CMakeLists.txt rename to samples/cpp/benchmarks/datarate_rec/CMakeLists.txt index 11fb438..92a0884 100644 --- a/samples/cpp/datarate/datarate_rec/CMakeLists.txt +++ b/samples/cpp/benchmarks/datarate_rec/CMakeLists.txt @@ -39,4 +39,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/datarate) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/datarate) diff --git a/samples/cpp/datarate/datarate_rec/src/datarate_rec.cpp b/samples/cpp/benchmarks/datarate_rec/src/datarate_rec.cpp similarity index 79% rename from samples/cpp/datarate/datarate_rec/src/datarate_rec.cpp rename to samples/cpp/benchmarks/datarate_rec/src/datarate_rec.cpp index ac3379b..2d3dc63 100644 --- a/samples/cpp/datarate/datarate_rec/src/datarate_rec.cpp +++ b/samples/cpp/benchmarks/datarate_rec/src/datarate_rec.cpp @@ -27,16 +27,6 @@ #include #include -std::vector rec_buffer; - -// subscriber callback function -void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) -{ - // make a memcpy to emulate user action - rec_buffer.reserve(data_->size); - std::memcpy(rec_buffer.data(), data_->buf, data_->size); -} - // main entry int main(int argc, char** argv) { @@ -47,7 +37,7 @@ int main(int argc, char** argv) cmd.parse(argc, argv); // get parameters - std::string topic_name(arg_topic_name.getValue()); + const std::string topic_name(arg_topic_name.getValue()); // log parameter std::cout << "Topic name = " << topic_name << std::endl; @@ -59,7 +49,13 @@ int main(int argc, char** argv) eCAL::CSubscriber sub(topic_name); // add callback - sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2)); + std::vector rec_buffer; + auto on_receive = [&](const struct eCAL::SReceiveCallbackData* data_) { + // make a memory copy to emulate user action + rec_buffer.reserve(data_->size); + std::memcpy(rec_buffer.data(), data_->buf, data_->size); + }; + sub.AddReceiveCallback(std::bind(on_receive, std::placeholders::_2)); // idle main thread while (eCAL::Ok()) diff --git a/samples/cpp/datarate/datarate_snd/CMakeLists.txt b/samples/cpp/benchmarks/datarate_snd/CMakeLists.txt similarity index 98% rename from samples/cpp/datarate/datarate_snd/CMakeLists.txt rename to samples/cpp/benchmarks/datarate_snd/CMakeLists.txt index 5da8a6c..01a7d62 100644 --- a/samples/cpp/datarate/datarate_snd/CMakeLists.txt +++ b/samples/cpp/benchmarks/datarate_snd/CMakeLists.txt @@ -39,4 +39,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/datarate) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/datarate) diff --git a/samples/cpp/datarate/datarate_snd/src/datarate_snd.cpp b/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp similarity index 53% rename from samples/cpp/datarate/datarate_snd/src/datarate_snd.cpp rename to samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp index b7dcc53..e90c606 100644 --- a/samples/cpp/datarate/datarate_snd/src/datarate_snd.cpp +++ b/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp @@ -31,31 +31,23 @@ int main(int argc, char **argv) { // parse command line TCLAP::CmdLine cmd("datarate_snd"); - TCLAP::ValueArg arg_topic_name( "t", "topic_name", "Topic name to publish.", false, "topic", "string" ); - TCLAP::ValueArg arg_size( "s", "size", "Message size in MB.", false, 8, "int" ); - TCLAP::ValueArg arg_sleep( "d", "delay", "Delay between publication in ms.", false, 1, "int" ); - TCLAP::SwitchArg arg_zero_copy( "z", "zero_copy", "Zero copy mode on/off." ); - TCLAP::ValueArg arg_buffer_count( "b", "buffer_count", "Number of memory file buffers.", false, 1, "int" ); + TCLAP::ValueArg arg_topic_name ("t", "topic_name", "Topic name to publish.", false, "topic", "string"); + TCLAP::ValueArg arg_size ("s", "size", "Message size in MB.", false, 8, "int"); + TCLAP::ValueArg arg_sleep ("d", "delay", "Delay between publication in ms.", false, 1, "int"); cmd.add(arg_topic_name); cmd.add(arg_size); cmd.add(arg_sleep); - cmd.add(arg_zero_copy); - cmd.add(arg_buffer_count); cmd.parse(argc, argv); // get parameters - std::string topic_name(arg_topic_name.getValue()); - size_t size(arg_size.getValue()); - int sleep(arg_sleep.getValue()); - bool zero_copy(arg_zero_copy.getValue()); - int buffer_count(arg_buffer_count.getValue()); + const std::string topic_name(arg_topic_name.getValue()); + size_t size(arg_size.getValue()); + const int sleep(arg_sleep.getValue()); // log parameter - std::cout << "Topic name = " << topic_name << std::endl; - std::cout << "Message size = " << size << " MByte" << std::endl; - std::cout << "Sleep time = " << sleep << " ms" << std::endl; - std::cout << "Zero copy mode = " << zero_copy << std::endl; - std::cout << "Buffer count = " << buffer_count << std::endl; + std::cout << "Topic name = " << topic_name << std::endl; + std::cout << "Message size = " << size << " MByte" << std::endl; + std::cout << "Sleep time = " << sleep << " ms" << std::endl; // initialize eCAL API eCAL::Initialize(argc, argv, "datarate_snd"); @@ -72,19 +64,13 @@ int main(int argc, char **argv) } send_s.resize(size); - // set zero copy - pub.ShmEnableZeroCopy(zero_copy); - - // set buffering - pub.ShmSetBufferCount(buffer_count); - // send updates while(eCAL::Ok()) { // send content pub.Send(send_s); // sleep - std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + if(sleep > 0) std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); } // destroy publisher diff --git a/samples/cpp/latency/latency_rec/CMakeLists.txt b/samples/cpp/benchmarks/latency_rec/CMakeLists.txt similarity index 98% rename from samples/cpp/latency/latency_rec/CMakeLists.txt rename to samples/cpp/benchmarks/latency_rec/CMakeLists.txt index adb2d47..7ad2d71 100644 --- a/samples/cpp/latency/latency_rec/CMakeLists.txt +++ b/samples/cpp/benchmarks/latency_rec/CMakeLists.txt @@ -41,4 +41,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/latency) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/latency) diff --git a/samples/cpp/latency/latency_rec/src/latency_log.cpp b/samples/cpp/benchmarks/latency_rec/src/latency_log.cpp similarity index 71% rename from samples/cpp/latency/latency_rec/src/latency_log.cpp rename to samples/cpp/benchmarks/latency_rec/src/latency_log.cpp index ccb3830..7c8a9e2 100644 --- a/samples/cpp/latency/latency_rec/src/latency_log.cpp +++ b/samples/cpp/benchmarks/latency_rec/src/latency_log.cpp @@ -39,26 +39,26 @@ void evaluate(std::vector& lat_arr_, size_t rec_size_, size_t warmups } // evaluate all - size_t sum_msg = lat_arr_.size(); + const size_t sum_msg = lat_arr_.size(); ss << "--------------------------------------------" << std::endl; ss << "Messages received : " << sum_msg << std::endl; if (sum_msg > warmups_) { - long long sum_time = std::accumulate(lat_arr_.begin(), lat_arr_.end(), 0LL); - long long avg_time = sum_time / sum_msg; - auto min_it = std::min_element(lat_arr_.begin(), lat_arr_.end()); - auto max_it = std::max_element(lat_arr_.begin(), lat_arr_.end()); - size_t min_pos = min_it - lat_arr_.begin(); - size_t max_pos = max_it - lat_arr_.begin(); - long long min_time = *min_it; - long long max_time = *max_it; + const long long sum_time = std::accumulate(lat_arr_.begin(), lat_arr_.end(), 0LL); + const long long avg_time = sum_time / sum_msg; + auto min_it = std::min_element(lat_arr_.begin(), lat_arr_.end()); + auto max_it = std::max_element(lat_arr_.begin(), lat_arr_.end()); + const size_t min_pos = min_it - lat_arr_.begin(); + const size_t max_pos = max_it - lat_arr_.begin(); + const long long min_time = *min_it; + const long long max_time = *max_it; ss << "Message size received : " << rec_size_ / 1024 << " kB" << std::endl; ss << "Message average latency : " << avg_time << " us" << std::endl; ss << "Message min latency : " << min_time << " us @ " << min_pos << std::endl; ss << "Message max latency : " << max_time << " us @ " << max_pos << std::endl; - ss << "Throughput : " << static_cast(((rec_size_ * sum_msg) / 1024.0) / (sum_time / 1000.0 / 1000.0)) << " kB/s" << std::endl; - ss << " : " << static_cast(((rec_size_ * sum_msg) / 1024.0 / 1024.0) / (sum_time / 1000.0 / 1000.0)) << " MB/s" << std::endl; - ss << " : " << static_cast(sum_msg / (sum_time / 1000.0 / 1000.0)) << " Msg/s" << std::endl; + ss << "Throughput : " << static_cast(((rec_size_ * sum_msg) / 1024.0 / 1024.0) / (sum_time / 1000.0 / 1000.0)) << " MB/s" << std::endl; + ss << " : " << static_cast(((rec_size_ * sum_msg) / 1024.0 / 1024.0 / 1024.0) / (sum_time / 1000.0 / 1000.0)) << " GB/s" << std::endl; + ss << " : " << static_cast((sum_msg / 1000.0) / (sum_time / 1000.0 / 1000.0)) << " kMsg/s" << std::endl; } ss << "--------------------------------------------" << std::endl; @@ -68,9 +68,9 @@ void evaluate(std::vector& lat_arr_, size_t rec_size_, size_t warmups // log into logfile (append) if (!log_file_.empty()) { - std::ofstream ofile; - ofile.open(log_file_, std::ios::out | std::ios::app); - ofile << ss.str(); + std::ofstream ostream; + ostream.open(log_file_, std::ios::out | std::ios::app); + ostream << ss.str(); } } @@ -80,10 +80,10 @@ void log2file(std::vector& lat_arr_, size_t rec_size_, std::string& l { std::stringstream ss; ss << std::setw(6) << std::setfill('0') << rec_size_ / 1024; - std::string rec_size_s = ss.str(); + const std::string rec_size_s = ss.str(); - std::ofstream ofile(rec_size_s + "-" + log_file_); - std::ostream_iteratoroutput_iterator(ofile, "\n"); + std::ofstream ostream(rec_size_s + "-" + log_file_); + const std::ostream_iteratoroutput_iterator(ostream, "\n"); std::copy(lat_arr_.begin(), lat_arr_.end(), output_iterator); } } diff --git a/samples/cpp/latency/latency_rec/src/latency_log.h b/samples/cpp/benchmarks/latency_rec/src/latency_log.h similarity index 100% rename from samples/cpp/latency/latency_rec/src/latency_log.h rename to samples/cpp/benchmarks/latency_rec/src/latency_log.h diff --git a/samples/cpp/latency/latency_rec/src/latency_rec.cpp b/samples/cpp/benchmarks/latency_rec/src/latency_rec.cpp similarity index 94% rename from samples/cpp/latency/latency_rec/src/latency_rec.cpp rename to samples/cpp/benchmarks/latency_rec/src/latency_rec.cpp index 7de7ca4..87a074c 100644 --- a/samples/cpp/latency/latency_rec/src/latency_rec.cpp +++ b/samples/cpp/benchmarks/latency_rec/src/latency_rec.cpp @@ -47,7 +47,7 @@ void on_receive(const struct eCAL::SReceiveCallbackData* data_, SCallbackPar* pa auto rec_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); // update latency, size and msg number - std::lock_guard lock(par_->mtx); + const std::lock_guard lock(par_->mtx); par_->latency_array.push_back(rec_time - data_->time); par_->rec_size = data_->size; par_->msg_num++; @@ -75,7 +75,7 @@ void do_run(int delay_, std::string& log_file_) // check once a second if we still receive new messages // if not, we stop and evaluate this run { - std::lock_guard lock(cb_par.mtx); + const std::lock_guard lock(cb_par.mtx); if ((cb_par.msg_num > 0) && (msg_last == cb_par.msg_num)) break; else msg_last = cb_par.msg_num; } @@ -108,7 +108,10 @@ int main(int argc, char** argv) cmd.parse(argc, argv); // run tests - do { do_run(delay.getValue(), log_file.getValue()); } while (eCAL::Ok()); + while(eCAL::Ok()) + { + do_run(delay.getValue(), log_file.getValue()); + } } catch (TCLAP::ArgException& e) // catch any exceptions { diff --git a/samples/cpp/latency/latency_snd/CMakeLists.txt b/samples/cpp/benchmarks/latency_snd/CMakeLists.txt similarity index 98% rename from samples/cpp/latency/latency_snd/CMakeLists.txt rename to samples/cpp/benchmarks/latency_snd/CMakeLists.txt index e93a6d1..f182920 100644 --- a/samples/cpp/latency/latency_snd/CMakeLists.txt +++ b/samples/cpp/benchmarks/latency_snd/CMakeLists.txt @@ -39,4 +39,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/latency) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/latency) diff --git a/samples/cpp/services/ecalplayer_gui_client/src/ecalplayer_gui_client.h b/samples/cpp/benchmarks/latency_snd/src/binary_payload_writer.h similarity index 50% rename from samples/cpp/services/ecalplayer_gui_client/src/ecalplayer_gui_client.h rename to samples/cpp/benchmarks/latency_snd/src/binary_payload_writer.h index d5354c3..bea3b85 100644 --- a/samples/cpp/services/ecalplayer_gui_client/src/ecalplayer_gui_client.h +++ b/samples/cpp/benchmarks/latency_snd/src/binary_payload_writer.h @@ -19,43 +19,37 @@ #pragma once -#include +#include +#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100 4505 4800) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ui_main_window.h" - -class EcalplayGuiClient : public QMainWindow +// a binary payload +class CBinaryPayload : public eCAL::CPayloadWriter { - Q_OBJECT - public: - EcalplayGuiClient(QWidget *parent = Q_NULLPTR); - ~EcalplayGuiClient(); - -private slots: - void getConfigRequest(); - void setConfigRequest(); - void commandRequest(); - -signals: - void setResponseSignal(QString response); - -private: - void onPlayerResponse(const struct eCAL::SServiceResponse& service_response_); + CBinaryPayload(size_t size_) : size(size_) {} + + bool WriteFull(void* buf_, size_t len_) override + { + // write complete content to the shared memory file + if (len_ < size) return false; + memset(buf_, 42, size); + return true; + }; + + bool WriteModified(void* buf_, size_t len_) override + { + // update content of the shared memory file + if (len_ < size) return false; + const size_t write_idx((clock % 1024) % len_); + const char write_chr(clock % 10 + 48); + static_cast(buf_)[write_idx] = write_chr; + clock++; + return true; + }; + + size_t GetSize() override { return size; }; private: - Ui::EcalplayGuiServiceMainWindow ui_; - - eCAL::protobuf::CServiceClient player_service_; + size_t size = 0; + int clock = 0; }; diff --git a/samples/cpp/latency/latency_snd/src/latency_snd.cpp b/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp similarity index 72% rename from samples/cpp/latency/latency_snd/src/latency_snd.cpp rename to samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp index d78a36c..a25462e 100644 --- a/samples/cpp/latency/latency_snd/src/latency_snd.cpp +++ b/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp @@ -24,25 +24,18 @@ #include #include +#include "binary_payload_writer.h" + // warmup runs not to measure const int warmups(100); // single test run -void do_run(const int runs, int snd_size /*kB*/, int mem_buffer, bool zero_copy) +void do_run(const int runs, int snd_size /*kB*/) { // log parameter std::cout << "--------------------------------------------" << std::endl; std::cout << "Runs : " << runs << std::endl; std::cout << "Message size : " << snd_size << " kB" << std::endl; - std::cout << "Memory buffer : " << mem_buffer << std::endl; - if (zero_copy) - { - std::cout << "Zero copy : ON" << std::endl; - } - else - { - std::cout << "Zero copy : OFF" << std::endl; - } // initialize eCAL API eCAL::Initialize(0, nullptr, "latency_snd"); @@ -50,26 +43,20 @@ void do_run(const int runs, int snd_size /*kB*/, int mem_buffer, bool zero_copy) // create publisher and subscriber eCAL::CPublisher pub("ping"); - // set number of publisher memory buffers - pub.ShmSetBufferCount(mem_buffer); - - // enable zero copy mode - pub.ShmEnableZeroCopy(zero_copy); - // prepare send buffer - std::vector snd_array(snd_size * 1024); + CBinaryPayload payload(snd_size * 1024); // let them match std::this_thread::sleep_for(std::chrono::milliseconds(2000)); // add some extra loops for warmup :-) int run(0); - for (run = 0; run < runs+warmups; ++run) + for (; run < runs+warmups; ++run) { // get microseconds auto snd_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); // send message (with receive timeout 100 ms) - pub.Send(snd_array.data(), snd_array.size(), snd_time, 100 /*ms*/); + pub.Send(payload, snd_time); } // log test @@ -91,23 +78,19 @@ int main(int argc, char **argv) TCLAP::CmdLine cmd("latency_snd"); TCLAP::ValueArg runs ("r", "runs", "Number of messages to send.", false, 5000, "int"); TCLAP::ValueArg size ("s", "size", "Messages size in kB.", false, -1, "int"); - TCLAP::ValueArg mem_buffer ("b", "mem_buffer", "Number of memory files per connection.", false, 1, "int"); - TCLAP::SwitchArg zero_copy ("z", "zero_copy", "Switch zero copy mode on.", false); cmd.add(runs); cmd.add(size); - cmd.add(mem_buffer); - cmd.add(zero_copy); cmd.parse(argc, argv); if(size < 0) { // automatic size mode - for (int s = 1; s <= 32768; s *= 2) do_run(runs.getValue(), s, mem_buffer.getValue(), zero_copy.getValue()); + for (int s = 1; s <= 32768; s *= 2) do_run(runs.getValue(), s); } else { // run single test - do_run(runs.getValue(), size.getValue(), mem_buffer.getValue(), zero_copy.getValue()); + do_run(runs.getValue(), size.getValue()); } } catch (TCLAP::ArgException &e) // catch any exceptions diff --git a/samples/cpp/benchmarks/many_connections/CMakeLists.txt b/samples/cpp/benchmarks/many_connections/CMakeLists.txt deleted file mode 100644 index 608497d..0000000 --- a/samples/cpp/benchmarks/many_connections/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(many_connections) - -find_package(eCAL REQUIRED) - -ecal_add_sample(many_connections_snd - many_connections_snd.cpp -) - -ecal_add_sample(many_connections_rec - many_connections_rec.cpp -) - -target_link_libraries(many_connections_snd PRIVATE eCAL::core) -target_link_libraries(many_connections_rec PRIVATE eCAL::core) - -target_compile_features(many_connections_snd PRIVATE cxx_std_14) -target_compile_features(many_connections_rec PRIVATE cxx_std_14) - -ecal_install_sample(many_connections_snd) -ecal_install_sample(many_connections_rec) - -set_property(TARGET many_connections_snd PROPERTY FOLDER samples/cpp/benchmarks) -set_property(TARGET many_connections_rec PROPERTY FOLDER samples/cpp/benchmarks) diff --git a/samples/cpp/counter/counter_rec_cb/CMakeLists.txt b/samples/cpp/benchmarks/many_connections_rec/CMakeLists.txt similarity index 85% rename from samples/cpp/counter/counter_rec_cb/CMakeLists.txt rename to samples/cpp/benchmarks/many_connections_rec/CMakeLists.txt index 30ce7c5..6afbc28 100644 --- a/samples/cpp/counter/counter_rec_cb/CMakeLists.txt +++ b/samples/cpp/benchmarks/many_connections_rec/CMakeLists.txt @@ -20,15 +20,15 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(counter_rec_cb) +project(many_connections_rec) find_package(eCAL REQUIRED) -set(counter_rec_cb_src - src/counter_rec_cb.cpp +set(many_connections_rec_src + src/many_connections_rec.cpp ) -ecal_add_sample(${PROJECT_NAME} ${counter_rec_cb_src}) +ecal_add_sample(${PROJECT_NAME} ${many_connections_rec_src}) target_link_libraries(${PROJECT_NAME} eCAL::core) @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/counter) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/many_connections) diff --git a/samples/cpp/benchmarks/many_connections/many_connections_rec.cpp b/samples/cpp/benchmarks/many_connections_rec/src/many_connections_rec.cpp similarity index 76% rename from samples/cpp/benchmarks/many_connections/many_connections_rec.cpp rename to samples/cpp/benchmarks/many_connections_rec/src/many_connections_rec.cpp index 593c8aa..6f6682a 100644 --- a/samples/cpp/benchmarks/many_connections/many_connections_rec.cpp +++ b/samples/cpp/benchmarks/many_connections_rec/src/many_connections_rec.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include @@ -28,13 +30,15 @@ class SubscriberCreator public: SubscriberCreator(int publisher_count) { - std::string ttype("THIS IS THE TOPIC TYPE NAME"); - std::string tdesc("THIS IS THE LONG TOPIC DESCRIPTOR "); - for (auto rep = 0; rep < 4; ++rep) tdesc = tdesc + tdesc; + const std::string ttype("THIS IS THE TOPIC TYPE NAME"); + std::string tdesc("THIS IS THE LONG TOPIC DESCRIPTOR "); + for (auto rep = 0; rep < 4; ++rep) tdesc = tdesc.append(tdesc); for (int i = 0; i < publisher_count; ++i) { - subscribers.emplace_back("Publisher" + std::to_string(i), ttype, tdesc); + 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)); } } @@ -54,7 +58,7 @@ int main(int argc, char** argv) eCAL::Initialize(argc, argv, "many_connections_rec"); // create many subscriber - SubscriberCreator subscribers(1000); + const SubscriberCreator subscribers(10000); std::cout << "Done Initializing" << std::endl; while (eCAL::Ok()) diff --git a/samples/cpp/benchmarks/many_connections_snd/CMakeLists.txt b/samples/cpp/benchmarks/many_connections_snd/CMakeLists.txt new file mode 100644 index 0000000..9977cde --- /dev/null +++ b/samples/cpp/benchmarks/many_connections_snd/CMakeLists.txt @@ -0,0 +1,39 @@ +# ========================= 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(many_connections_snd) + +find_package(eCAL REQUIRED) + +set(many_connections_snd_src + src/many_connections_snd.cpp +) + +ecal_add_sample(${PROJECT_NAME} ${many_connections_snd_src}) + +target_link_libraries(${PROJECT_NAME} eCAL::core) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_sample(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/many_connections) diff --git a/samples/cpp/benchmarks/many_connections/many_connections_snd.cpp b/samples/cpp/benchmarks/many_connections_snd/src/many_connections_snd.cpp similarity index 74% rename from samples/cpp/benchmarks/many_connections/many_connections_snd.cpp rename to samples/cpp/benchmarks/many_connections_snd/src/many_connections_snd.cpp index 26834dc..623d8a4 100644 --- a/samples/cpp/benchmarks/many_connections/many_connections_snd.cpp +++ b/samples/cpp/benchmarks/many_connections_snd/src/many_connections_snd.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include @@ -28,13 +30,15 @@ class PublisherCreator public: PublisherCreator(int publisher_count) { - std::string ttype("THIS IS THE TOPIC TYPE NAME"); - std::string tdesc("THIS IS THE LONG TOPIC DESCRIPTOR "); - for (auto rep = 0; rep < 4; ++rep) tdesc = tdesc + tdesc; + const std::string ttype("THIS IS THE TOPIC TYPE NAME"); + std::string tdesc("THIS IS THE LONG TOPIC DESCRIPTOR "); + for (auto rep = 0; rep < 4; ++rep) tdesc = tdesc.append(tdesc); - for (int i = 0; i < publisher_count; ++i) + for (int i = 1; i <= publisher_count; ++i) { - publishers.emplace_back("Publisher" + std::to_string(i), ttype, tdesc); + std::ostringstream tname; + tname << std::setw(5) << std::setfill('0') << i; + publishers.emplace_back("Topic" + tname.str(), eCAL::SDataTypeInformation{ ttype, "", tdesc }); } } @@ -57,7 +61,7 @@ int main(int argc, char** argv) eCAL::Initialize(argc, argv, "many_connections_snd"); // create many publisher - PublisherCreator publishers(1000); + PublisherCreator publishers(10000); std::cout << "Done Initializing" << std::endl; while (eCAL::Ok()) diff --git a/samples/cpp/benchmarks/multiple_rec/CMakeLists.txt b/samples/cpp/benchmarks/multiple_rec/CMakeLists.txt new file mode 100644 index 0000000..767f57f --- /dev/null +++ b/samples/cpp/benchmarks/multiple_rec/CMakeLists.txt @@ -0,0 +1,41 @@ +# ========================= 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(multiple_rec) + +find_package(eCAL REQUIRED) + +set(multiple_rec_src + src/multiple_rec.cpp +) + +ecal_add_sample(${PROJECT_NAME} ${multiple_rec_src}) + +target_link_libraries(${PROJECT_NAME} + eCAL::core +) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_sample(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/multiple) diff --git a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb.cpp b/samples/cpp/benchmarks/multiple_rec/src/multiple_rec.cpp similarity index 96% rename from samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb.cpp rename to samples/cpp/benchmarks/multiple_rec/src/multiple_rec.cpp index 70c35d4..e60c548 100644 --- a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb.cpp +++ b/samples/cpp/benchmarks/multiple_rec/src/multiple_rec.cpp @@ -26,9 +26,7 @@ #include #include -#define RUN_TIMEOUT 0 #define SUBSCRIBER_NUMBER 200 -#define USE_OMP 0 // globals struct SSubCount @@ -59,15 +57,12 @@ void OnReceive(const char* topic_name_) g_overalll_read++; } -void MultipleRec(int argc, char **argv) +int main(int argc, char **argv) { // initialize eCAL API eCAL::Initialize(argc, argv, "multiple_rec_cb"); // create dummy subscriber -#if USE_OMP -#pragma omp parallel for -#endif std::cout << "create subscribers .." << std::endl; for(int i = 0; i < SUBSCRIBER_NUMBER; i++) { @@ -141,4 +136,6 @@ void MultipleRec(int argc, char **argv) // finalize eCAL API eCAL::Finalize(); + + return(0); } diff --git a/samples/cpp/benchmarks/multiple_snd/CMakeLists.txt b/samples/cpp/benchmarks/multiple_snd/CMakeLists.txt new file mode 100644 index 0000000..71b9025 --- /dev/null +++ b/samples/cpp/benchmarks/multiple_snd/CMakeLists.txt @@ -0,0 +1,41 @@ +# ========================= 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(multiple_snd) + +find_package(eCAL REQUIRED) + +set(multiple_snd_src + src/multiple_snd.cpp +) + +ecal_add_sample(${PROJECT_NAME} ${multiple_snd_src}) + +target_link_libraries(${PROJECT_NAME} + eCAL::core +) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_sample(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/multiple) diff --git a/samples/cpp/multiple/multiple_snd/src/multiple_snd.cpp b/samples/cpp/benchmarks/multiple_snd/src/multiple_snd.cpp similarity index 76% rename from samples/cpp/multiple/multiple_snd/src/multiple_snd.cpp rename to samples/cpp/benchmarks/multiple_snd/src/multiple_snd.cpp index 493800d..95ef14f 100644 --- a/samples/cpp/multiple/multiple_snd/src/multiple_snd.cpp +++ b/samples/cpp/benchmarks/multiple_snd/src/multiple_snd.cpp @@ -30,16 +30,13 @@ #define PAYLOAD_SIZE 1024 #define PRINT_LOG 0 #define PUBLISHER_NUMBER 200 -#define SUBSCRIBER_NUMBER 0 -#define USE_OMP 0 -#define USE_RELIABILITY 0 -void MultipleSend(int argc, char **argv) +int main(int argc, char **argv) { // initialize eCAL API eCAL::Initialize(argc, argv, "multiple_snd"); - // create dummy publisher + // create publisher struct SPubCount { SPubCount() @@ -61,9 +58,6 @@ void MultipleSend(int argc, char **argv) } send_s.resize(PAYLOAD_SIZE); -#if USE_OMP -#pragma omp parallel for shared(pub_vec) -#endif for(int i = 0; i < PUBLISHER_NUMBER; i++) { std::stringstream tname; @@ -71,45 +65,13 @@ void MultipleSend(int argc, char **argv) // publisher topic name std::shared_ptr pub = std::make_shared(); -#if USE_RELIABILITY - pub->SetQOS_Reliability(eCAL::QOS::reliable_reliability_qos); -#endif + pub->Create(tname.str()); struct SPubCount pub_count; pub_count.pub = std::move(pub); pub_vec[i] = pub_count; } -#if SUBSCRIBER_NUMBER - // create dummy subscriber - struct SSubCount - { - SSubCount() - { - sub = nullptr; - } - std::shared_ptr> sub; - }; - typedef std::vector SubMapT; - SubMapT sub_vec; - sub_vec.resize(SUBSCRIBER_NUMBER); - -#if USE_OMP -#pragma omp parallel for -#endif - for(int i = 0; i < SUBSCRIBER_NUMBER; i++) - { - std::stringstream tname; - tname << "PUB_" << i; - - // publisher topic name - std::shared_ptr> sub = std::make_shared(eCAL::string::CSubscriber(tname.str))); - struct SSubCount sub_count; - sub_count.sub = sub; - sub_vec[i] = sub_count; - } -#endif - // loop counter std::atomic global_written(0); @@ -123,9 +85,6 @@ void MultipleSend(int argc, char **argv) cnt++; // send dummy topics -#if USE_OMP -#pragma omp parallel for shared(pub_vec) -#endif for(int i = 0; i < PUBLISHER_NUMBER; i++) { #if PRINT_LOG @@ -174,11 +133,8 @@ void MultipleSend(int argc, char **argv) // destroy publisher pub_vec.clear(); - // destroy subscriber -#if SUBSCRIBER_NUMBER - sub_vec.clear(); -#endif - // finalize eCAL API eCAL::Finalize(); + + return(0); } diff --git a/samples/cpp/performance/performance_rec/CMakeLists.txt b/samples/cpp/benchmarks/performance_rec/CMakeLists.txt similarity index 98% rename from samples/cpp/performance/performance_rec/CMakeLists.txt rename to samples/cpp/benchmarks/performance_rec/CMakeLists.txt index 9c71198..188ab00 100644 --- a/samples/cpp/performance/performance_rec/CMakeLists.txt +++ b/samples/cpp/benchmarks/performance_rec/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/performance) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/performance) diff --git a/samples/cpp/performance/performance_rec_cb/src/performance_rec_cb.cpp b/samples/cpp/benchmarks/performance_rec/src/performance_rec.cpp similarity index 53% rename from samples/cpp/performance/performance_rec_cb/src/performance_rec_cb.cpp rename to samples/cpp/benchmarks/performance_rec/src/performance_rec.cpp index f40f98a..a4aea0a 100644 --- a/samples/cpp/performance/performance_rec_cb/src/performance_rec_cb.cpp +++ b/samples/cpp/benchmarks/performance_rec/src/performance_rec.cpp @@ -22,50 +22,31 @@ #include #include -#include #include #include -// globals -std::chrono::steady_clock::time_point start_time(std::chrono::nanoseconds(0)); -long long g_msgs (0); -long long g_bytes(0); - // print performance results -void PrintStatistic(const std::string& topic_name_, const std::chrono::duration& diff_time_, const size_t size_, long long& bytes_, long long& msgs_) +void PrintStatistic(const std::string& topic_name_, const std::chrono::duration& diff_time_, const size_t size_, long long& bytes_, long long& msgs_, const struct eCAL::SReceiveCallbackData* data_) { std::stringstream out; - out << "Topic Name: " << topic_name_ << std::endl; - out << "Message size (kByte): " << (unsigned int)(size_ / 1024 ) << std::endl; - out << "kByte/s: " << (unsigned int)(bytes_ / 1024 / diff_time_.count()) << std::endl; - out << "MByte/s: " << (unsigned int)(bytes_ / 1024 / 1024 / diff_time_.count()) << std::endl; - out << "Messages/s: " << (unsigned int)(msgs_ / diff_time_.count()) << std::endl; - out << "Latency (us): " << (diff_time_.count() / msgs_) * 1000 * 1000 << std::endl; + out << "Topic Name: " << topic_name_ << std::endl; + if (data_->size > 15) + { + out << "Message [0 - 15]: "; + for (auto i = 0; i < 16; ++i) out << (static_cast(data_->buf))[i] << " "; + out << std::endl; + } + out << "Message size (kByte): " << (unsigned int)(size_ / 1024.0) << std::endl; + out << "kByte/s: " << (unsigned int)(bytes_ / 1024.0 / diff_time_.count()) << std::endl; + out << "MByte/s: " << (unsigned int)(bytes_ / 1024.0 / 1024.0 / diff_time_.count()) << std::endl; + out << "GByte/s: " << (unsigned int)(bytes_ / 1024.0 / 1024.0 / 1024.0 / diff_time_.count()) << std::endl; + out << "Messages/s: " << (unsigned int)(msgs_ / diff_time_.count()) << std::endl; + out << "Latency (us): " << (diff_time_.count() * 1e6) / (double)msgs_ << std::endl; std::cout << out.str() << std::endl; msgs_ = 0; bytes_ = 0; } -// subscriber callback function -void OnReceive(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) -{ - size_t size = data_->size; - - g_msgs++; - g_bytes += size; - - // block it 10 ms, so we emulate some workload in the callback - // std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - // check time and print results every second - std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; - if (diff_time >= std::chrono::seconds(1)) - { - PrintStatistic(topic_name_, diff_time, size, g_bytes, g_msgs); - start_time = std::chrono::steady_clock::now(); - } -} - // main entry int main(int argc, char **argv) { @@ -75,15 +56,27 @@ int main(int argc, char **argv) // create subscriber for topic "Performance" eCAL::CSubscriber sub("Performance"); - // dump instance state if creation failed - if(!sub.IsCreated()) - { - std::cout << "Could not create subscriber !" << std::endl; - return(0); - } + // helper variables for time and throughput + std::chrono::steady_clock::time_point start_time(std::chrono::nanoseconds(0)); + long long msgs (0); + long long bytes(0); // add callback - sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2)); + auto on_receive = [&](const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) { + auto size = data_->size; + + msgs++; + bytes += size; + + // check time and print results every second + const std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; + if (diff_time >= std::chrono::seconds(1)) + { + PrintStatistic(topic_name_, diff_time, size, bytes, msgs, data_); + start_time = std::chrono::steady_clock::now(); + } + }; + sub.AddReceiveCallback(std::bind(on_receive, std::placeholders::_1, std::placeholders::_2)); // idle main thread while(eCAL::Ok()) diff --git a/samples/cpp/performance/performance_snd/CMakeLists.txt b/samples/cpp/benchmarks/performance_snd/CMakeLists.txt similarity index 98% rename from samples/cpp/performance/performance_snd/CMakeLists.txt rename to samples/cpp/benchmarks/performance_snd/CMakeLists.txt index 5fcfdb8..002a107 100644 --- a/samples/cpp/performance/performance_snd/CMakeLists.txt +++ b/samples/cpp/benchmarks/performance_snd/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/performance) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/performance) diff --git a/samples/cpp/benchmarks/performance_snd/src/binary_payload_writer.h b/samples/cpp/benchmarks/performance_snd/src/binary_payload_writer.h new file mode 100644 index 0000000..bea3b85 --- /dev/null +++ b/samples/cpp/benchmarks/performance_snd/src/binary_payload_writer.h @@ -0,0 +1,55 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include + +// a binary payload +class CBinaryPayload : public eCAL::CPayloadWriter +{ +public: + CBinaryPayload(size_t size_) : size(size_) {} + + bool WriteFull(void* buf_, size_t len_) override + { + // write complete content to the shared memory file + if (len_ < size) return false; + memset(buf_, 42, size); + return true; + }; + + bool WriteModified(void* buf_, size_t len_) override + { + // update content of the shared memory file + if (len_ < size) return false; + const size_t write_idx((clock % 1024) % len_); + const char write_chr(clock % 10 + 48); + static_cast(buf_)[write_idx] = write_chr; + clock++; + return true; + }; + + size_t GetSize() override { return size; }; + +private: + size_t size = 0; + int clock = 0; +}; diff --git a/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp b/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp new file mode 100644 index 0000000..b7f3a5c --- /dev/null +++ b/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp @@ -0,0 +1,101 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include + +#include +#include +#include + +#include "binary_payload_writer.h" + +// performance settings +const size_t payload_size_default (8* 1024 * 1024); + +// main entry +int main(int argc, char **argv) +{ + size_t payload_size(payload_size_default); + if(argc > 1) payload_size = atoi(argv[1]); + if(payload_size < 1) payload_size = 1; + + // initialize eCAL API + eCAL::Initialize(argc, argv, "performance_snd"); + + // create payload + CBinaryPayload binary_payload(payload_size); + + // create publisher + eCAL::CPublisher pub("Performance"); + + // counter + long long msgs (0); + long long bytes(0); + long long clock(0); + + // set start time + auto start_time = std::chrono::steady_clock::now(); + + // send updates + while(eCAL::Ok()) + { + // send content + pub.Send(binary_payload); + + // manage counters + clock++; + msgs++; + bytes += payload_size; + + // check timer and print results every second + if(clock%2000 == 0) + { + const std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; + if(diff_time >= std::chrono::seconds(1)) + { + // log results + std::stringstream out; + out << "Message size (kByte): " << (unsigned int)(binary_payload.GetSize() / 1024) << std::endl; + out << "kByte/s: " << (unsigned int)(bytes / 1024.0 / diff_time.count()) << std::endl; + out << "MByte/s: " << (unsigned int)(bytes / 1024.0 / 1024.0 / diff_time.count()) << std::endl; + out << "GByte/s: " << (unsigned int)(bytes / 1024.0 / 1024.0 / 1024.0 / diff_time.count()) << std::endl; + out << "Messages/s: " << (unsigned int)(msgs / diff_time.count()) << std::endl; + out << "Latency (us): " << (diff_time.count() * 1e6) / (double)msgs << std::endl; + std::cout << out.str() << std::endl; + eCAL::Logging::Log(out.str()); + + // reset counter + msgs = 0; + bytes = 0; + + // reset start time + start_time = std::chrono::steady_clock::now(); + } + } + } + + // destroy publisher + pub.Destroy(); + + // finalize eCAL API + eCAL::Finalize(); + + return(0); +} diff --git a/samples/cpp/services/ecalsys_client/CMakeLists.txt b/samples/cpp/benchmarks/perftool/CMakeLists.txt similarity index 59% rename from samples/cpp/services/ecalsys_client/CMakeLists.txt rename to samples/cpp/benchmarks/perftool/CMakeLists.txt index f6ca33d..f819d38 100644 --- a/samples/cpp/services/ecalsys_client/CMakeLists.txt +++ b/samples/cpp/benchmarks/perftool/CMakeLists.txt @@ -1,13 +1,13 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2023 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. @@ -16,28 +16,41 @@ # # ========================= eCAL LICENSE ================================= -cmake_minimum_required(VERSION 3.10) - +cmake_minimum_required(VERSION 3.13) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(ecalsys_client) +project(perftool) +find_package(Threads REQUIRED) find_package(eCAL REQUIRED) -set(ecalsys_client_src - src/ecalsys_client.cpp +set(source_files + src/main.cpp + src/publisher.cpp + src/publisher.h + src/publisher_statistics.h + src/subscriber.cpp + src/subscriber.h + src/subscriber_statistics.h ) -ecal_add_sample(${PROJECT_NAME} ${ecalsys_client_src}) - -target_include_directories(${PROJECT_NAME} PRIVATE .) +#add_executable(${PROJECT_NAME} ${source_files}) +ecal_add_sample(${PROJECT_NAME} ${source_files}) target_link_libraries(${PROJECT_NAME} eCAL::core - eCAL::app_pb) + Threads::Threads +) + +target_include_directories(${PROJECT_NAME} PRIVATE src) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + +source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" + FILES + ${source_files} +) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/perftool) diff --git a/samples/cpp/benchmarks/perftool/Readme.md b/samples/cpp/benchmarks/perftool/Readme.md new file mode 100644 index 0000000..b3372a7 --- /dev/null +++ b/samples/cpp/benchmarks/perftool/Readme.md @@ -0,0 +1,52 @@ +# ecal-perftool + +The ecal-perftool is a simple application to estimate the performance of eCAL pub-sub connections using dummy-data being published a at a constant frequency. + +## Usage + +``` +Usage: + ecal_sample_perftool pub [options] +or: + ecal_sample_perftool sub [callback_delay_ms] [options] + +Options: + -q, --quiet: Do not print any output + -v, --verbose: Print all measured times for all messages + --busy-wait: Busy wait when receiving messages (i.e. burn CPU). For subscribers only. + --hickup : Further delay a single callback. For subscribers only. +``` + +## Output + +**Publisher**: + +``` +[ 78436.510] | cnt: 9 | loop_dt(ms) mean: 99.954 [ 99.587, 100.001] | loop_freq(Hz): 10.0 | snd_dt(ms) mean: 0.001 [ 0.001, 0.001] +[ 78437.525] | cnt: 10 | loop_dt(ms) mean: 100.000 [ 99.999, 100.001] | loop_freq(Hz): 10.0 | snd_dt(ms) mean: 0.001 [ 0.000, 0.001] +[ 78438.538] | cnt: 10 | loop_dt(ms) mean: 100.001 [ 99.999, 100.013] | loop_freq(Hz): 10.0 | snd_dt(ms) mean: 0.001 [ 0.000, 0.001] +[ 78439.545] | cnt: 10 | loop_dt(ms) mean: 99.999 [ 99.987, 100.002] | loop_freq(Hz): 10.0 | snd_dt(ms) mean: 0.001 [ 0.001, 0.001] +[ 78440.551] | cnt: 10 | loop_dt(ms) mean: 100.000 [ 99.999, 100.001] | loop_freq(Hz): 10.0 | snd_dt(ms) mean: 0.001 [ 0.001, 0.001] +``` + +- `[ xxx]`: Log system time +- `cnt`: Amount of messages sent since last log output +- `loop_dt`: Duration of publishing loop, consisting of mean `mean [min, max]` in milliseconds +- `loop_freq`: computed loop frequency in Hz +- `snd_dt`: Duration of the eCAL `CPublisher::Send()` call only, consisting of `mean [min, max]` in milliseconds + +**Subscriber** + +``` +[ 78927.089] | cnt: 10 | lost: 0 | msg_dt(ms) mean: 99.997 [ 99.967, 100.019] | msg_freq(Hz): 10.0 +[ 78928.103] | cnt: 10 | lost: 0 | msg_dt(ms) mean: 100.000 [ 99.964, 100.031] | msg_freq(Hz): 10.0 +[ 78929.104] | cnt: 10 | lost: 0 | msg_dt(ms) mean: 99.998 [ 99.966, 100.039] | msg_freq(Hz): 10.0 +[ 78930.117] | cnt: 10 | lost: 0 | msg_dt(ms) mean: 100.001 [ 99.993, 100.010] | msg_freq(Hz): 10.0 +[ 78931.132] | cnt: 10 | lost: 0 | msg_dt(ms) mean: 100.000 [ 99.966, 100.030] | msg_freq(Hz): 10.0 +``` + +- `[ xxx]`: Log system time +- `cnt`: Amount of received sent since last log output +- `lost`: Amount of dropped messages since the last log output. Determined by comparing the native eCAL message counter of each message to the previous. +- `msg_dt`: Duration between the received messages, consisting of `mean [min, max]` in milliseconds +- `msg_freq`: Computed message frequency in Hz diff --git a/samples/cpp/benchmarks/perftool/src/main.cpp b/samples/cpp/benchmarks/perftool/src/main.cpp new file mode 100644 index 0000000..de88ae4 --- /dev/null +++ b/samples/cpp/benchmarks/perftool/src/main.cpp @@ -0,0 +1,249 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2023 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 // IWYU pragma: keep + +#include "publisher.h" +#include "subscriber.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#endif + + +void printUsage(const std::string& arg0) +{ + std::cout << "Usage:" << std::endl; + std::cout << " " << arg0 << " pub [options]" << std::endl; + std::cout << "or:" << std::endl; + std::cout << " " << arg0 << " sub [callback_delay_ms] [options]" << std::endl; + std::cout << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -q, --quiet: Do not print any output" << std::endl; + std::cout << " -v, --verbose: Print all measured times for all messages" << std::endl; + std::cout << " --busy-wait: Busy wait when receiving messages (i.e. burn CPU). For subscribers only." << std::endl; + std::cout << " --hickup : Further delay a single callback. For subscribers only." << std::endl; + +} + +int main(int argc, char** argv) +{ +#ifdef WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif // WIN32 + + bool quiet_arg = false; + bool verbose_print_times = false; + bool busy_wait_arg = false; + + bool hickup_arg = false; + std::chrono::steady_clock::duration hickup_time (0); + std::chrono::steady_clock::duration hickup_delay(0); + + // convert argc, argv to vector of strings + std::vector args; + args.reserve(static_cast(argc)); + for (int i = 0; i < argc; ++i) + { + args.emplace_back(argv[i]); + } + + // Check for -h / --help + if (args.size() < 2 + || std::find(args.begin(), args.end(), "-h") != args.end() + || std::find(args.begin(), args.end(), "--help") != args.end()) + { + printUsage(args[0]); + return 0; + } + + // find "--hickup" argument and remove it from args + { + auto hickup_arg_it = std::find(args.begin(), args.end(), "--hickup"); + if (hickup_arg_it != args.end()) + { + hickup_arg = true; + + // Check if there are enough arguments for the time and delay after the hickup_arg_it and parse those as doubles + if (args.size() < static_cast(std::distance(args.begin(), hickup_arg_it) + 3)) + { + std::cerr << "Invalid number of parameters after --hickup" << std::endl; + printUsage(args[0]); + return 1; + } + else + { + try + { + // Parse the next two arguments as double + const double hickup_time_ms = std::stod(*(std::next(hickup_arg_it, 1))); + const double hickup_delay_ms = std::stod(*(std::next(hickup_arg_it, 2))); + + hickup_time = std::chrono::duration_cast(std::chrono::duration(hickup_time_ms)); + hickup_delay = std::chrono::duration_cast(std::chrono::duration(hickup_delay_ms)); + } + catch (const std::exception& e) + { + std::cerr << "Failed parsing parameters after --hickup: " << e.what() << std::endl; + printUsage(args[0]); + return 1; + } + + // Remove all 3 parameters + args.erase(hickup_arg_it, std::next(hickup_arg_it, 3)); + } + } + } + + // find "--quiet" argument and remove it from args + { + auto quiet_arg_it = std::find(args.begin(), args.end(), "--quiet"); + if (quiet_arg_it != args.end()) + { + quiet_arg = true; + args.erase(quiet_arg_it); + } + } + + // find "-q" argument and remove it from args + { + auto q_arg_it = std::find(args.begin(), args.end(), "-q"); + if (q_arg_it != args.end()) + { + quiet_arg = true; + args.erase(q_arg_it); + } + } + + // find "--verbose" argument and remove it from args + { + auto verbose_arg_it = std::find(args.begin(), args.end(), "--verbose"); + if (verbose_arg_it != args.end()) + { + verbose_print_times = true; + args.erase(verbose_arg_it); + } + } + + // find "-v" argument and remove it from args + { + auto v_arg_it = std::find(args.begin(), args.end(), "-v"); + if (v_arg_it != args.end()) + { + verbose_print_times = true; + args.erase(v_arg_it); + } + } + + // Validate quite and verbose args + if (quiet_arg && verbose_print_times) + { + std::cerr << "Invalid arguments: Cannot use \"quiet\" and \"verbose\" simultaneously" << std::endl; + printUsage(argv[0]); + return 1; + } + + // find "--busy-wait" argument and remove it from args + { + auto busy_wait_arg_it = std::find(args.begin(), args.end(), "--busy-wait"); + if (busy_wait_arg_it != args.end()) + { + busy_wait_arg = true; + args.erase(busy_wait_arg_it); + } + } + + if (args[1] == "pub") + { + if (args.size() != 5) + { + std::cerr << "Invalid number of parameters" << std::endl; + printUsage(args[0]); + return 1; + } + const std::string topic_name = args[2]; + const double frequency_hz = std::stod(args[3]); + const unsigned long long payload_size_bytes = std::stoull(args[4]); + + // Initialize eCAL + eCAL::Initialize(argc, argv, "ecal-perftool"); + eCAL::Util::EnableLoopback(true); + + const Publisher publisher(topic_name, frequency_hz, payload_size_bytes, quiet_arg, verbose_print_times); + + // Just don't exit + while (eCAL::Ok()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // finalize eCAL API + eCAL::Finalize(); + } + else if (args[1] == "sub") + { + if (args.size() < 3 || args.size() > 4) + { + std::cerr << "Invalid number of parameters" << std::endl; + printUsage(args[0]); + return 1; + } + + const std::string topic_name = args[2]; + const std::chrono::milliseconds callback_delay((args.size() >= 4 ? std::stoull(args[3]) : 0)); + + + // Initialize eCAL + eCAL::Initialize(argc, argv, "ecal-perftool"); + eCAL::Util::EnableLoopback(true); + + const Subscriber subscriber(topic_name, callback_delay, busy_wait_arg, hickup_arg, hickup_time, hickup_delay, quiet_arg, verbose_print_times); + + // Just don't exit + while (eCAL::Ok()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // finalize eCAL API + eCAL::Finalize(); + } + else + { + std::cerr << "Invalid parameter: " << args[1] << std::endl; + printUsage(args[0]); + return 1; + } + + return 0; +} diff --git a/samples/cpp/benchmarks/perftool/src/publisher.cpp b/samples/cpp/benchmarks/perftool/src/publisher.cpp new file mode 100644 index 0000000..c3a1614 --- /dev/null +++ b/samples/cpp/benchmarks/perftool/src/publisher.cpp @@ -0,0 +1,180 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2023 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 "publisher.h" + +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep + +#include "publisher_statistics.h" + +#ifdef WIN32 + #define NOMINMAX + #define WIN32_LEAN_AND_MEAN + #include // IWYU pragma: keep +#else + #include +#endif // WIN32 + +Publisher::Publisher(const std::string& topic_name, double frequency, std::size_t payload_size, bool quiet, bool log_print_verbose_times) + : ecal_pub (topic_name) + , frequency_ (frequency) + , payload_ (payload_size) + , period_ (std::chrono::nanoseconds(static_cast(1e9 / frequency))) + , next_deadline_ (std::chrono::steady_clock::now() + period_) + , is_interrupted_ (false) + , log_print_verbose_times_(log_print_verbose_times) +{ + statistics_.reserve(static_cast((frequency + 1.0) * 1.2)); + + // Start the thread + publisher_thread_ = std::make_unique([this](){ this->loop(); }); + + if (!quiet) + statistics_thread_ = std::make_unique([this](){ this->statisticsLoop(); }); +} + +// Destructor +Publisher::~Publisher() +{ + // Interrupt the thread + { + const std::unique_lock lock(mutex_); + is_interrupted_ = true; + condition_variable_.notify_all(); + } + + // Join the thread + publisher_thread_->join(); + + if (statistics_thread_) + statistics_thread_->join(); +} + +void Publisher::loop() +{ + while (!is_interrupted_) + { + PublishedMessage message_info; + + auto timepoint_snd_start = std::chrono::steady_clock::now(); + ecal_pub.Send(payload_.data(), payload_.size()); + auto timepoint_snd_end = std::chrono::steady_clock::now(); + + if (next_deadline_ > std::chrono::steady_clock::now()) + { + preciseWaitUntil(next_deadline_); + next_deadline_ += period_; + } + else + { + next_deadline_ = std::chrono::steady_clock::now() + period_; + } + + message_info.publish_time = timepoint_snd_start; + message_info.send_call_duration = timepoint_snd_end - timepoint_snd_start; + + if (statistics_thread_) + { + const std::lock_guardlock (mutex_); + statistics_.push_back(message_info); + } + } +} + +void Publisher::statisticsLoop() +{ + while (!is_interrupted_) + { + PublisherStatistics statistics; + statistics.reserve(static_cast((frequency_ + 1.0) * 1.2)); + + { + std::unique_locklock (mutex_); + + condition_variable_.wait_for(lock, std::chrono::seconds(1), [this]() { return bool(is_interrupted_); }); + + if (is_interrupted_) + return; + + if(statistics_.size() > 1) + statistics_.swap(statistics); + + // Initialize the new statistics vector with the last element of the old one. This is important for properly computing the loop time of the actual first message. + statistics_.push_back(statistics.back()); + } + + if (statistics.size() > 1) + printStatistics(statistics, log_print_verbose_times_); + else + std::cerr << "Not enough data" << std::endl; + } +} + +bool Publisher::preciseWaitUntil(std::chrono::steady_clock::time_point time) const +{ + constexpr auto max_time_to_poll_wait = std::chrono::milliseconds(20); + constexpr auto max_time_to_busy_wait = std::chrono::microseconds(5); + + while(true) + { + auto remaining_time_to_wait = time - std::chrono::steady_clock::now(); + + auto time_to_poll_wait = remaining_time_to_wait - max_time_to_busy_wait; + auto time_to_normal_wait = remaining_time_to_wait - max_time_to_poll_wait - max_time_to_busy_wait; + + if (time_to_normal_wait > std::chrono::steady_clock::duration::zero()) + { + std::unique_lock lock(mutex_); + condition_variable_.wait_for(lock, time_to_normal_wait, [this](){ return bool(is_interrupted_); }); + + if (is_interrupted_) + return false; + } + else if (time_to_poll_wait > std::chrono::steady_clock::duration::zero()) + { + while (std::chrono::steady_clock::now() < (time - max_time_to_busy_wait)) + { +#ifdef WIN32 + Sleep(0); // NOLINT(misc-include-cleaner) +#else + usleep(1); +#endif + if (is_interrupted_) + return false; + } + } + else + { + while ((std::chrono::steady_clock::now() < time) && !is_interrupted_) + { + // Busy wait + } + return !is_interrupted_; + } + } +} diff --git a/samples/cpp/benchmarks/perftool/src/publisher.h b/samples/cpp/benchmarks/perftool/src/publisher.h new file mode 100644 index 0000000..6d56d43 --- /dev/null +++ b/samples/cpp/benchmarks/perftool/src/publisher.h @@ -0,0 +1,85 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2023 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 +#include +#include +#include +#include +#include + +#include "publisher_statistics.h" + +class Publisher +{ +////////////////////////////////////// +/// Publisher, Destructor +////////////////////////////////////// +public: + // Constructor that gets a frequency in Hz + Publisher(const std::string& topic_name, double frequency, std::size_t payload_size, bool quiet, bool log_print_verbose_times); + + // Delete copy + Publisher(const Publisher&) = delete; + Publisher& operator=(const Publisher&) = delete; + + // Delete move (the class uses a this reference) + Publisher(Publisher&&) noexcept = delete; + Publisher& operator=(Publisher&&) noexcept = delete; + + // Destructor + ~Publisher(); + +////////////////////////////////////// +/// Implementation +////////////////////////////////////// +private: + void loop(); + void statisticsLoop(); + + bool preciseWaitUntil(std::chrono::steady_clock::time_point time) const; + +////////////////////////////////////// +/// Member variables +////////////////////////////////////// +private: + eCAL::CPublisher ecal_pub; + const double frequency_; + std::vector payload_; + + std::unique_ptr publisher_thread_; + std::unique_ptr statistics_thread_; + + std::chrono::nanoseconds period_; + std::chrono::steady_clock::time_point next_deadline_; + + mutable std::mutex mutex_; + mutable std::condition_variable condition_variable_; + std::atomic is_interrupted_; + PublisherStatistics statistics_; + + const bool log_print_verbose_times_; +}; diff --git a/samples/cpp/benchmarks/perftool/src/publisher_statistics.h b/samples/cpp/benchmarks/perftool/src/publisher_statistics.h new file mode 100644 index 0000000..c2af3bd --- /dev/null +++ b/samples/cpp/benchmarks/perftool/src/publisher_statistics.h @@ -0,0 +1,129 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2023 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 +#include +#include + +struct PublishedMessage +{ + std::chrono::steady_clock::time_point publish_time; + std::chrono::steady_clock::duration send_call_duration; +}; + +using PublisherStatistics = std::vector; + +inline void printStatistics(const PublisherStatistics& statistics, bool print_verbose_times) +{ + // Compute entire entire_duration from first to last message and the mean + auto entire_duration = statistics.back().publish_time - statistics.front().publish_time; + + // Get the minium and maximum send_call_duration. Skip the first element, as that one actually is from teh last iteration. + auto send_call_duration_min = std::chrono::steady_clock::duration::max(); + auto send_call_duration_max = std::chrono::steady_clock::duration::min(); + for (size_t i = 1; i < statistics.size(); ++i) + { + auto send_call_duration = statistics[i].send_call_duration; + if (send_call_duration < send_call_duration_min) + send_call_duration_min = send_call_duration; + if (send_call_duration > send_call_duration_max) + send_call_duration_max = send_call_duration; + } + + // Compute the mean send_call_duration. Skip the first element, as that one actually is from the last iteration. + auto send_call_duration_mean = std::chrono::steady_clock::duration(0); + for (size_t i = 1; i < statistics.size(); ++i) + { + send_call_duration_mean += statistics[i].send_call_duration; + } + send_call_duration_mean /= (statistics.size() - 1); + + // Get the minimum and maximum loop time (based on the publish_time timestamp) + auto loop_time_min = std::chrono::steady_clock::duration::max(); + auto loop_time_max = std::chrono::steady_clock::duration::min(); + for (size_t i = 1; i < statistics.size(); ++i) + { + auto loop_time = statistics[i].publish_time - statistics[i - 1].publish_time; + if (loop_time < loop_time_min) + loop_time_min = loop_time; + if (loop_time > loop_time_max) + loop_time_max = loop_time; + } + + // Compute the mean loop time (based on the publish_time timestamp) + auto loop_time_mean = entire_duration / (statistics.size() - 1); + + auto loop_frequency = 1.0 / std::chrono::duration_cast>(loop_time_mean).count(); + + + // Print statistics (mean and min/max) + { + std::stringstream ss; + ss << std::right << std::fixed; + ss << "[" << std::setprecision(3) << std::setw(10) << std::chrono::duration_cast>(std::chrono::steady_clock::now().time_since_epoch()).count() << "]"; + ss << " | cnt:" << std::setprecision(3) << std::setw(5) << statistics.size() - 1; + ss << " | loop_dt(ms) mean:" << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(loop_time_mean).count(); + ss << " [" << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(loop_time_min).count(); + ss << "," << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(loop_time_max).count(); + ss << "]"; + ss << " | loop_freq(Hz):" << std::setprecision(1) << std::setw(7) << loop_frequency; + ss << " | snd_dt(ms) mean:" << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(send_call_duration_mean).count(); + ss << " [" << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(send_call_duration_min).count(); + ss << "," << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(send_call_duration_max).count(); + ss << "]"; + + std::cerr << ss.str() << std::endl; + } + + // Print all times verbose + if (print_verbose_times) + { + std::stringstream ss; + ss << std::right << std::fixed; + + ss << " loop_dt(ms): "; + for (size_t i = 1; i < statistics.size(); ++i) + { + if (i > 1) + ss << " "; + + auto loop_time = statistics[i].publish_time - statistics[i - 1].publish_time; + ss << std::setprecision(1) << std::setw(5) << std::chrono::duration_cast>(loop_time).count(); + } + ss << std::endl; + + ss << " snd_dt(ms) : "; + for (size_t i = 1; i < statistics.size(); ++i) + { + if (i > 1) + ss << " "; + auto send_call_duration = statistics[i].send_call_duration; + ss << std::setprecision(1) << std::setw(5) << std::chrono::duration_cast>(send_call_duration).count(); + } + + std::cerr << ss.str() << std::endl; + } +} diff --git a/samples/cpp/benchmarks/perftool/src/subscriber.cpp b/samples/cpp/benchmarks/perftool/src/subscriber.cpp new file mode 100644 index 0000000..6d20c04 --- /dev/null +++ b/samples/cpp/benchmarks/perftool/src/subscriber.cpp @@ -0,0 +1,153 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2023 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 "subscriber.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "subscriber_statistics.h" + +Subscriber::Subscriber(const std::string& topic_name + , std::chrono::nanoseconds time_to_waste + , bool busy_wait + , bool hickup + , std::chrono::steady_clock::duration wait_before_hickup + , std::chrono::steady_clock::duration hickup_delay + , bool quiet + , bool log_print_verbose_times) + : ecal_sub (topic_name) + , time_to_waste_ (time_to_waste) + , busy_wait_ (busy_wait) + , hickup_ (hickup) + , wait_before_hickup_ (wait_before_hickup) + , hickup_time_ (std::chrono::steady_clock::time_point::max()) + , hickup_delay_ (hickup_delay) + , is_interrupted_ (false) + , statistics_size_ (100) + , log_print_verbose_times_(log_print_verbose_times) +{ + statistics_.reserve(statistics_size_); + + // create statistics thread + if (!quiet) + statistics_thread_ = std::make_unique([this](){ this->statisticsLoop(); }); + + ecal_sub.AddReceiveCallback([this](const char* topic_name_, const eCAL::SReceiveCallbackData* data_) { callback(topic_name_, data_); }); +} + +// Destructor +Subscriber::~Subscriber() +{ + // Interrupt the thread + { + const std::unique_lock lock(mutex_); + is_interrupted_ = true; + condition_variable_.notify_all(); + } + + // Join the thread + if (statistics_thread_) + statistics_thread_->join(); +} + +void Subscriber::callback(const char* /*topic_name_*/, const eCAL::SReceiveCallbackData* data_) +{ + // Initialize callback timepoint, if necessary + if (hickup_ && hickup_time_ == std::chrono::steady_clock::time_point::max()) + hickup_time_ = std::chrono::steady_clock::now() + wait_before_hickup_; + + SubscribedMessage message_info; + message_info.local_receive_time = std::chrono::steady_clock::now(); + message_info.ecal_receive_time = eCAL::Time::ecal_clock::now(); + message_info.ecal_send_time = eCAL::Time::ecal_clock::time_point(std::chrono::microseconds(data_->time)); + message_info.ecal_counter = data_->clock; + message_info.size_bytes = data_->size; + + std::chrono::steady_clock::duration time_to_waste_this_iteration(time_to_waste_); + + // Check if we need to hickup + if (hickup_ && std::chrono::steady_clock::now() >= hickup_time_) + { + // Reset hickup (we only want to do that once) + hickup_ = false; + + // use another sleep time for this iteratoin + time_to_waste_this_iteration = hickup_delay_; + } + + if (time_to_waste_this_iteration >= std::chrono::nanoseconds::zero()) + { + if (busy_wait_) + { + auto start = std::chrono::high_resolution_clock::now(); + while (std::chrono::high_resolution_clock::now() - start < time_to_waste_this_iteration) + { + // Do nothing + } + } + else + { + std::this_thread::sleep_for(time_to_waste_this_iteration); + } + } + + if (statistics_thread_) + { + const std::unique_locklock(mutex_); + statistics_.push_back(message_info); + } +} + +void Subscriber::statisticsLoop() +{ + while (!is_interrupted_) + { + SubscriberStatistics statistics; + statistics.reserve(statistics_size_ * 2); + + { + std::unique_locklock (mutex_); + + condition_variable_.wait_for(lock, std::chrono::seconds(1), [this]() { return bool(is_interrupted_); }); + + if (is_interrupted_) + return; + + if(statistics_.size() > 1) + { + statistics_size_ = std::max(statistics_size_, statistics_.size()); + statistics_.swap(statistics); + + // Initialize the new statistics vector with the last element of the old one. This is important for detecting drops and properly computing the delay of the actual first message. + statistics_.push_back(statistics.back()); + } + } + + if (statistics.size() > 1) + printStatistics(statistics, log_print_verbose_times_); + else + std::cerr << "Not enough data" << std::endl; + } +} diff --git a/samples/cpp/benchmarks/perftool/src/subscriber.h b/samples/cpp/benchmarks/perftool/src/subscriber.h new file mode 100644 index 0000000..bc3acb4 --- /dev/null +++ b/samples/cpp/benchmarks/perftool/src/subscriber.h @@ -0,0 +1,91 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2023 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 +#include +#include +#include +#include +#include "subscriber_statistics.h" + +class Subscriber +{ +////////////////////////////////////// +/// Publisher, Destructor +////////////////////////////////////// +public: + // Constructor that gets a frequency in Hz + Subscriber(const std::string& topic_name + , std::chrono::nanoseconds time_to_waste + , bool busy_wait + , bool hickup + , std::chrono::steady_clock::duration wait_before_hickup + , std::chrono::steady_clock::duration hickup_delay + , bool quiet + , bool log_print_verbose_times); + + // Delete copy + Subscriber(const Subscriber&) = delete; + Subscriber& operator=(const Subscriber&) = delete; + + // Delete move (the class uses a this reference) + Subscriber(Subscriber&&) noexcept = delete; + Subscriber& operator=(Subscriber&&) noexcept = delete; + + // Destructor + ~Subscriber(); + +////////////////////////////////////// +/// Implementation +////////////////////////////////////// +private: + void callback(const char* topic_name_, const eCAL::SReceiveCallbackData* data_); + + void statisticsLoop(); + +////////////////////////////////////// +/// Member variables +////////////////////////////////////// +private: + eCAL::CSubscriber ecal_sub; + std::chrono::nanoseconds time_to_waste_; + bool busy_wait_; + + bool hickup_; + const std::chrono::steady_clock::duration wait_before_hickup_; + std::chrono::steady_clock::time_point hickup_time_; + const std::chrono::steady_clock::duration hickup_delay_; + + std::unique_ptr statistics_thread_; + mutable std::mutex mutex_; + mutable std::condition_variable condition_variable_; + std::atomic is_interrupted_; + SubscriberStatistics statistics_; + size_t statistics_size_; + + const bool log_print_verbose_times_; +}; diff --git a/samples/cpp/benchmarks/perftool/src/subscriber_statistics.h b/samples/cpp/benchmarks/perftool/src/subscriber_statistics.h new file mode 100644 index 0000000..35e7ac9 --- /dev/null +++ b/samples/cpp/benchmarks/perftool/src/subscriber_statistics.h @@ -0,0 +1,113 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2023 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 +#include +#include +#include + +#include + +struct SubscribedMessage +{ + std::chrono::steady_clock::time_point local_receive_time; + eCAL::Time::ecal_clock::time_point ecal_send_time; + eCAL::Time::ecal_clock::time_point ecal_receive_time; + unsigned long long size_bytes{}; + unsigned long long ecal_counter{}; +}; + +using SubscriberStatistics = std::vector; + +inline void printStatistics(const SubscriberStatistics& statistics, bool print_verbose_times) +{ + // Compute entire entire_duration from first to last message and the mean + const auto entire_duration = statistics.back().local_receive_time - statistics.front().local_receive_time; + + // The first message is from the previous loop run and only exists to count lost messages properly and to compute the delay of the actual first message. + const int received_msgs = static_cast(statistics.size()) - 1; + + // Check if the ecal_counter is continous. If not, we have lost messages. Count them. + bool ecal_counter_is_monotinc = true; + unsigned long long lost_msgs = 0; + for (size_t i = 1; i < statistics.size(); ++i) + { + if (statistics[i].ecal_counter <= statistics[i - 1].ecal_counter) + { + ecal_counter_is_monotinc = false; + break; + } + lost_msgs += statistics[i].ecal_counter - statistics[i - 1].ecal_counter - 1; + } + + // Get the minimum and maximum delay time between two messages + auto msg_dt_min = std::chrono::steady_clock::duration::max(); + auto msg_dt_max = std::chrono::steady_clock::duration::min(); + for (size_t i = 1; i < statistics.size(); ++i) + { + auto delay = statistics[i].local_receive_time - statistics[i - 1].local_receive_time; + if (delay < msg_dt_min) + msg_dt_min = delay; + if (delay > msg_dt_max) + msg_dt_max = delay; + } + auto msg_dt_mean = entire_duration / (statistics.size() - 1); + + auto msg_frequency = 1.0 / std::chrono::duration_cast>(msg_dt_mean).count(); + + // Print mean entire_duration and rmse in a single line in milliseconds + { + std::stringstream ss; + ss << std::right << std::fixed; + ss << "[" << std::setprecision(3) << std::setw(10) << std::chrono::duration_cast>(std::chrono::steady_clock::now().time_since_epoch()).count() << "]"; + ss << " | cnt:" << std::setprecision(3) << std::setw(5) << received_msgs; + ss << " | lost:" << std::setprecision(3) << std::setw(5) << (ecal_counter_is_monotinc ? std::to_string(lost_msgs) : "???"); + ss << " | msg_dt(ms) mean:" << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(msg_dt_mean).count(); + ss << " [" << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(msg_dt_min).count(); + ss << "," << std::setprecision(3) << std::setw(8) << std::chrono::duration_cast>(msg_dt_max).count(); + ss << "]"; + ss << " | msg_freq(Hz):" << std::setprecision(1) << std::setw(7) << msg_frequency; + + std::cerr << ss.str() << std::endl; + } + + // Print verbose times + if (print_verbose_times) + { + std::stringstream ss; + ss << std::right << std::fixed; + ss << " msg_dt(ms): "; + for (size_t i = 1; i < statistics.size(); ++i) + { + if (i > 1) + ss << " "; + + auto delay = statistics[i].local_receive_time - statistics[i - 1].local_receive_time; + ss << std::setprecision(1) << std::setw(5) << std::chrono::duration_cast>(delay).count(); + } + + std::cerr << ss.str() << std::endl; + } +} diff --git a/samples/cpp/capnp/addressbook_rec/CMakeLists.txt b/samples/cpp/capnp/addressbook_rec/CMakeLists.txt deleted file mode 100644 index d71eba2..0000000 --- a/samples/cpp/capnp/addressbook_rec/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(addressbook_rec) - -find_package(CapnProto REQUIRED) -find_package(eCAL REQUIRED) - -set(CAPNPC_IMPORT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS src/addressbook.capnp) - -ecal_add_sample(${PROJECT_NAME} src/addressbook_rec.cpp ${CAPNP_SRCS} ${CAPNP_HDRS}) - -set_source_files_properties(${CAPNP_SRCS} PROPERTIES COMPILE_FLAGS $<$:-W0>) - -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) -target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) -target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/addressbook) \ No newline at end of file diff --git a/samples/cpp/capnp/addressbook_rec/src/addressbook.capnp b/samples/cpp/capnp/addressbook_rec/src/addressbook.capnp deleted file mode 100644 index d1ea54a..0000000 --- a/samples/cpp/capnp/addressbook_rec/src/addressbook.capnp +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors -# Licensed under the MIT License: -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -@0x9eb32e19f86ee174; - -# using Cxx = import "/capnp/c++.capnp"; -# $Cxx.namespace("addressbook"); - -struct Person { - id @0 :UInt32; - name @1 :Text; - email @2 :Text; - phones @3 :List(PhoneNumber); - - struct PhoneNumber { - number @0 :Text; - type @1 :Type; - - enum Type { - mobile @0; - home @1; - work @2; - } - } - - employment :union { - unemployed @4 :Void; - employer @5 :Text; - school @6 :Text; - selfEmployed @7 :Void; - # We assume that a person is only one of these. - } -} - -struct AddressBook { - people @0 :List(Person); -} - diff --git a/samples/cpp/capnp/addressbook_rec/src/addressbook_rec.cpp b/samples/cpp/capnp/addressbook_rec/src/addressbook_rec.cpp deleted file mode 100644 index 9d7f43f..0000000 --- a/samples/cpp/capnp/addressbook_rec/src/addressbook_rec.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -// capnp includes -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif /*_MSC_VER*/ -#include "addressbook.capnp.h" -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include -#include - -#include -#include -#include - - - -void printAddressBook(const AddressBook::Reader& addressBook) -{ - for (Person::Reader person : addressBook.getPeople()) - { - std::cout << person.getName().cStr() << ": " << person.getEmail().cStr() << std::endl; - for (Person::PhoneNumber::Reader phone : person.getPhones()) - { - const char* typeName = "UNKNOWN"; - switch (phone.getType()) { - case Person::PhoneNumber::Type::MOBILE: typeName = "mobile"; break; - case Person::PhoneNumber::Type::HOME: typeName = "home"; break; - case Person::PhoneNumber::Type::WORK: typeName = "work"; break; - } - std::cout << " " << typeName << " phone: " << phone.getNumber().cStr() << std::endl; - } - Person::Employment::Reader employment = person.getEmployment(); - - switch (employment.which()) - { - case Person::Employment::UNEMPLOYED: - std::cout << " unemployed" << std::endl; - break; - case Person::Employment::EMPLOYER: - std::cout << " employer: " - << employment.getEmployer().cStr() << std::endl; - break; - case Person::Employment::SCHOOL: - std::cout << " student at: " - << employment.getSchool().cStr() << std::endl; - break; - case Person::Employment::SELF_EMPLOYED: - std::cout << " self-employed" << std::endl; - break; - } - - std::cout << std::endl; - } -} - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "addressbook subscriber"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a subscriber (topic name "addressbook") - eCAL::capnproto::CSubscriber sub("addressbook"); - // enter main loop - while (eCAL::Ok()) - { - // receive content - if (sub.Receive(nullptr, 0)) - { - AddressBook::Reader reader{ sub.getReader() }; - printAddressBook(reader); - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/capnp/addressbook_rec_cb/CMakeLists.txt b/samples/cpp/capnp/addressbook_rec_cb/CMakeLists.txt deleted file mode 100644 index c3ce85e..0000000 --- a/samples/cpp/capnp/addressbook_rec_cb/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(addressbook_rec_cb) - -find_package(CapnProto REQUIRED) -find_package(eCAL REQUIRED) - -set(CAPNPC_IMPORT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS src/addressbook.capnp) - -ecal_add_sample(${PROJECT_NAME} src/addressbook_rec_cb.cpp ${CAPNP_SRCS} ${CAPNP_HDRS}) - -set_source_files_properties(${CAPNP_SRCS} PROPERTIES COMPILE_FLAGS $<$:-W0>) - -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) -target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) -target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/addressbook) \ No newline at end of file diff --git a/samples/cpp/capnp/addressbook_rec_cb/src/addressbook.capnp b/samples/cpp/capnp/addressbook_rec_cb/src/addressbook.capnp deleted file mode 100644 index d1ea54a..0000000 --- a/samples/cpp/capnp/addressbook_rec_cb/src/addressbook.capnp +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors -# Licensed under the MIT License: -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -@0x9eb32e19f86ee174; - -# using Cxx = import "/capnp/c++.capnp"; -# $Cxx.namespace("addressbook"); - -struct Person { - id @0 :UInt32; - name @1 :Text; - email @2 :Text; - phones @3 :List(PhoneNumber); - - struct PhoneNumber { - number @0 :Text; - type @1 :Type; - - enum Type { - mobile @0; - home @1; - work @2; - } - } - - employment :union { - unemployed @4 :Void; - employer @5 :Text; - school @6 :Text; - selfEmployed @7 :Void; - # We assume that a person is only one of these. - } -} - -struct AddressBook { - people @0 :List(Person); -} - diff --git a/samples/cpp/capnp/addressbook_rec_cb/src/addressbook_rec_cb.cpp b/samples/cpp/capnp/addressbook_rec_cb/src/addressbook_rec_cb.cpp deleted file mode 100644 index cd28202..0000000 --- a/samples/cpp/capnp/addressbook_rec_cb/src/addressbook_rec_cb.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -// capnp includes -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif /*_MSC_VER*/ -#include "addressbook.capnp.h" -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include -#include - -#include -#include -#include - - - -void printAddressBook(const AddressBook::Reader& addressBook) -{ - for (Person::Reader person : addressBook.getPeople()) - { - std::cout << person.getName().cStr() << ": " << person.getEmail().cStr() << std::endl; - for (Person::PhoneNumber::Reader phone : person.getPhones()) - { - const char* typeName = "UNKNOWN"; - switch (phone.getType()) { - case Person::PhoneNumber::Type::MOBILE: typeName = "mobile"; break; - case Person::PhoneNumber::Type::HOME: typeName = "home"; break; - case Person::PhoneNumber::Type::WORK: typeName = "work"; break; - } - std::cout << " " << typeName << " phone: " << phone.getNumber().cStr() << std::endl; - } - Person::Employment::Reader employment = person.getEmployment(); - - switch (employment.which()) - { - case Person::Employment::UNEMPLOYED: - std::cout << " unemployed" << std::endl; - break; - case Person::Employment::EMPLOYER: - std::cout << " employer: " - << employment.getEmployer().cStr() << std::endl; - break; - case Person::Employment::SCHOOL: - std::cout << " student at: " - << employment.getSchool().cStr() << std::endl; - break; - case Person::Employment::SELF_EMPLOYED: - std::cout << " self-employed" << std::endl; - break; - } - } -} - -void OnAddressbook(const char* topic_name_, AddressBook::Reader msg_, const long long time_) -{ - // print content - std::cout << "topic name : " << topic_name_ << std::endl; - std::cout << "time : " << time_ << std::endl; - std::cout << std::endl; - printAddressBook(msg_); - std::cout << std::endl; -} - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "addressbook subscriber"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a subscriber (topic name "addressbook") - eCAL::capnproto::CSubscriber sub("addressbook"); - - // add receive callback function (_1 = topic_name, _2 = msg, _3 = time) - auto callback = std::bind(OnAddressbook, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - sub.AddReceiveCallback(callback); - - // enter main loop - while (eCAL::Ok()) - { - // sleep 500 ms - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/capnp/addressbook_rec_dynamic/CMakeLists.txt b/samples/cpp/capnp/addressbook_rec_dynamic/CMakeLists.txt deleted file mode 100644 index 76d6bf8..0000000 --- a/samples/cpp/capnp/addressbook_rec_dynamic/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(addressbook_rec_dynamic) - -find_package(CapnProto REQUIRED) -find_package(eCAL REQUIRED) - -ecal_add_sample(${PROJECT_NAME} src/addressbook_rec_dynamic.cpp) - -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/capnp) - -target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) -target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/addressbook) \ No newline at end of file diff --git a/samples/cpp/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp b/samples/cpp/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp deleted file mode 100644 index c25c8f3..0000000 --- a/samples/cpp/capnp/addressbook_rec_dynamic/src/addressbook_rec_dynamic.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include -#include - -#include -#include - - -void dynamicPrintValue(const capnp::DynamicValue::Reader& value) -{ - using namespace capnp; - // Print an arbitrary message via the dynamic API by - // iterating over the schema. Look at the handling - // of STRUCT in particular. - - switch (value.getType()) { - case DynamicValue::VOID: - std::cout << ""; - break; - case DynamicValue::BOOL: - std::cout << (value.as() ? "true" : "false"); - break; - case DynamicValue::INT: - std::cout << value.as(); - break; - case DynamicValue::UINT: - std::cout << value.as(); - break; - case DynamicValue::FLOAT: - std::cout << value.as(); - break; - case DynamicValue::TEXT: - std::cout << '\"' << value.as().cStr() << '\"'; - break; - case DynamicValue::LIST: { - std::cout << "["; - bool first = true; - for (auto element : value.as()) { - if (first) { - first = false; - } - else { - std::cout << ", "; - } - dynamicPrintValue(element); - } - std::cout << "]"; - break; - } - case DynamicValue::ENUM: { - auto enumValue = value.as(); - KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) { - std::cout << - enumerant->getProto().getName().cStr(); - } - else { - // Unknown enum value; output raw number. - std::cout << enumValue.getRaw(); - } - break; - } - case DynamicValue::STRUCT: { - std::cout << "("; - auto structValue = value.as(); - bool first = true; - for (auto field : structValue.getSchema().getFields()) { - if (!structValue.has(field)) continue; - if (first) { - first = false; - } - else { - std::cout << ", "; - } - std::cout << field.getProto().getName().cStr() - << " = "; - dynamicPrintValue(structValue.get(field)); - } - std::cout << ")"; - std::cout << "\n"; - break; - } - default: - // There are other types, we aren't handling them. - std::cout << "?"; - break; - } -} - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "addressbook subscriber"); - - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a subscriber (topic name "addressbook") - eCAL::capnproto::CDynamicSubscriber sub("addressbook"); - - auto callback = std::bind(dynamicPrintValue, std::placeholders::_2); - sub.AddReceiveCallback(callback); - - // enter main loop - while (eCAL::Ok()) - { - // sleep 500 ms - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/capnp/addressbook_snd/CMakeLists.txt b/samples/cpp/capnp/addressbook_snd/CMakeLists.txt deleted file mode 100644 index 390634d..0000000 --- a/samples/cpp/capnp/addressbook_snd/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(addressbook_snd) - -find_package(CapnProto REQUIRED) -find_package(eCAL REQUIRED) - -set(CAPNPC_IMPORT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS src/addressbook.capnp) - -ecal_add_sample(${PROJECT_NAME} src/addressbook_snd.cpp ${CAPNP_SRCS} ${CAPNP_HDRS}) - -set_source_files_properties(${CAPNP_SRCS} PROPERTIES COMPILE_FLAGS $<$:-W0>) - -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src) -target_link_libraries(${PROJECT_NAME} PRIVATE CapnProto::capnp eCAL::core) -target_link_options(${PROJECT_NAME} PRIVATE $<$:/ignore:4099>) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/addressbook) \ No newline at end of file diff --git a/samples/cpp/capnp/addressbook_snd/src/addressbook.capnp b/samples/cpp/capnp/addressbook_snd/src/addressbook.capnp deleted file mode 100644 index 88df42e..0000000 --- a/samples/cpp/capnp/addressbook_snd/src/addressbook.capnp +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors -# Licensed under the MIT License: -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -@0x9eb32e19f86ee174; - -# using Cxx = import "/capnp/c++.capnp"; -# $Cxx.namespace("addressbook"); - -struct Person { - id @0 :UInt32; - name @1 :Text; - email @2 :Text; - phones @3 :List(PhoneNumber); - - struct PhoneNumber { - number @0 :Text; - type @1 :Type; - - enum Type { - mobile @0; - home @1; - work @2; - } - } - - employment :union { - unemployed @4 :Void; - employer @5 :Text; - school @6 :Text; - selfEmployed @7 :Void; - # We assume that a person is only one of these. - } - - weight @8 :Float32; - data @9 :Data; -} - -struct AddressBook { - people @0 :List(Person); -} - diff --git a/samples/cpp/capnp/addressbook_snd/src/addressbook_snd.cpp b/samples/cpp/capnp/addressbook_snd/src/addressbook_snd.cpp deleted file mode 100644 index 90cc245..0000000 --- a/samples/cpp/capnp/addressbook_snd/src/addressbook_snd.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include -#include - -#include -#include -#include -#include - -// capnp includes -#ifdef _MSC_VER -#pragma warning(push, 0) -#endif /*_MSC_VER*/ -#include "addressbook.capnp.h" -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -void printAddressBook(AddressBook::Builder addressBook) -{ - auto m_string = addressBook.toString().flatten(); - std::cout << "our string: " << m_string.cStr() << std::endl; -} - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "addressbook publisher"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a publisher (topic name "addressbook") - eCAL::capnproto::CPublisher pub("addressbook"); - - auto addressBook = pub.GetBuilder(); - auto people = addressBook.initPeople(2); - - auto alice = people[0]; - alice.setId(123); - alice.setName("Alice"); - alice.setEmail("alice@example.com"); - - auto alicePhones = alice.initPhones(1); - alicePhones[0].setNumber("555-1212"); - alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE); - alice.getEmployment().setSchool("MIT"); - alice.setWeight(60.4f); - - kj::byte* data = new kj::byte[6]; - data[0] = 0x1F; - data[1] = 0x00; - data[2] = 0xA1; - data[3] = 0xB4; - data[4] = 0x14; - data[5] = 0x54; - capnp::Data::Builder aliceData(data, 6); - alice.setData(aliceData); - - auto bob = people[1]; - bob.setId(456); - bob.setName("Bob"); - bob.setEmail("bob@example.com"); - - auto bobPhones = bob.initPhones(2); - bobPhones[0].setNumber("555-4567"); - bobPhones[0].setType(Person::PhoneNumber::Type::HOME); - bobPhones[1].setNumber("555-7654"); - bobPhones[1].setType(Person::PhoneNumber::Type::WORK); - bob.getEmployment().setUnemployed(); - bob.setWeight(80.8f); - - // enter main loop - while (eCAL::Ok()) - { - // send content - pub.Send(); - - // print content - printAddressBook(addressBook); - std::cout << std::endl; - - // sleep 500 ms - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/event/event_rec/src/event_rec.cpp b/samples/cpp/event/event_rec/src/event_rec.cpp deleted file mode 100644 index 435c0f6..0000000 --- a/samples/cpp/event/event_rec/src/event_rec.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include - -int main(int /*argc*/, char** /*argv*/) -{ - // global parameter - const std::string event_name = "my_event"; - - // create named event - eCAL::EventHandleT event_handle; - eCAL::gOpenEvent(&event_handle, event_name); - - // timer - auto start_time(std::chrono::steady_clock::now()); - int clock(0); - int msgs(0); - - // send updates - for(;;) - { - // fire event - if(gWaitForEvent(event_handle, -1)) - { - // collect data - clock++; - msgs++; - - // check timer and print results every second - if(clock%10000 == 0) - { - std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; - if(diff_time >= std::chrono::seconds(1)) - { - start_time = std::chrono::steady_clock::now(); - std::stringstream out; - out << "Events/s: " << int(msgs / diff_time.count()) << std::endl; - std::cout << out.str() << std::endl; - msgs = 0; - } - } - } - } -} diff --git a/samples/cpp/event/event_snd/CMakeLists.txt b/samples/cpp/event/event_snd/CMakeLists.txt deleted file mode 100644 index c490a92..0000000 --- a/samples/cpp/event/event_snd/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(event_snd) - -find_package(eCAL REQUIRED) - -set(event_rec_snd - src/event_snd.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${event_rec_snd}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/event) diff --git a/samples/cpp/event/event_snd/src/event_snd.cpp b/samples/cpp/event/event_snd/src/event_snd.cpp deleted file mode 100644 index 6825b9c..0000000 --- a/samples/cpp/event/event_snd/src/event_snd.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include - -int main(int /*argc*/, char** /*argv*/) -{ - // global parameter - const std::string event_name = "my_event"; - - // create named event - eCAL::EventHandleT event_handle; - eCAL::gOpenEvent(&event_handle, event_name); - - // timer - auto start_time(std::chrono::steady_clock::now()); - int clock(0); - int msgs(0); - - // send updates - for(;;) - { - // fire event - gSetEvent(event_handle); - - // collect data - clock++; - msgs++; - - // check timer and print results every second - if(clock%10011 == 0) - { - std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; - if(diff_time >= std::chrono::seconds(1)) - { - start_time = std::chrono::steady_clock::now(); - std::stringstream out; - out << "Events/s: " << int(msgs / diff_time.count()) << std::endl; - std::cout << out.str() << std::endl; - msgs = 0; - } - } - } -} diff --git a/samples/cpp/flatbuffer/monster_rec/src/flatbuffers/monster.fbs b/samples/cpp/flatbuffer/monster_rec/src/flatbuffers/monster.fbs deleted file mode 100644 index 02f6699..0000000 --- a/samples/cpp/flatbuffer/monster_rec/src/flatbuffers/monster.fbs +++ /dev/null @@ -1,27 +0,0 @@ -// example IDL file - -namespace Game.Sample; - -enum Color:byte { Red = 0, Green, Blue = 2 } - -union Any { Monster } // add more elements.. - -struct Vec3 -{ - x:float; - y:float; - z:float; -} - -table Monster -{ - pos:Vec3; - mana:short = 150; - hp:short = 100; - name:string; - friendly:bool = false (deprecated); - inventory:[ubyte]; - color:Color = Blue; -} - -root_type Monster; diff --git a/samples/cpp/flatbuffer/monster_rec/src/monster_rec.cpp b/samples/cpp/flatbuffer/monster_rec/src/monster_rec.cpp deleted file mode 100644 index d044da0..0000000 --- a/samples/cpp/flatbuffer/monster_rec/src/monster_rec.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include -#include - -// flatbuffers includes -#include - -// flatbuffers generated includes -#include "monster_generated.h" - -void OnMonster(const char* topic_name_, const flatbuffers::FlatBufferBuilder& msg_, const long long time_) -{ - // create monster - auto monster(Game::Sample::GetMonster(msg_.GetBufferPointer())); - - // print content - std::cout << "topic name : " << topic_name_ << std::endl; - std::cout << "time : " << time_ << std::endl; - std::cout << std::endl; - std::cout << "monster pos x : " << monster->pos()->x() << std::endl; - std::cout << "monster pos y : " << monster->pos()->y() << std::endl; - std::cout << "monster pos z : " << monster->pos()->z() << std::endl; - std::cout << "monster mana : " << monster->mana() << std::endl; - std::cout << "monster hp : " << monster->hp() << std::endl; - std::cout << "monster name : " << monster->name()->c_str() << std::endl; - - std::cout << "monster inventory : "; - for(auto iter = monster->inventory()->begin(); iter != monster->inventory()->end(); ++iter) - { - std::cout << static_cast(*iter) << " "; - } - std::cout << std::endl; - - std::cout << "monster color : "; - switch (monster->color()) - { - case Game::Sample::Color_Red: - std::cout << "Red"; - break; - case Game::Sample::Color_Green: - std::cout << "Green"; - break; - case Game::Sample::Color_Blue: - std::cout << "Blue"; - break; - } - std::cout << std::endl; - - std::cout << std::endl; -} - - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "monster subscriber"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a subscriber (topic name "monster") - eCAL::flatbuffers::CSubscriber sub("monster"); - - // add receive callback function (_1 = topic_name, _2 = msg, _3 = time) - auto callback = std::bind(OnMonster, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - sub.AddReceiveCallback(callback); - - while(eCAL::Ok()) - { - // sleep 100 ms - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/flatbuffer/monster_snd/src/flatbuffers/monster.fbs b/samples/cpp/flatbuffer/monster_snd/src/flatbuffers/monster.fbs deleted file mode 100644 index 02f6699..0000000 --- a/samples/cpp/flatbuffer/monster_snd/src/flatbuffers/monster.fbs +++ /dev/null @@ -1,27 +0,0 @@ -// example IDL file - -namespace Game.Sample; - -enum Color:byte { Red = 0, Green, Blue = 2 } - -union Any { Monster } // add more elements.. - -struct Vec3 -{ - x:float; - y:float; - z:float; -} - -table Monster -{ - pos:Vec3; - mana:short = 150; - hp:short = 100; - name:string; - friendly:bool = false (deprecated); - inventory:[ubyte]; - color:Color = Blue; -} - -root_type Monster; diff --git a/samples/cpp/flatbuffer/monster_snd/src/monster_snd.cpp b/samples/cpp/flatbuffer/monster_snd/src/monster_snd.cpp deleted file mode 100644 index ba9d241..0000000 --- a/samples/cpp/flatbuffer/monster_snd/src/monster_snd.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include - -// flatbuffers includes -#include - -// flatbuffers generated includes -#include "monster_generated.h" - - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "monster publisher"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a publisher (topic name "monster") - eCAL::flatbuffers::CPublisher pub("monster"); - - // the generic builder instance - flatbuffers::FlatBufferBuilder builder; - - // generate a class instance of Monster - auto vec = Game::Sample::Vec3(1, 2, 3); - - auto name = builder.CreateString("Monster"); - - unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - auto inventory = builder.CreateVector(inv_data, 10); - - // shortcut for creating monster with all fields set: - auto mloc = Game::Sample::CreateMonster(builder, &vec, 150, 80, name, inventory, Game::Sample::Color_Blue); - - builder.Finish(mloc); - - // enter main loop - auto cnt = 0; - while(eCAL::Ok()) - { - // mutate elements - auto monster(Game::Sample::GetMutableMonster(builder.GetBufferPointer())); - monster->mutate_hp(static_cast(++cnt)); // set table field. - monster->mutable_pos()->mutate_z(42); // set struct field. - monster->mutable_inventory()->Mutate(0, 42); // set vector element. - - // send the monster object - pub.Send(builder, -1); - - // print content - std::cout << "monster pos x : " << monster->pos()->x() << std::endl; - std::cout << "monster pos y : " << monster->pos()->y() << std::endl; - std::cout << "monster pos z : " << monster->pos()->z() << std::endl; - std::cout << "monster mana : " << monster->mana() << std::endl; - std::cout << "monster hp : " << monster->hp() << std::endl; - std::cout << "monster name : " << monster->name()->c_str() << std::endl; - - std::cout << "monster inventory : "; - for(auto iter = monster->inventory()->begin(); iter != monster->inventory()->end(); ++iter) - { - std::cout << static_cast(*iter) << " "; - } - std::cout << std::endl; - - std::cout << "monster color : "; - switch (monster->color()) - { - case Game::Sample::Color_Red: - std::cout << "Red"; - break; - case Game::Sample::Color_Green: - std::cout << "Green"; - break; - case Game::Sample::Color_Blue: - std::cout << "Blue"; - break; - } - std::cout << std::endl; - - std::cout << std::endl; - - // sleep 500 ms - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/misc/process/CMakeLists.txt b/samples/cpp/misc/process/CMakeLists.txt deleted file mode 100644 index 7764ce0..0000000 --- a/samples/cpp/misc/process/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(process) - -find_package(eCAL REQUIRED) - -set(process_src - src/process.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${process_src}) - -target_include_directories(${PROJECT_NAME} PRIVATE .) - -target_link_libraries (${PROJECT_NAME} - eCAL::core -) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/misc) diff --git a/samples/cpp/misc/process/src/process.cpp b/samples/cpp/misc/process/src/process.cpp deleted file mode 100644 index cfeb4f0..0000000 --- a/samples/cpp/misc/process/src/process.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#ifdef ECAL_OS_WINDOWS -const char* proc_name = "notepad.exe"; -#else // ECAL_OS_WINDOWS -const char* proc_name = "gedit"; -#endif // ECAL_OS_WINDOWS - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "process", eCAL::Init::None); - - // start process - eCAL::Process::StartProcess(proc_name, "", "", false, proc_smode_normal, false); - - // sleep 2 seconds - eCAL::Process::SleepMS(2000); - - // stop process - eCAL::Process::StopProcess(proc_name); - - // sleep 2 seconds - eCAL::Process::SleepMS(2000); - - // start process - int pid = eCAL::Process::StartProcess(proc_name, "", "", false, proc_smode_normal, false); - - // sleep 2 seconds - eCAL::Process::SleepMS(2000); - - // stop notepad - eCAL::Process::StopProcess(pid); - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/misc/time/src/time.cpp b/samples/cpp/misc/time/src/time.cpp deleted file mode 100644 index e3b1d3c..0000000 --- a/samples/cpp/misc/time/src/time.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include -#include - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "timer"); - - std::cout << "eCAL Time Interface : " << eCAL::Time::GetName() << std::endl; - std::cout << "eCAL Time is Synchronized : " << eCAL::Time::IsSynchronized() << std::endl; - std::cout << "eCAL Time is Master : " << eCAL::Time::IsMaster() << std::endl; - std::cout << std::endl; - eCAL::Process::SleepMS(3000); - - while(eCAL::Ok()) - { - auto now = eCAL::Time::ecal_clock::now(); - std::cout << "eCAL Time " << std::chrono::duration_cast(now.time_since_epoch()).count() << " ms" << std::endl; - eCAL::Process::SleepMS(100); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/monitoring/monitoring_get_services/CMakeLists.txt b/samples/cpp/monitoring/monitoring_get_services/CMakeLists.txt deleted file mode 100644 index 9449124..0000000 --- a/samples/cpp/monitoring/monitoring_get_services/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(monitoring_get_services) - -find_package(eCAL REQUIRED) - -set(monitoring_get_services_src - src/monitoring_get_services.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${monitoring_get_services_src}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_link_libraries(${PROJECT_NAME} eCAL::core_pb) - -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/samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp b/samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp deleted file mode 100644 index 3f40b7a..0000000 --- a/samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - int run(0), runs(100000); - std::chrono::steady_clock::time_point start_time; - - // initialize eCAL core API - eCAL::Initialize(argc, argv, "monitoring get services"); - - // monitor for ever - while(eCAL::Ok()) - { - // GetServices - { - std::map, eCAL::Util::SServiceMethodInfo> service_info_map; - - start_time = std::chrono::steady_clock::now(); - for (run = 0; run < runs; ++run) - { - eCAL::Util::GetServices(service_info_map); - } - - auto num_services = service_info_map.size(); - auto diff_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time); - std::cout << "GetServices : " << static_cast(diff_time.count()) / runs << " ms" << " (" << num_services << " services)" << std::endl; - std::cout << std::endl; - } - - // GetServiceNames - { - std::vector> service_method_names; - - start_time = std::chrono::steady_clock::now(); - for (run = 0; run < runs; ++run) - { - eCAL::Util::GetServiceNames(service_method_names); - } - - auto num_services = service_method_names.size(); - auto diff_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time); - std::cout << "GetServicesNames : " << static_cast(diff_time.count()) / runs << " ms" << " (" << num_services << " services)" << std::endl; - std::cout << std::endl; - } - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/monitoring/monitoring_get_topics/CMakeLists.txt b/samples/cpp/monitoring/monitoring_get_topics/CMakeLists.txt deleted file mode 100644 index bf8148e..0000000 --- a/samples/cpp/monitoring/monitoring_get_topics/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(monitoring_get_topics) - -find_package(eCAL REQUIRED) - -set(monitoring_get_topics_src - src/monitoring_get_topics.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${monitoring_get_topics_src}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_link_libraries(${PROJECT_NAME} eCAL::core_pb) - -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/samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp b/samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp deleted file mode 100644 index fb1d28a..0000000 --- a/samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - int run(0), runs(1000); - std::chrono::steady_clock::time_point start_time; - - // initialize eCAL core API - eCAL::Initialize(argc, argv, "monitoring get topics"); - - // monitor for ever - while(eCAL::Ok()) - { - // GetTopics - { - std::unordered_map topic_info_map; - - start_time = std::chrono::steady_clock::now(); - for (run = 0; run < runs; ++run) - { - eCAL::Util::GetTopics(topic_info_map); - } - - auto num_topics = topic_info_map.size(); - auto diff_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time); - std::cout << "GetTopics : " << static_cast(diff_time.count()) / runs << " ms" << " (" << num_topics << " topics)" << std::endl; - std::cout << std::endl; - } - - // GetTopicNames - { - std::vector topic_names; - - start_time = std::chrono::steady_clock::now(); - for (run = 0; run < runs; ++run) - { - eCAL::Util::GetTopicNames(topic_names); - } - - auto num_topics = topic_names.size(); - auto diff_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time); - std::cout << "GetTopicsNames : " << static_cast(diff_time.count()) / runs << " ms" << " (" << num_topics << " topics)" << std::endl; - std::cout << std::endl; - } - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/monitoring/monitoring_rec/CMakeLists.txt b/samples/cpp/monitoring/monitoring_rec/CMakeLists.txt index e5cf029..7fb68c6 100644 --- a/samples/cpp/monitoring/monitoring_rec/CMakeLists.txt +++ b/samples/cpp/monitoring/monitoring_rec/CMakeLists.txt @@ -23,16 +23,27 @@ set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) project(monitoring_rec) find_package(eCAL REQUIRED) +find_package(Protobuf REQUIRED) set(monitoring_rec_src src/monitoring_rec.cpp ) +set(monitoring_rec_proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/host.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/layer.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/monitoring.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/process.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/service.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/topic.proto +) ecal_add_sample(${PROJECT_NAME} ${monitoring_rec_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${monitoring_rec_proto}) -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_link_libraries(${PROJECT_NAME} eCAL::core_pb) +target_link_libraries(${PROJECT_NAME} + eCAL::core + protobuf::libprotobuf +) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) diff --git a/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp b/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp index fd45ba3..2c2d31f 100644 --- a/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp +++ b/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp @@ -22,10 +22,9 @@ #include #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4100 4127 4146 4800 4505) // disable proto warnings +#pragma warning(push, 0) #endif -#include +#include #ifdef _MSC_VER #pragma warning(pop) #endif @@ -45,35 +44,20 @@ int main(int argc, char **argv) // initialize eCAL core API eCAL::Initialize(argc, argv, "monitoring", eCAL::Init::All); - // set filtering exclusive regex - eCAL::Monitoring::SetExclFilter("ecal.*"); - - // set filtering inclusive regex - eCAL::Monitoring::SetInclFilter(""); - - // set filtering on - eCAL::Monitoring::SetFilterState(true); - - // activate monitoring publisher - eCAL::Monitoring::PubMonitoring(true); - - // activate logging publisher - eCAL::Monitoring::PubLogging(true); - - // monitoring instance to store complete snapshot + // monitoring instance to store snapshot eCAL::pb::Monitoring monitoring; std::string monitoring_s; // monitor for ever while(eCAL::Ok()) { - // take snapshot :-) - eCAL::Monitoring::GetMonitoring(monitoring_s); - monitoring.ParseFromString(monitoring_s); - // monitor hosts ? if(g_do_monitor_hosts) { + // take snapshot :-) + eCAL::Monitoring::GetMonitoring(monitoring_s, eCAL::Monitoring::Entity::Host); + monitoring.ParseFromString(monitoring_s); + // collect host infos std::cout << "-------- HOSTS ----------" << std::endl; @@ -93,6 +77,10 @@ int main(int argc, char **argv) // monitor processes ? if(g_do_monitor_procs) { + // take snapshot :-) + eCAL::Monitoring::GetMonitoring(monitoring_s, eCAL::Monitoring::Entity::Process); + monitoring.ParseFromString(monitoring_s); + // collect process infos std::cout << "------- PROCESSES -------" << std::endl; @@ -108,9 +96,6 @@ int main(int argc, char **argv) std::cout << "pname : " << process.pname() << std::endl; // process name std::cout << "uname : " << process.uname() << std::endl; // unit name std::cout << "pparam : " << process.pparam() << std::endl; // process parameter - std::cout << "pmemory : " << process.pmemory() << std::endl; // process memory - std::cout << "pcpu : " << process.pcpu() << std::endl; // process cpu usage - std::cout << "usrptime : " << process.usrptime() << std::endl; // process user time std::cout << "datawrite : " << process.datawrite() << std::endl; // data write bytes per sec std::cout << "dataread : " << process.dataread() << std::endl; // date read bytes per sec std::cout << "severity : " << process.state().severity() << std::endl; // process state severity @@ -124,6 +109,10 @@ int main(int argc, char **argv) // monitor services ? if(g_do_monitor_services) { + // take snapshot :-) + eCAL::Monitoring::GetMonitoring(monitoring_s, eCAL::Monitoring::Entity::Server | eCAL::Monitoring::Entity::Client); + monitoring.ParseFromString(monitoring_s); + // collect process infos std::cout << "------- SERVICES -------" << std::endl; @@ -157,6 +146,10 @@ int main(int argc, char **argv) // monitor topics ? if(g_do_monitor_topics) { + // take snapshot :-) + eCAL::Monitoring::GetMonitoring(monitoring_s, eCAL::Monitoring::Entity::Publisher | eCAL::Monitoring::Entity::Subscriber); + monitoring.ParseFromString(monitoring_s); + // collect topic infos std::cout << "-------- TOPICS ---------" << std::endl; @@ -195,9 +188,6 @@ int main(int argc, char **argv) case eCAL::pb::eTLayerType::tl_ecal_tcp: layer_type = "tlayer_tcp"; break; - case eCAL::pb::eTLayerType::tl_inproc: - layer_type = "tlayer_inproc"; - break; case eCAL::pb::eTLayerType::tl_all: layer_type = "tlayer_all"; break; diff --git a/ecal/core_pb/src/ecal/core/pb/host.proto b/samples/cpp/monitoring/monitoring_rec/src/protobuf/host.proto similarity index 100% rename from ecal/core_pb/src/ecal/core/pb/host.proto rename to samples/cpp/monitoring/monitoring_rec/src/protobuf/host.proto diff --git a/ecal/core_pb/src/ecal/core/pb/layer.proto b/samples/cpp/monitoring/monitoring_rec/src/protobuf/layer.proto similarity index 72% rename from ecal/core_pb/src/ecal/core/pb/layer.proto rename to samples/cpp/monitoring/monitoring_rec/src/protobuf/layer.proto index 5e5aa5c..d6de62d 100644 --- a/ecal/core_pb/src/ecal/core/pb/layer.proto +++ b/samples/cpp/monitoring/monitoring_rec/src/protobuf/layer.proto @@ -43,7 +43,6 @@ message ConnnectionPar // connection parameter for read { LayerParUdpMC layer_par_udpmc = 1; // parameter for ecal udp multicast LayerParShm layer_par_shm = 2; // parameter for ecal shared memory - LayerParInproc layer_par_inproc = 3; // parameter for ecal inner process LayerParTcp layer_par_tcp = 4; // parameter for ecal tcp } @@ -51,11 +50,8 @@ enum eTLayerType // transport layer { tl_none = 0; // undefined tl_ecal_udp_mc = 1; // ecal udp multicast - // 2 = ecal udp unicast (not supported anymore) - // 3 = ecal udp metal (not supported anymore) tl_ecal_shm = 4; // ecal shared memory tl_ecal_tcp = 5; // ecal tcp - tl_inproc = 42; // inproc (inner process) tl_all = 255; // all layer } @@ -64,14 +60,5 @@ message TLayer eTLayerType type = 1; // transport layer type int32 version = 2; // transport layer version bool confirmed = 3; // transport layer used ? - - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - bytes par_shm = 4; // transport layer parameter (shm only, deprecated) - // ---------------------------------------------------------------------- - // REMOVE ME IN ECAL6 - // ---------------------------------------------------------------------- - ConnnectionPar par_layer = 5; // transport layer parameter } diff --git a/samples/cpp/person/person_snd_events/src/protobuf/person.proto b/samples/cpp/monitoring/monitoring_rec/src/protobuf/monitoring.proto similarity index 61% rename from samples/cpp/person/person_snd_events/src/protobuf/person.proto rename to samples/cpp/monitoring/monitoring_rec/src/protobuf/monitoring.proto index 4200a43..790e74f 100644 --- a/samples/cpp/person/person_snd_events/src/protobuf/person.proto +++ b/samples/cpp/monitoring/monitoring_rec/src/protobuf/monitoring.proto @@ -19,24 +19,18 @@ syntax = "proto3"; -import "animal.proto"; -import "house.proto"; +import "host.proto"; +import "process.proto"; +import "service.proto"; +import "topic.proto"; -package pb.People; +package eCAL.pb; -message Person +message Monitoring // eCAL monitoring information { - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - - Animal.Dog dog = 5; - Environment.House house = 6; + repeated Host hosts = 1; // hosts + repeated Process processes = 2; // processes + repeated Service services = 3; // services + repeated Client clients = 5; // clients + repeated Topic topics = 4; // topics } diff --git a/ecal/core_pb/src/ecal/core/pb/process.proto b/samples/cpp/monitoring/monitoring_rec/src/protobuf/process.proto similarity index 91% rename from ecal/core_pb/src/ecal/core/pb/process.proto rename to samples/cpp/monitoring/monitoring_rec/src/protobuf/process.proto index cd06998..2c104e8 100644 --- a/ecal/core_pb/src/ecal/core/pb/process.proto +++ b/samples/cpp/monitoring/monitoring_rec/src/protobuf/process.proto @@ -58,19 +58,17 @@ message Process // process { int32 rclock = 1; // registration clock string hname = 2; // host name + string hgname = 18; // host group name int32 pid = 3; // process id string pname = 4; // process name string uname = 5; // unit name string pparam = 6; // process parameter - int64 pmemory = 7; // process memory - float pcpu = 8; // process cpu usage - float usrptime = 9; // process user time int64 datawrite = 10; // data write bytes per sec int64 dataread = 11; // data read bytes per sec ProcessState state = 12; // process state info - eTSyncState tsync_state = 13; // time sychronisation state - string tsync_mod_name = 14; // time sychronisation module name - int32 component_init_state = 15; // eCAL component initilization state (eCAL::Initialize(..)) + eTSyncState tsync_state = 13; // time synchronization state + string tsync_mod_name = 14; // time synchronization module name + int32 component_init_state = 15; // eCAL component initialization state (eCAL::Initialize(..)) string component_init_info = 16; // like comp_init_state as human readable string (pub|sub|srv|mon|log|time|proc) string ecal_runtime_version = 17; // loaded / runtime eCAL version of a component } diff --git a/ecal/core_pb/src/ecal/core/pb/service.proto b/samples/cpp/monitoring/monitoring_rec/src/protobuf/service.proto similarity index 88% rename from ecal/core_pb/src/ecal/core/pb/service.proto rename to samples/cpp/monitoring/monitoring_rec/src/protobuf/service.proto index 1c6afb9..6c4b591 100644 --- a/ecal/core_pb/src/ecal/core/pb/service.proto +++ b/samples/cpp/monitoring/monitoring_rec/src/protobuf/service.proto @@ -71,8 +71,12 @@ message Service // service int32 pid = 5; // process id string sname = 6; // service name string sid = 9; // service id - uint32 tcp_port = 7; // the tcp port used for that service repeated Method methods = 8; // list of methods + + // transport specific parameter (for internal use) + uint32 version = 10; // service protocol version + uint32 tcp_port_v0 = 7; // the tcp port used for that service + uint32 tcp_port_v1 = 11; // the tcp port used for that service } message Client // client @@ -84,4 +88,7 @@ message Client // client int32 pid = 5; // process id string sname = 6; // service name string sid = 7; // service id + + // transport specific parameter (for internal use) + uint32 version = 8; // client protocol version } diff --git a/samples/cpp/monitoring/monitoring_rec/src/protobuf/topic.proto b/samples/cpp/monitoring/monitoring_rec/src/protobuf/topic.proto new file mode 100644 index 0000000..5228d7f --- /dev/null +++ b/samples/cpp/monitoring/monitoring_rec/src/protobuf/topic.proto @@ -0,0 +1,61 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +import "layer.proto"; + +package eCAL.pb; + +message DataTypeInformation +{ + string name = 1; // name of the datatype + string encoding = 2; // encoding of the datatype (e.g. protobuf, flatbuffers, capnproto) + bytes desc = 3; // descriptor information of the datatype (necessary for reflection) +} + +message Topic // eCAL topic +{ + int32 rclock = 1; // registration clock (heart beat) + string hname = 2; // host name + string hgname = 28; // host group name + int32 pid = 3; // process id + string pname = 4; // process name + string uname = 5; // unit name + string tid = 6; // topic id + string tname = 7; // topic name + string direction = 8; // direction (publisher, subscriber) + string ttype = 9; // topic type + topic encoding (deprecated) + bytes tdesc = 10; // topic description (protocol descriptor) (deprecated) + + DataTypeInformation tdatatype = 30; // topic datatype information (encoding & type & description) + + repeated TLayer tlayer = 12; // active topic transport layers and it's specific parameter + int32 tsize = 13; // topic size + + int32 connections_loc = 16; // number of local connected entities + int32 connections_ext = 17; // number of external connected entities + int32 message_drops = 18; // dropped messages + + int64 did = 19; // data send id (publisher setid) + int64 dclock = 20; // data clock (send / receive action) + int32 dfreq = 21; // data frequency (send / receive samples per second) [mHz] + + map attr = 27; // generic topic description +} diff --git a/samples/cpp/monitoring/monitoring_reg/CMakeLists.txt b/samples/cpp/monitoring/monitoring_reg/CMakeLists.txt index fb3d0a2..6836b09 100644 --- a/samples/cpp/monitoring/monitoring_reg/CMakeLists.txt +++ b/samples/cpp/monitoring/monitoring_reg/CMakeLists.txt @@ -23,16 +23,27 @@ set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) project(monitoring_reg) find_package(eCAL REQUIRED) +find_package(Protobuf REQUIRED) set(monitoring_reg_src src/monitoring_reg.cpp ) +set(monitoring_reg_proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/ecal.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/host.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/layer.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/process.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/service.proto + ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/topic.proto +) ecal_add_sample(${PROJECT_NAME} ${monitoring_reg_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${monitoring_reg_proto}) -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_link_libraries(${PROJECT_NAME} eCAL::core_pb) +target_link_libraries(${PROJECT_NAME} + eCAL::core + protobuf::libprotobuf +) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) diff --git a/samples/cpp/monitoring/monitoring_reg/src/monitoring_reg.cpp b/samples/cpp/monitoring/monitoring_reg/src/monitoring_reg.cpp index 9864cb4..b314c50 100644 --- a/samples/cpp/monitoring/monitoring_reg/src/monitoring_reg.cpp +++ b/samples/cpp/monitoring/monitoring_reg/src/monitoring_reg.cpp @@ -22,10 +22,9 @@ #include #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4100 4127 4146 4800 4505) // disable proto warnings +#pragma warning(push, 0) #endif -#include +#include #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/ecal/core_pb/src/ecal/core/pb/ecal.proto b/samples/cpp/monitoring/monitoring_reg/src/protobuf/ecal.proto similarity index 59% rename from ecal/core_pb/src/ecal/core/pb/ecal.proto rename to samples/cpp/monitoring/monitoring_reg/src/protobuf/ecal.proto index 9dad248..bf1ba16 100644 --- a/ecal/core_pb/src/ecal/core/pb/ecal.proto +++ b/samples/cpp/monitoring/monitoring_reg/src/protobuf/ecal.proto @@ -19,10 +19,10 @@ syntax = "proto3"; -import "ecal/core/pb/host.proto"; -import "ecal/core/pb/process.proto"; -import "ecal/core/pb/service.proto"; -import "ecal/core/pb/topic.proto"; +import "host.proto"; +import "process.proto"; +import "service.proto"; +import "topic.proto"; package eCAL.pb; @@ -31,20 +31,26 @@ message Content // topic content int64 id = 1; // sample id int64 clock = 2; // internal used clock int64 time = 3; // time the content was updated - bytes payload = 4; // octet stream - int32 size = 6; // size (redundant for compatibility) int64 hash = 7; // unique hash for that sample + int32 size = 6; // size (additional for none payload "header only samples") + bytes payload = 4; // octet stream } enum eCmdType // command type { - bct_none = 0; // undefined command - bct_set_sample = 1; // set sample content - bct_reg_publisher = 2; // register publisher - bct_reg_subscriber = 3; // register subscriber - bct_reg_process = 4; // register process - bct_reg_service = 5; // register service - bct_reg_client = 6; // register client + bct_none = 0; // undefined command + bct_set_sample = 1; // set sample content + bct_reg_publisher = 2; // register publisher + bct_reg_subscriber = 3; // register subscriber + bct_reg_process = 4; // register process + bct_reg_service = 5; // register service + bct_reg_client = 6; // register client + + bct_unreg_publisher = 12; // unregister publisher + bct_unreg_subscriber = 13; // unregister subscriber + bct_unreg_process = 14; // unregister process + bct_unreg_service = 15; // unregister service + bct_unreg_client = 16; // unregister client } message Sample // a sample is a topic, it's descriptions and it's content @@ -56,9 +62,10 @@ message Sample // a sample is a topic, it's desc Client client = 7; // client information Topic topic = 5; // topic information Content content = 6; // topic content + bytes padding = 8; // 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 } message SampleList { - repeated Sample samples = 1; + repeated Sample samples = 1; // list of Samples used currently by SHM registration } diff --git a/samples/cpp/person/person_snd_dyn/src/protobuf/animal.proto b/samples/cpp/monitoring/monitoring_reg/src/protobuf/host.proto similarity index 71% rename from samples/cpp/person/person_snd_dyn/src/protobuf/animal.proto rename to samples/cpp/monitoring/monitoring_reg/src/protobuf/host.proto index 625a78d..f16fdfe 100644 --- a/samples/cpp/person/person_snd_dyn/src/protobuf/animal.proto +++ b/samples/cpp/monitoring/monitoring_reg/src/protobuf/host.proto @@ -19,10 +19,15 @@ syntax = "proto3"; -package pb.Animal; +package eCAL.pb; -message Dog +message OSInfo // operating system details { - string name = 1; - string colour = 2; + string osname = 1; // name +} + +message Host // eCAL host +{ + string hname = 1; // host name + OSInfo os = 2; // operating system details } diff --git a/samples/cpp/monitoring/monitoring_reg/src/protobuf/layer.proto b/samples/cpp/monitoring/monitoring_reg/src/protobuf/layer.proto new file mode 100644 index 0000000..d6de62d --- /dev/null +++ b/samples/cpp/monitoring/monitoring_reg/src/protobuf/layer.proto @@ -0,0 +1,64 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +package eCAL.pb; + +message LayerParUdpMC +{ +} + +message LayerParShm +{ + repeated string memory_file_list = 1; // list of memory file names +} + +message LayerParInproc +{ +} + +message LayerParTcp +{ + int32 port = 1; // tcp writers port number +} + +message ConnnectionPar // connection parameter for reader / writer +{ + LayerParUdpMC layer_par_udpmc = 1; // parameter for ecal udp multicast + LayerParShm layer_par_shm = 2; // parameter for ecal shared memory + LayerParTcp layer_par_tcp = 4; // parameter for ecal tcp +} + +enum eTLayerType // transport layer +{ + tl_none = 0; // undefined + tl_ecal_udp_mc = 1; // ecal udp multicast + tl_ecal_shm = 4; // ecal shared memory + tl_ecal_tcp = 5; // ecal tcp + tl_all = 255; // all layer +} + +message TLayer +{ + eTLayerType type = 1; // transport layer type + int32 version = 2; // transport layer version + bool confirmed = 3; // transport layer used ? + ConnnectionPar par_layer = 5; // transport layer parameter +} diff --git a/samples/cpp/monitoring/monitoring_reg/src/protobuf/process.proto b/samples/cpp/monitoring/monitoring_reg/src/protobuf/process.proto new file mode 100644 index 0000000..2c104e8 --- /dev/null +++ b/samples/cpp/monitoring/monitoring_reg/src/protobuf/process.proto @@ -0,0 +1,74 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +package eCAL.pb; + +enum eProcessSeverity // process severity +{ + proc_sev_unknown = 0; // condition unknown + proc_sev_healthy = 1; // process healthy + proc_sev_warning = 2; // process warning level + proc_sev_critical = 3; // process critical + proc_sev_failed = 4; // process failed +} + +enum eProcessSeverityLevel // process severity level +{ + proc_sev_level_unknown = 0; // condition unknown + proc_sev_level1 = 1; // default severity level 1 + proc_sev_level2 = 2; // severity level 2 + proc_sev_level3 = 3; // severity level 3 + proc_sev_level4 = 4; // severity level 4 + proc_sev_level5 = 5; // severity level 5 +} + +message ProcessState // process state +{ + eProcessSeverity severity = 1; // severity + eProcessSeverityLevel severity_level = 3; // severity level + string info = 2; // info string +} + +enum eTSyncState // time synchronisation +{ + tsync_none = 0; // not synchronized + tsync_realtime = 1; // real time sync mode + tsync_replay = 2; // replay time sync mode +} + +message Process // process +{ + int32 rclock = 1; // registration clock + string hname = 2; // host name + string hgname = 18; // host group name + int32 pid = 3; // process id + string pname = 4; // process name + string uname = 5; // unit name + string pparam = 6; // process parameter + int64 datawrite = 10; // data write bytes per sec + int64 dataread = 11; // data read bytes per sec + ProcessState state = 12; // process state info + eTSyncState tsync_state = 13; // time synchronization state + string tsync_mod_name = 14; // time synchronization module name + int32 component_init_state = 15; // eCAL component initialization state (eCAL::Initialize(..)) + string component_init_info = 16; // like comp_init_state as human readable string (pub|sub|srv|mon|log|time|proc) + string ecal_runtime_version = 17; // loaded / runtime eCAL version of a component +} diff --git a/samples/cpp/monitoring/monitoring_reg/src/protobuf/service.proto b/samples/cpp/monitoring/monitoring_reg/src/protobuf/service.proto new file mode 100644 index 0000000..6c4b591 --- /dev/null +++ b/samples/cpp/monitoring/monitoring_reg/src/protobuf/service.proto @@ -0,0 +1,94 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +package eCAL.pb; + +message ServiceHeader +{ + enum eCallState + { + none = 0; + executed = 1; + failed = 2; + } + + string hname = 1; // host name + string sname = 2; // service name + string sid = 7; // service id + string mname = 3; // method name + string error = 4; // error message + int32 id = 5; // session id + eCallState state = 6; // method call state +} + +message Request // client request +{ + ServiceHeader header = 1; // common service header + bytes request = 2; // request payload +} + +message Response // server response +{ + ServiceHeader header = 1; // common service header + bytes response = 2; // response payload + int64 ret_state = 3; // callback return state +} + +message Method // method +{ + string mname = 1; // method name + string req_type = 2; // request type + bytes req_desc = 5; // request descriptor + string resp_type = 3; // response type + bytes resp_desc = 6; // response descriptor + int64 call_count = 4; // call counter +} + +message Service // service +{ + int32 rclock = 1; // registration clock + string hname = 2; // host name + string pname = 3; // process name + string uname = 4; // unit name + int32 pid = 5; // process id + string sname = 6; // service name + string sid = 9; // service id + repeated Method methods = 8; // list of methods + + // transport specific parameter (for internal use) + uint32 version = 10; // service protocol version + uint32 tcp_port_v0 = 7; // the tcp port used for that service + uint32 tcp_port_v1 = 11; // the tcp port used for that service +} + +message Client // client +{ + int32 rclock = 1; // registration clock + string hname = 2; // host name + string pname = 3; // process name + string uname = 4; // unit name + int32 pid = 5; // process id + string sname = 6; // service name + string sid = 7; // service id + + // transport specific parameter (for internal use) + uint32 version = 8; // client protocol version +} diff --git a/samples/cpp/monitoring/monitoring_reg/src/protobuf/topic.proto b/samples/cpp/monitoring/monitoring_reg/src/protobuf/topic.proto new file mode 100644 index 0000000..5228d7f --- /dev/null +++ b/samples/cpp/monitoring/monitoring_reg/src/protobuf/topic.proto @@ -0,0 +1,61 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +import "layer.proto"; + +package eCAL.pb; + +message DataTypeInformation +{ + string name = 1; // name of the datatype + string encoding = 2; // encoding of the datatype (e.g. protobuf, flatbuffers, capnproto) + bytes desc = 3; // descriptor information of the datatype (necessary for reflection) +} + +message Topic // eCAL topic +{ + int32 rclock = 1; // registration clock (heart beat) + string hname = 2; // host name + string hgname = 28; // host group name + int32 pid = 3; // process id + string pname = 4; // process name + string uname = 5; // unit name + string tid = 6; // topic id + string tname = 7; // topic name + string direction = 8; // direction (publisher, subscriber) + string ttype = 9; // topic type + topic encoding (deprecated) + bytes tdesc = 10; // topic description (protocol descriptor) (deprecated) + + DataTypeInformation tdatatype = 30; // topic datatype information (encoding & type & description) + + repeated TLayer tlayer = 12; // active topic transport layers and it's specific parameter + int32 tsize = 13; // topic size + + int32 connections_loc = 16; // number of local connected entities + int32 connections_ext = 17; // number of external connected entities + int32 message_drops = 18; // dropped messages + + int64 did = 19; // data send id (publisher setid) + int64 dclock = 20; // data clock (send / receive action) + int32 dfreq = 21; // data frequency (send / receive samples per second) [mHz] + + map attr = 27; // generic topic description +} diff --git a/samples/cpp/msgpack/address_rec/src/address_rec.cpp b/samples/cpp/msgpack/address_rec/src/address_rec.cpp deleted file mode 100644 index 0cff8b2..0000000 --- a/samples/cpp/msgpack/address_rec/src/address_rec.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include - -class CAddress -{ -public: - CAddress() : - street_number(0) - {}; - - std::string fname; - std::string lname; - std::string street; - int street_number; - std::string town; - std::string country; - - MSGPACK_DEFINE(fname, lname, street, street_number, town, country); -}; - -void OnAddress(const char* topic_name_, const CAddress& msg_, const long long time_) -{ - std::cout << "topic name : " << topic_name_ << std::endl; - std::cout << "time : " << time_ << std::endl; - std::cout << "first name : " << msg_.fname << std::endl; - std::cout << "last name : " << msg_.lname << std::endl; - std::cout << "street : " << msg_.street << std::endl; - std::cout << "street_number : " << msg_.street_number << std::endl; - std::cout << "town : " << msg_.town << std::endl; - std::cout << "country : " << msg_.country << std::endl; - std::cout << std::endl; -} - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "address subscriber"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a subscriber (topic name "address") - eCAL::messagepack::CSubscriber sub("address"); - - // add receive callback function (_1 = topic_name, _2 = msg, _3 = time) - auto callback = std::bind(OnAddress, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - sub.AddReceiveCallback(callback); - - // enter main loop - while (eCAL::Ok()) - { - // sleep 500 ms - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/msgpack/address_snd/src/address_snd.cpp b/samples/cpp/msgpack/address_snd/src/address_snd.cpp deleted file mode 100644 index 810adb2..0000000 --- a/samples/cpp/msgpack/address_snd/src/address_snd.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include - -class CAddress -{ -public: - CAddress() : - street_number(0) - {}; - - std::string fname; - std::string lname; - std::string street; - int street_number; - std::string town; - std::string country; - - MSGPACK_DEFINE(fname, lname, street, street_number, town, country); -}; - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "address publisher"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a publisher (topic name "address") - eCAL::messagepack::CPublisher pub("address"); - - // create the message - CAddress address; - address.fname = "Peter"; - address.lname = "Miller"; - address.street = "Continental drive"; - address.street_number = 1; - address.town = "Auburn Hills"; - address.country = "USA"; - - // enter main loop - while (eCAL::Ok()) - { - // send content - pub.Send(address, -1); - - // print content - std::cout << "first name : " << address.fname << std::endl; - std::cout << "last name : " << address.lname << std::endl; - std::cout << "street : " << address.street << std::endl; - std::cout << "street_number : " << address.street_number << std::endl; - std::cout << "town : " << address.town << std::endl; - std::cout << "country : " << address.country << std::endl; - std::cout << std::endl; - - // sleep 500 ms - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/multiple/multiple_rec_cb/CMakeLists.txt b/samples/cpp/multiple/multiple_rec_cb/CMakeLists.txt deleted file mode 100644 index c418824..0000000 --- a/samples/cpp/multiple/multiple_rec_cb/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(multiple_rec_cb) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(multiple_rec_cb_src - src/multiple_rec_cb.cpp - src/multiple_rec_cb_person.cpp - src/multiple_rec_cb_vector.cpp - src/main.cpp -) - -set(multiple_rec_cb_headers - src/multiple_rec_cb.h - src/multiple_rec_cb_person.h - src/multiple_rec_cb_vector.h - src/main.cpp -) - -set(multiple_rec_cb_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) - -ecal_add_sample(${PROJECT_NAME} ${multiple_rec_cb_headers} ${multiple_rec_cb_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${multiple_rec_cb_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/multiple) diff --git a/samples/cpp/multiple/multiple_rec_cb/src/main.cpp b/samples/cpp/multiple/multiple_rec_cb/src/main.cpp deleted file mode 100644 index 73d87c1..0000000 --- a/samples/cpp/multiple/multiple_rec_cb/src/main.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#define MULTIPLE_REC 1 -#define MULTIPLE_REC_VECTOR 0 -#define MULTIPLE_REC_PERSON 0 - -#include "multiple_rec_cb.h" -#include "multiple_rec_cb_vector.h" -#include "multiple_rec_cb_person.h" - -int main(int argc, char **argv) -{ -#if MULTIPLE_REC - MultipleRec(argc, argv); -#endif - -#if MULTIPLE_REC_VECTOR - MultipleRecVector(argc, argv); -#endif - -#if MULTIPLE_REC_PERSON - MultipleRecPerson(argc, argv); -#endif - return(0); -} diff --git a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb.h b/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb.h deleted file mode 100644 index 047fe2e..0000000 --- a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb.h +++ /dev/null @@ -1,22 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -void MultipleRec(int argc, char **argv); diff --git a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.cpp b/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.cpp deleted file mode 100644 index c3d7979..0000000 --- a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define SUBSCRIBER_NUMBER 5 - -typedef std::map SubMapT; -SubMapT g_sub_map_person; -std::mutex g_sub_map_sync_person; - -// global subscriber callback -void OnReceivePerson(const char* topic_name_) -{ - // statistics - { - std::lock_guard lock(g_sub_map_sync_person); - g_sub_map_person[topic_name_]++; - std::cout << "Received something from " << topic_name_ << std::endl; - } -} - -void MultipleRecPerson(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "multiple_rec_cb"); - - // create dummy subscriber - std::vector> vector_of_subscribers; - std::cout << "create subscribers .." << std::endl; - for(int i = 0; i < SUBSCRIBER_NUMBER; i++) - { - // publisher topic name - std::stringstream tname; - tname << "PERSON_" << i; - - // create subscriber - vector_of_subscribers.emplace_back(tname.str()); - - // setup receive callback - auto callback = std::bind(OnReceivePerson, std::placeholders::_1); - vector_of_subscribers.back().AddReceiveCallback(callback); - } - - // idle main thread - while(eCAL::Ok()) - { - // sleep 100 ms - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.h b/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.h deleted file mode 100644 index 5890b99..0000000 --- a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_person.h +++ /dev/null @@ -1,22 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -void MultipleRecPerson(int argc, char **argv); diff --git a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.cpp b/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.cpp deleted file mode 100644 index b1594eb..0000000 --- a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include -#include -#include -#include - -#define SUBSCRIBER_NUMBER 10 - -typedef std::map SubMapT; -SubMapT g_sub_map_vector; -std::mutex g_sub_map_sync_vector; - -// global subscriber callback -void OnReceiveVector(const char* topic_name_) -{ - // statistics - { - std::lock_guard lock(g_sub_map_sync_vector); - g_sub_map_vector[topic_name_]++; - std::cout << "Received something from " << topic_name_ << std::endl; - } -} - -void MultipleRecVector(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "multiple_rec_cb"); - - // create dummy subscriber - std::vector vector_of_subscribers; - std::cout << "create subscribers .." << std::endl; - for(int i = 0; i < SUBSCRIBER_NUMBER; i++) - { - // publisher topic name - std::stringstream tname; - tname << "PUB_" << i; - - // create subscriber - vector_of_subscribers.emplace_back(tname.str()); - - // setup receive callback - auto callback = std::bind(OnReceiveVector, std::placeholders::_1); - vector_of_subscribers.back().AddReceiveCallback(callback); - } - - // idle main thread - while(eCAL::Ok()) - { - // sleep 100 ms - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.h b/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.h deleted file mode 100644 index 5be5675..0000000 --- a/samples/cpp/multiple/multiple_rec_cb/src/multiple_rec_cb_vector.h +++ /dev/null @@ -1,22 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -void MultipleRecVector(int argc, char **argv); diff --git a/samples/cpp/multiple/multiple_snd/CMakeLists.txt b/samples/cpp/multiple/multiple_snd/CMakeLists.txt deleted file mode 100644 index f0dbc3c..0000000 --- a/samples/cpp/multiple/multiple_snd/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(multiple_snd) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(multiple_snd_src - src/multiple_snd.cpp - src/multiple_snd_person.cpp - src/multiple_snd_vector.cpp - src/main.cpp -) - -set(multiple_snd_headers - src/multiple_snd.h - src/multiple_snd_person.h - src/multiple_snd_vector.h - src/main.cpp -) - -set(multiple_snd_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) -ecal_add_sample(${PROJECT_NAME} ${multiple_snd_headers} ${multiple_snd_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${multiple_snd_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/multiple) diff --git a/samples/cpp/multiple/multiple_snd/src/main.cpp b/samples/cpp/multiple/multiple_snd/src/main.cpp deleted file mode 100644 index ff526dd..0000000 --- a/samples/cpp/multiple/multiple_snd/src/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#define MULTIPLE_SEND 1 -#define MULTIPLE_SEND_VECTOR 0 -#define MULTIPLE_SEND_PERSON 0 - -#include "multiple_snd.h" -#include "multiple_snd_vector.h" -#include "multiple_snd_person.h" - -int main(int argc, char **argv) -{ -#if MULTIPLE_SEND - MultipleSend(argc, argv); -#endif - -#if MULTIPLE_SEND_VECTOR - MultipleSendVector(argc, argv); -#endif - -#if MULTIPLE_SEND_PERSON - MultipleSendPerson(argc, argv); -#endif - - return(0); -} diff --git a/samples/cpp/multiple/multiple_snd/src/multiple_snd.h b/samples/cpp/multiple/multiple_snd/src/multiple_snd.h deleted file mode 100644 index 9b44796..0000000 --- a/samples/cpp/multiple/multiple_snd/src/multiple_snd.h +++ /dev/null @@ -1,22 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -void MultipleSend(int argc, char **argv); diff --git a/samples/cpp/multiple/multiple_snd/src/multiple_snd_person.cpp b/samples/cpp/multiple/multiple_snd/src/multiple_snd_person.cpp deleted file mode 100644 index 4964b37..0000000 --- a/samples/cpp/multiple/multiple_snd/src/multiple_snd_person.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define PUBLISHER_NUMBER 5 -const std::array PersonNames{ "Max", "Marie", "Jana", "Christoph", "Sarah"}; - -using PersonSendVector_t = std::vector> ; - -void SendAll(PersonSendVector_t& vector) -{ - pb::People::Person person; - int person_index = 0; - for (auto& pub : vector) - { - person.set_name(PersonNames[person_index]); - person.set_id(person_index); - pub.Send(person); - person_index++; - } -} - -void MultipleSendPerson(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "multiple_snd_vector"); - - // create dummy subscriber - std::vector> vector_of_publishers; - std::cout << "Creating publishers ..." << std::endl; - for(int i = 0; i < PUBLISHER_NUMBER; i++) - { - // publisher topic name - std::stringstream tname; - tname << "PERSON_" << i; - - // create subscriber - vector_of_publishers.emplace_back(tname.str()); - } - - int i(0); - // idle main thread - while(eCAL::Ok() && i < 100) - { - // sleep 100 ms - SendAll(vector_of_publishers); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - i++; - } - - std::cout << "Moving publishers ..." << std::endl; - PersonSendVector_t moved_vector_of_publishers(std::move(vector_of_publishers)); - - while (eCAL::Ok()) - { - SendAll(moved_vector_of_publishers); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/multiple/multiple_snd/src/multiple_snd_person.h b/samples/cpp/multiple/multiple_snd/src/multiple_snd_person.h deleted file mode 100644 index 0661f3c..0000000 --- a/samples/cpp/multiple/multiple_snd/src/multiple_snd_person.h +++ /dev/null @@ -1,22 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -void MultipleSendPerson(int argc, char **argv); diff --git a/samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.cpp b/samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.cpp deleted file mode 100644 index 1a6fa19..0000000 --- a/samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include -#include -#include -#include - -#define PUBLISHER_NUMBER 10 - -void MultipleSendVector(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "multiple_snd_vector"); - - // create dummy subscriber - std::vector vector_of_publishers; - std::cout << "Creating publishers ..." << std::endl; - for(int i = 0; i < PUBLISHER_NUMBER; i++) - { - // publisher topic name - std::stringstream tname; - tname << "PUB_" << i; - - // create subscriber - vector_of_publishers.emplace_back(tname.str()); - } - - std::string s{ "Hello World" }; - - int i(0); - // idle main thread - while(eCAL::Ok() && i < 100) - { - // sleep 100 ms - for (auto& pub : vector_of_publishers) - { - pub.Send(s); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - i++; - } - - std::cout << "Moving publishers ..." << std::endl; - std::vector moved_vector_of_publishers(std::move(vector_of_publishers)); - - while (eCAL::Ok()) - { - for (auto& pub : moved_vector_of_publishers) - { - pub.Send(s); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.h b/samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.h deleted file mode 100644 index cfb007c..0000000 --- a/samples/cpp/multiple/multiple_snd/src/multiple_snd_vector.h +++ /dev/null @@ -1,22 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -void MultipleSendVector(int argc, char **argv); diff --git a/samples/cpp/orchestration/component1/CMakeLists.txt b/samples/cpp/orchestration/component1/CMakeLists.txt deleted file mode 100644 index 9591857..0000000 --- a/samples/cpp/orchestration/component1/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2022 Eclipse Foundation -# -# 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(component1) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(component1_src - src/component1.cpp -) - -set(component1_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/component.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/orchestrator.proto -) -ecal_add_sample(${PROJECT_NAME} ${component1_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${component1_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/orchestration) diff --git a/samples/cpp/orchestration/component1/src/component1.cpp b/samples/cpp/orchestration/component1/src/component1.cpp deleted file mode 100644 index 26285ea..0000000 --- a/samples/cpp/orchestration/component1/src/component1.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 "orchestrator.pb.h" -#include "component.pb.h" - -#include - -// component service class implementation -class ComponentServiceImpl final : public orchestrator::ComponentService -{ -public: - ComponentServiceImpl() - { - // create 2 publisher for sending messages 'foo' and 'vec' - publisher_foo = eCAL::protobuf::CPublisher("foo"); - publisher_vec = eCAL::protobuf::CPublisher("vec"); - } - - // the component execute method - void execute(google::protobuf::RpcController* /* controller */, const orchestrator::request* request, - orchestrator::response* /*response*/, ::google::protobuf::Closure* /* done */) override - { - std::cout << "Component called with ID: " << request->id() << std::endl; - - // send 'foo' - { - component::foo msg; - msg.set_id(request->id()); - msg.set_hello("HELLO"); - publisher_foo.Send(msg); - std::cout << "Topic 'foo' sent with ID: " << request->id() << std::endl; - } - - // send 'vec' - { - component::vec msg; - msg.set_id(request->id()); - for (uint64_t i = 0U; i < 8192; ++i) msg.add_uvec(i); - publisher_vec.Send(msg); - std::cout << "Topic 'vec' sent with ID: " << request->id() << std::endl; - } - - std::cout << std::endl; - } - -private: - eCAL::protobuf::CPublisher publisher_foo; - eCAL::protobuf::CPublisher publisher_vec; -}; - -int main(int argc, char** argv) -{ - // initialize eCAL API - const std::string component("component1"); - eCAL::Initialize(argc, argv, component.c_str()); - - // start the component service - std::shared_ptr component_service_impl = std::make_shared(); - eCAL::protobuf::CServiceServer component1_service(component_service_impl, component); - - while (eCAL::Ok()) - { - // sleep 100 ms - eCAL::Process::SleepMS(100); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/orchestration/component1/src/protobuf/component.proto b/samples/cpp/orchestration/component1/src/protobuf/component.proto deleted file mode 100644 index 35d37ed..0000000 --- a/samples/cpp/orchestration/component1/src/protobuf/component.proto +++ /dev/null @@ -1,34 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 ================================= -*/ - -syntax = "proto3"; - -package component; - -message foo -{ - uint64 id = 1; - string hello = 2; -} - -message vec -{ - uint64 id = 1; - repeated uint64 uvec = 2; -} diff --git a/samples/cpp/orchestration/component1/src/protobuf/orchestrator.proto b/samples/cpp/orchestration/component1/src/protobuf/orchestrator.proto deleted file mode 100644 index 12a041b..0000000 --- a/samples/cpp/orchestration/component1/src/protobuf/orchestrator.proto +++ /dev/null @@ -1,35 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 ================================= -*/ - -syntax = "proto3"; -option cc_generic_services = true; - -package orchestrator; - -message request -{ - uint64 id = 1; -} - -message response {} - -service ComponentService -{ - rpc execute (request) returns (response); -} diff --git a/samples/cpp/orchestration/component2/CMakeLists.txt b/samples/cpp/orchestration/component2/CMakeLists.txt deleted file mode 100644 index a6e63e2..0000000 --- a/samples/cpp/orchestration/component2/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2022 Eclipse Foundation -# -# 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(component2) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(component2_src - src/component2.cpp -) - -set(component2_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/component.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/orchestrator.proto -) -ecal_add_sample(${PROJECT_NAME} ${component2_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${component2_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/orchestration) diff --git a/samples/cpp/orchestration/component2/src/component2.cpp b/samples/cpp/orchestration/component2/src/component2.cpp deleted file mode 100644 index 90990c4..0000000 --- a/samples/cpp/orchestration/component2/src/component2.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 "orchestrator.pb.h" -#include "component.pb.h" - -#include -#include - -// component service class implementation -class ComponentServiceImpl final : public orchestrator::ComponentService -{ -public: - ComponentServiceImpl() - { - // create subscriber for topic 'foo' - subscriber_foo = eCAL::protobuf::CSubscriber("foo"); - subscriber_foo.AddReceiveCallback(std::bind(&ComponentServiceImpl::on_foo_message, this, std::placeholders::_2)); - - // create subscriber for topic 'vec' - subscriber_vec = eCAL::protobuf::CSubscriber("vec"); - subscriber_vec.AddReceiveCallback(std::bind(&ComponentServiceImpl::on_vec_message, this, std::placeholders::_2)); - } - - // the component execute method - void execute(google::protobuf::RpcController* /*controller*/, const orchestrator::request* request, - orchestrator::response* /*response*/, ::google::protobuf::Closure* /*done*/) override - { - std::lock_guard lock(callback_mtx); - uint64_t id_component = request->id(); - - // component call id and the id's of 'foo' and 'vec' should be equal - bool consistency = (id_component == id_foo) && (id_foo == id_vec); - if (!consistency) - { - std::cout << std::endl << "Component interface inconsistent !" << std::endl << std::endl << std::endl; - eCAL::Process::SleepMS(10000); - err_cnt++; - } - else - { - std::cout << "Component called with ID: " << id_component << std::endl; - if(err_cnt > 0) std::cout << "Error count : " << err_cnt << std::endl; - std::cout << std::endl; - } - } - - // 'foo' message callback - void on_foo_message(const component::foo& msg) - { - std::lock_guard lock(callback_mtx); - id_foo = msg.id(); - std::cout << "Topic 'foo' received with ID: " << id_foo << std::endl; - } - - // 'vec' message callback - void on_vec_message(const component::vec& msg) - { - std::lock_guard lock(callback_mtx); - id_vec = msg.id(); - std::cout << "Topic 'vec' received with ID: " << id_vec << std::endl; - } - -private: - std::mutex callback_mtx; - eCAL::protobuf::CSubscriber subscriber_foo; - eCAL::protobuf::CSubscriber subscriber_vec; - uint64_t id_foo = 0; - uint64_t id_vec = 0; - int err_cnt = 0; -}; - -int main(int argc, char** argv) -{ - // initialize eCAL API - const std::string component("component2"); - eCAL::Initialize(argc, argv, component.c_str()); - - // start the component service - std::shared_ptr component_service_impl = std::make_shared(); - eCAL::protobuf::CServiceServer component1_service(component_service_impl, component); - - while (eCAL::Ok()) - { - // sleep 100 ms - eCAL::Process::SleepMS(100); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/orchestration/component2/src/protobuf/component.proto b/samples/cpp/orchestration/component2/src/protobuf/component.proto deleted file mode 100644 index 35d37ed..0000000 --- a/samples/cpp/orchestration/component2/src/protobuf/component.proto +++ /dev/null @@ -1,34 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 ================================= -*/ - -syntax = "proto3"; - -package component; - -message foo -{ - uint64 id = 1; - string hello = 2; -} - -message vec -{ - uint64 id = 1; - repeated uint64 uvec = 2; -} diff --git a/samples/cpp/orchestration/component2/src/protobuf/orchestrator.proto b/samples/cpp/orchestration/component2/src/protobuf/orchestrator.proto deleted file mode 100644 index 12a041b..0000000 --- a/samples/cpp/orchestration/component2/src/protobuf/orchestrator.proto +++ /dev/null @@ -1,35 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 ================================= -*/ - -syntax = "proto3"; -option cc_generic_services = true; - -package orchestrator; - -message request -{ - uint64 id = 1; -} - -message response {} - -service ComponentService -{ - rpc execute (request) returns (response); -} diff --git a/samples/cpp/orchestration/orchestrator/CMakeLists.txt b/samples/cpp/orchestration/orchestrator/CMakeLists.txt deleted file mode 100644 index 8780f2d..0000000 --- a/samples/cpp/orchestration/orchestrator/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2022 Eclipse Foundation -# -# 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(orchestrator) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(orchestrator_src - src/orchestrator.cpp -) - -set(orchestrator_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/orchestrator.proto -) -ecal_add_sample(${PROJECT_NAME} ${orchestrator_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${orchestrator_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/orchestration) diff --git a/samples/cpp/orchestration/orchestrator/src/orchestrator.cpp b/samples/cpp/orchestration/orchestrator/src/orchestrator.cpp deleted file mode 100644 index 8240e18..0000000 --- a/samples/cpp/orchestration/orchestrator/src/orchestrator.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 "orchestrator.pb.h" - -#include - -int main(int argc, char** argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "orchestrator"); - - eCAL::protobuf::CServiceClient component1("component1"); - eCAL::protobuf::CServiceClient component2("component2"); - - // sleep 2 seconds - eCAL::Process::SleepMS(2000); - - // prepare service request and response vector - orchestrator::request srv_request; - eCAL::ServiceResponseVecT srv_response_vec; - - // call components 1 and 2 - uint64_t cycle = 0; - while (eCAL::Ok()) - { - srv_request.set_id(cycle); - - std::cout << "call component 1" << std::endl; - component1.Call("execute", srv_request, -1, &srv_response_vec); - - std::cout << "call component 2" << std::endl << std::endl; - component2.Call("execute", srv_request, -1, &srv_response_vec); - - ++cycle; - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/orchestration/orchestrator/src/protobuf/orchestrator.proto b/samples/cpp/orchestration/orchestrator/src/protobuf/orchestrator.proto deleted file mode 100644 index 12a041b..0000000 --- a/samples/cpp/orchestration/orchestrator/src/protobuf/orchestrator.proto +++ /dev/null @@ -1,35 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2022 Eclipse Foundation - * - * 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 ================================= -*/ - -syntax = "proto3"; -option cc_generic_services = true; - -package orchestrator; - -message request -{ - uint64 id = 1; -} - -message response {} - -service ComponentService -{ - rpc execute (request) returns (response); -} diff --git a/samples/cpp/performance/dynsize_snd/CMakeLists.txt b/samples/cpp/performance/dynsize_snd/CMakeLists.txt deleted file mode 100644 index e9f2c6d..0000000 --- a/samples/cpp/performance/dynsize_snd/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(dynsize_snd) - -find_package(eCAL REQUIRED) - -set(dynsize_snd_src - src/dynsize_snd.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${dynsize_snd_src}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/performance) diff --git a/samples/cpp/performance/dynsize_snd/src/dynsize_snd.cpp b/samples/cpp/performance/dynsize_snd/src/dynsize_snd.cpp deleted file mode 100644 index 69f9547..0000000 --- a/samples/cpp/performance/dynsize_snd/src/dynsize_snd.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include -#include - -#include - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "dynsize_snd"); - - // publisher for topic "Performance" - eCAL::CPublisher pub("Performance"); - - // prepare snd_buf - const int MAX_BUFSIZE(10*1024*1024); - std::vector snd_buf(MAX_BUFSIZE); - for(int i = 0; i < MAX_BUFSIZE; ++i) - { - switch (i % 4) - { - case 0: - snd_buf[i] = 'A'; - break; - case 1: - snd_buf[i] = 'B'; - break; - case 2: - snd_buf[i] = 'C'; - break; - case 3: - snd_buf[i] = 'D'; - break; - } - } - - // send updates - size_t max_size(MAX_BUFSIZE); - for(int cnt = 1; cnt < MAX_BUFSIZE; cnt += 1 + cnt/10000) - { - // first we increase send size byte wise - // later we keep size constant for a while - size_t send_size(cnt); - if(cnt > 10*1024) - { - send_size = int(cnt/10240) * 10240; - } - if(send_size > max_size) - { - send_size = max_size; - } - - // send content - size_t snd_len = pub.Send(snd_buf.data(), send_size, cnt); - if((snd_len > 0) && (snd_len != send_size)) - { - std::cout << std::endl << "Sending failed !" << std::endl; - } - - // print content - if(cnt%10000 == 0) - { - if(send_size < 6*1024) - { - std::cout << std::endl << "Sent message size: " << send_size << " Byte" << std::endl; - } - else - { - std::cout << std::endl << "Sent message size: " << send_size/1024.0 << " kByte" << std::endl; - } - } - - // sleep 1 s - if(cnt%1000 == 0) eCAL::Process::SleepMS(1000); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/performance/performance_rec/src/performance_rec.cpp b/samples/cpp/performance/performance_rec/src/performance_rec.cpp deleted file mode 100644 index 3be982b..0000000 --- a/samples/cpp/performance/performance_rec/src/performance_rec.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - long long msgs = 0; - long long bytes = 0; - long long slen = 0; - - // initialize eCAL API - eCAL::Initialize(argc, argv, "performance_rec"); - - // create subscriber for topic "Performance" - eCAL::CSubscriber sub("Performance", "base:std::string"); - - // dump instance state if creation failed - if(!sub.IsCreated()) - { - std::cout << "Could not create subscriber !" << std::endl; - return(0); - } - - // safe the start time - auto start_time(std::chrono::steady_clock::now()); - - // receive updates - std::string rcv_buf; - while(eCAL::Ok()) - { - // receive content with infinite timeout - bool success = sub.ReceiveBuffer(rcv_buf, nullptr, -1); - // collect data - if(success) - { - msgs++; - slen = rcv_buf.size(); - bytes += rcv_buf.size(); - } - - // check time and print results every second - std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; - if (diff_time >= std::chrono::seconds(1)) - { - start_time = std::chrono::steady_clock::now(); - std::stringstream out; - out << "Message size (kByte): " << (unsigned int)(slen / 1024) << std::endl; - out << "kByte/s: " << (unsigned int)(bytes / 1024 / diff_time.count()) << std::endl; - out << "MByte/s: " << (unsigned int)(bytes / 1024 / 1024 / diff_time.count()) << std::endl; - out << "Messages/s: " << (unsigned int)(msgs / diff_time.count()) << std::endl; - out << "Latency (us): " << (diff_time.count() / msgs) * 1000 * 1000 << std::endl; - std::cout << out.str() << std::endl; - msgs = 0; - bytes = 0; - } - } - - // destroy subscriber - sub.Destroy(); - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/performance/performance_rec_cb/CMakeLists.txt b/samples/cpp/performance/performance_rec_cb/CMakeLists.txt deleted file mode 100644 index ccbccc9..0000000 --- a/samples/cpp/performance/performance_rec_cb/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(performance_rec_cb) - -find_package(eCAL REQUIRED) - -set(performance_rec_cb_src - src/performance_rec_cb.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${performance_rec_cb_src}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/performance) diff --git a/samples/cpp/performance/performance_snd/src/performance_snd.cpp b/samples/cpp/performance/performance_snd/src/performance_snd.cpp deleted file mode 100644 index 7b2c8ba..0000000 --- a/samples/cpp/performance/performance_snd/src/performance_snd.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include -#include - -// main entry -int main(int argc, char **argv) -{ - size_t payload_size(1024*1024); - if(argc > 1) payload_size = atoi(argv[1]); - if(payload_size < 1) payload_size = 1; - - // initialize eCAL API - eCAL::Initialize(argc, argv, "performance_snd"); - - // new publisher - eCAL::CPublisher pub("Performance"); - - int clock(0); - int msgs (0); - unsigned long long bytes(0); - size_t slen (0); - int trigger(1); - bool zero_copy(true); - int buffer_count(1); - bool handshake(true); - - // default send string - std::string send_s = "Hello World "; - while(send_s.size() < payload_size) - { - send_s += send_s; - } - send_s.resize(payload_size); - - std::cout << "Message size = " << int(send_s.size()) << " Byte = " << int(send_s.size()/1024) << " kByte = " << int(send_s.size()/1024/1024) << " MByte" << std::endl << std::endl; - slen = send_s.size(); - - // set zero copy - if (zero_copy) - { - std::cout << "Switch zero copy mode on" << std::endl << std::endl; - } - pub.ShmEnableZeroCopy(zero_copy); - - // set write buffer count - if (buffer_count > 1) - { - std::cout << "Set number of write buffer to " << buffer_count << std::endl << std::endl; - } - pub.ShmSetBufferCount(buffer_count); - - // set handshake - if (handshake) - { - std::cout << "Switch handshake on" << std::endl << std::endl; - pub.ShmSetAcknowledgeTimeout(std::chrono::milliseconds(1000)); - } - else - { - pub.ShmSetAcknowledgeTimeout(0); - } - - // safe the start time - auto start_time = std::chrono::steady_clock::now(); - - // send updates - while(eCAL::Ok()) - { - eCAL::Logging::StartCoreTimer(); - - // send content - size_t snd_len = pub.Send(send_s); - if((snd_len > 0) && (snd_len != slen)) - { - std::cerr << std::endl << "Send failed !" << " sent : " << slen << " returned : " << snd_len << std::endl; - } - - // collect data - clock++; - msgs++; - bytes += slen; - - // check timer and print results every second - if(clock%10011 == 0) - { - std::chrono::duration diff_time = std::chrono::steady_clock::now() - start_time; - if(diff_time >= std::chrono::seconds(1)) - { - trigger++; - - start_time = std::chrono::steady_clock::now(); - std::stringstream out; - out << "Message size (kByte): " << (unsigned int)(slen / 1024) << std::endl; - out << "kByte/s: " << (unsigned int)(bytes / 1024 / diff_time.count()) << std::endl; - out << "MByte/s: " << (unsigned int)(bytes / 1024 / 1024 / diff_time.count()) << std::endl; - out << "Messages/s: " << (unsigned int)(msgs / diff_time.count()) << std::endl; - out << "Latency (us): " << (diff_time.count() / msgs) * 1000 * 1000 << std::endl; - std::cout << out.str() << std::endl; - msgs = 0; - bytes = 0; - - eCAL::Logging::Log(out.str()); - } - } - - eCAL::Logging::StopCoreTimer(); - } - - // destroy publisher - pub.Destroy(); - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/performance/pubsub_throughput/CMakeLists.txt b/samples/cpp/performance/pubsub_throughput/CMakeLists.txt deleted file mode 100644 index f84f92c..0000000 --- a/samples/cpp/performance/pubsub_throughput/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(pubsub_throughput) - -find_package(eCAL REQUIRED) - -set(pubsub_throughput_src - src/pubsub_throughput.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${pubsub_throughput_src}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/performance) diff --git a/samples/cpp/performance/pubsub_throughput/src/pubsub_throughput.cpp b/samples/cpp/performance/pubsub_throughput/src/pubsub_throughput.cpp deleted file mode 100644 index ff70baa..0000000 --- a/samples/cpp/performance/pubsub_throughput/src/pubsub_throughput.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include -#include -#include -#include - -#define TEST_RELIABLE 0 -#define TEST_BEST_EFFORT 1 - -#define TEST_UDP 0 -#define TEST_SHM 1 -#define TEST_TCP 0 -#define TEST_INPROC 0 - -auto g_snd_size (1000 * 1024); -auto g_pre_loops(100); -auto g_snd_loops( 1000 * 1000); - -// subscriber callback function -std::atomic g_callback_received; -void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) -{ - g_callback_received += data_->size; -} - -static std::string CreatePayLoad(size_t payload_size_) -{ - std::string s = "Hello World "; - while (s.size() < payload_size_) s += s; - s.resize(payload_size_); - return(s); -} - -bool test_throughput(int snd_size, int snd_loops, eCAL::TLayer::eTransportLayer layer, bool reliable) -{ - // internal timings for registration and data flow delay - const int reg_time (2000); - const int data_time(200); - - // create publisher for topic "ThroughPut" - std::shared_ptr pub = std::make_shared(); - // set qos - eCAL::QOS::SWriterQOS wqos; - if (reliable) wqos.reliability = eCAL::QOS::reliable_reliability_qos; - else wqos.reliability = eCAL::QOS::best_effort_reliability_qos; - pub->SetQOS(wqos); - // set transport layer - pub->SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - pub->SetLayerMode(layer, eCAL::TLayer::smode_on); - // create it - pub->Create("ThroughPut"); - - // create subscriber for topic "ThroughPut" - std::shared_ptr sub = std::make_shared(); - // set qos - eCAL::QOS::SReaderQOS rqos; - if (reliable) rqos.reliability = eCAL::QOS::reliable_reliability_qos; - else rqos.reliability = eCAL::QOS::best_effort_reliability_qos; - sub->SetQOS(rqos); - // create it - sub->Create("ThroughPut"); - // add callback - sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2)); - - // let's match them - eCAL::Process::SleepMS(reg_time); - - // default send string - std::string send_s = CreatePayLoad(snd_size); - - // start time - auto start = std::chrono::high_resolution_clock::now(); - - // we send a few dummy loops to establish all connections - for (auto i = 0; i < g_pre_loops; ++i) - { - pub->Send(send_s); - } - - // let's finalize receive - eCAL::Process::SleepMS(data_time); - - // reset received bytes counter - g_callback_received = 0; - - // do some work - for (auto i = 0; i < snd_loops; ++i) - { - pub->Send(send_s); - } - - // end time - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed = finish - start; - std::cout << "Elapsed time : " << elapsed.count() << " s" << std::endl; - - // let's match them - eCAL::Process::SleepMS(data_time); - - size_t sum_snd_bytes = send_s.size() * snd_loops; - size_t sum_rcv_bytes = g_callback_received; - std::cout << "Sent : " << sum_snd_bytes << " bytes"; - std::cout << " (" << sum_snd_bytes / (1024 * 1024) << " MB)" << std::endl; - std::cout << "Received : " << sum_rcv_bytes << " bytes"; - std::cout << " (" << sum_rcv_bytes / (1024 * 1024) << " MB)" << std::endl; - std::cout << "Lost : " << sum_snd_bytes - sum_rcv_bytes << " bytes"; - std::cout << " (" << (sum_snd_bytes - sum_rcv_bytes) / (1024 * 1024) << " MB, "; - std::cout << (sum_snd_bytes - sum_rcv_bytes) * 100.0f / sum_snd_bytes << " %)" << std::endl; - std::cout << "Throughput : " << int((sum_snd_bytes / (1024.0 * 1024.0)) / elapsed.count()) << " MB/s " << std::endl; - - // check receive and send bytes - bool ret_state(false); - if (reliable) ret_state = sum_snd_bytes == sum_rcv_bytes; - else ret_state = (sum_snd_bytes * 3/4) <= sum_rcv_bytes; - - // destroy pub/sub - pub->Destroy(); - sub->Destroy(); - - return(ret_state); -} - -// main entry -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "pubsub_throughput"); - - // publish / subscribe match in the same process - eCAL::Util::EnableLoopback(true); - - bool success(false); - -#if TEST_RELIABLE - -#if TEST_UDP - std::cout << "RELIABLE : UDP" << std::endl; - success = test_throughput(g_snd_size, g_snd_loops, eCAL::TLayer::tlayer_udp_mc, true); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#if TEST_SHM - std::cout << "RELIABLE : SHM" << std::endl; - success = test_throughput(g_snd_size, g_snd_loops, eCAL::TLayer::tlayer_shm, true); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#if TEST_TCP - std::cout << "RELIABLE : TCP" << std::endl; - success = test_throughput(4000, g_snd_loops, eCAL::TLayer::tlayer_tcp, true); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#if TEST_INPROC - std::cout << "RELIABLE : INPROC" << std::endl; - success = test_throughput(4000, g_snd_loops, eCAL::TLayer::tlayer_inproc, true); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#endif // TEST_RELIABLE - -#if TEST_BEST_EFFORT - -#if TEST_UDP - std::cout << "BEST EFFORT : UDP" << std::endl; - success = test_throughput(g_snd_size, g_snd_loops, eCAL::TLayer::tlayer_udp_mc, false); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#if TEST_SHM - std::cout << "BEST EFFORT : SHM" << std::endl; - success = test_throughput(g_snd_size, g_snd_loops, eCAL::TLayer::tlayer_shm, false); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#if TEST_TCP - std::cout << "BEST EFFORT : TCP" << std::endl; - success = test_throughput(g_snd_size, g_snd_loops, eCAL::TLayer::tlayer_tcp, false); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#if TEST_INPROC - std::cout << "BEST EFFORT : INPROC" << std::endl; - success = test_throughput(4000, g_snd_loops, eCAL::TLayer::tlayer_inproc, false); - std::cout << "RESULT : " << success << std::endl << std::endl; -#endif - -#endif // TEST_BEST_EFFORT - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/person/person_rec_lambda_in_class/CMakeLists.txt b/samples/cpp/person/person_rec_lambda_in_class/CMakeLists.txt deleted file mode 100644 index 67c2bf4..0000000 --- a/samples/cpp/person/person_rec_lambda_in_class/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(person_rec_lambda_in_class) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(person_rec_lambda_in_class_src - src/addressbook.cpp -) - -include_directories(include) - -set(person_rec_lambda_in_class_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) -ecal_add_sample(${PROJECT_NAME} ${person_rec_lambda_in_class_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} src/protobuf ${person_rec_lambda_in_class_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/person) diff --git a/samples/cpp/person/person_rec_lambda_in_class/include/addressbook.h b/samples/cpp/person/person_rec_lambda_in_class/include/addressbook.h deleted file mode 100644 index 491411d..0000000 --- a/samples/cpp/person/person_rec_lambda_in_class/include/addressbook.h +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#ifndef ECAL_SAMPLES_CPP_CLASS_CALLBACK_PERSON_REC_INCLUDE_ADDRESSBOOK_H_ -#define ECAL_SAMPLES_CPP_CLASS_CALLBACK_PERSON_REC_INCLUDE_ADDRESSBOOK_H_ - -#include -#include - -#include -#include - -#include "person.pb.h" - -class Addressbook { - public: - Addressbook(); - void spin(); - void print(); - - private: - std::vector book_; - - eCAL::protobuf::CSubscriber subscriber_; - - void callback(const pb::People::Person& person_); - -}; - -#endif //ECAL_SAMPLES_CPP_CLASS_CALLBACK_PERSON_REC_INCLUDE_ADDRESSBOOK_H_ diff --git a/samples/cpp/person/person_rec_lambda_in_class/src/addressbook.cpp b/samples/cpp/person/person_rec_lambda_in_class/src/addressbook.cpp deleted file mode 100644 index bd36cd5..0000000 --- a/samples/cpp/person/person_rec_lambda_in_class/src/addressbook.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include "addressbook.h" - -Addressbook::Addressbook() { - // create a subscriber (topic name "person") - subscriber_ = eCAL::protobuf::CSubscriber("person"); - - // add receiver callback - auto lambda = [this](const char* /*topic_*/, const pb::People::Person& msg, long long /*time_*/, long long /*clock_*/, long long /*id_*/) { return this->callback(msg); }; - if (!subscriber_.AddReceiveCallback(lambda)) { - std::cout << "eCAL subscriber initialization failed\n"; - } - - book_ = std::vector(); -} - -void Addressbook::spin() { - while(eCAL::Ok() && book_.size() < 5) - { - // sleep 100 ms - eCAL::Process::SleepMS(100); - } -} - -void Addressbook::callback(const pb::People::Person &person_) -{ - std::cout << "Received message\n"; - book_.push_back(person_); - -} - -void Addressbook::print() { - std::cout << "Printing results\n"; - - for(auto person: this->book_) { - std::cout << "------------------------------------------" << std::endl; - std::cout << " CONTENT " << std::endl; - std::cout << "------------------------------------------" << std::endl; - std::cout << "person id : " << person.id() << std::endl; - std::cout << "person name : " << person.name() << std::endl; - std::cout << "person stype : " << person.stype() << std::endl; - std::cout << "person email : " << person.email() << std::endl; - std::cout << "dog.name : " << person.dog().name() << std::endl; - std::cout << "house.rooms : " << person.house().rooms() << std::endl; - std::cout << "------------------------------------------" << std::endl; - std::cout << std::endl; - } -} - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv); - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - std::cout << "Initializing Addressbook class\n"; - - // Initialize class - Addressbook addressbook = Addressbook(); - - std::cout << "Waiting for 5 messages\n"; - addressbook.spin(); - addressbook.print(); - - // cleanup - eCAL::Finalize(); - - return(0); -} - diff --git a/samples/cpp/person/person_snd_dyn/src/person_snd_dyn.cpp b/samples/cpp/person/person_snd_dyn/src/person_snd_dyn.cpp deleted file mode 100644 index 3604a8d..0000000 --- a/samples/cpp/person/person_snd_dyn/src/person_snd_dyn.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include - -#include "person.pb.h" - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "person publisher dynamic"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a dynamic publisher (topic name "person") - eCAL::protobuf::CDynamicPublisher pub1("person", std::make_shared()); - std::shared_ptr person1 = pub1.GetAs(); - - // create a dynamic publisher (topic name "person") - eCAL::protobuf::CDynamicPublisher pub2("person", "pb.People.Person"); - std::shared_ptr person2 = pub2.GetAs(); - - // set person1 object content - person1->set_name("Max"); - person1->set_stype(pb::People::Person_SType_MALE); - person1->set_email("max@mail.net"); - person1->mutable_dog()->set_name("Brandy"); - person1->mutable_house()->set_rooms(4); - - // set person2 object content - person2->set_name("Romy"); - person2->set_stype(pb::People::Person_SType_FEMALE); - person2->set_email("romy@mail.net"); - person2->mutable_dog()->set_name("Gorky"); - person2->mutable_house()->set_rooms(4); - - // enter main loop - auto cnt = 0; - std::shared_ptr person; - - while(eCAL::Ok()) - { - if (++cnt % 2) - { - // modify and send the person1 object - person1->set_id(cnt); - pub1.Send(); - - // for later printing - person = person1; - } - else - { - // modify and send the person2 object - person2->set_id(cnt); - pub2.Send(); - - // for later printing - person = person2; - } - - // print current person message - std::cout << "person id : " << person->id() << std::endl; - std::cout << "person name : " << person->name() << std::endl; - std::cout << "person stype : " << person->stype() << std::endl; - std::cout << "person email : " << person->email() << std::endl; - std::cout << "dog.name : " << person->dog().name() << std::endl; - std::cout << "house.rooms : " << person->house().rooms() << std::endl; - std::cout << std::endl; - - // sleep 500 ms - eCAL::Process::SleepMS(500); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/person/person_snd_dyn/src/protobuf/person.proto b/samples/cpp/person/person_snd_dyn/src/protobuf/person.proto deleted file mode 100644 index 4200a43..0000000 --- a/samples/cpp/person/person_snd_dyn/src/protobuf/person.proto +++ /dev/null @@ -1,42 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -import "animal.proto"; -import "house.proto"; - -package pb.People; - -message Person -{ - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - - Animal.Dog dog = 5; - Environment.House house = 6; -} diff --git a/samples/cpp/person/person_snd_events/CMakeLists.txt b/samples/cpp/person/person_snd_events/CMakeLists.txt deleted file mode 100644 index 81e7339..0000000 --- a/samples/cpp/person/person_snd_events/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(person_snd_events) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(person_snd_events_src - src/person_snd_events.cpp -) - -set(person_snd_events_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) -ecal_add_sample(${PROJECT_NAME} ${person_snd_events_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_snd_events_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/person) diff --git a/samples/cpp/person/person_snd_events/src/protobuf/animal.proto b/samples/cpp/person/person_snd_events/src/protobuf/animal.proto deleted file mode 100644 index 625a78d..0000000 --- a/samples/cpp/person/person_snd_events/src/protobuf/animal.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Animal; - -message Dog -{ - string name = 1; - string colour = 2; -} diff --git a/samples/cpp/person/person_snd_events/src/protobuf/house.proto b/samples/cpp/person/person_snd_events/src/protobuf/house.proto deleted file mode 100644 index fbe8cce..0000000 --- a/samples/cpp/person/person_snd_events/src/protobuf/house.proto +++ /dev/null @@ -1,27 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Environment; - -message House -{ - int32 rooms = 1; -} diff --git a/samples/cpp/person/person_snd_inproc/CMakeLists.txt b/samples/cpp/person/person_snd_inproc/CMakeLists.txt deleted file mode 100644 index fc73548..0000000 --- a/samples/cpp/person/person_snd_inproc/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(person_snd_inproc) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(person_snd_inproc_src - src/person_snd_inproc.cpp -) - -set(person_snd_inproc_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) -ecal_add_sample(${PROJECT_NAME} ${person_snd_inproc_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_snd_inproc_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/person) diff --git a/samples/cpp/person/person_snd_inproc/src/person_snd_inproc.cpp b/samples/cpp/person/person_snd_inproc/src/person_snd_inproc.cpp deleted file mode 100644 index 1e32b76..0000000 --- a/samples/cpp/person/person_snd_inproc/src/person_snd_inproc.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include -#include - -#include - -#include "person.pb.h" - -void OnPerson(const char* topic_name_, const pb::People::Person& person_, const long long time_, const long long clock_) -{ - std::cout << "------------------------------------------" << std::endl; - std::cout << " HEAD " << std::endl; - std::cout << "------------------------------------------" << std::endl; - std::cout << "topic name : " << topic_name_ << std::endl; - std::cout << "topic time : " << time_ << std::endl; - std::cout << "topic clock : " << clock_ << std::endl; - std::cout << "------------------------------------------" << std::endl; - std::cout << " CONTENT " << std::endl; - std::cout << "------------------------------------------" << std::endl; - std::cout << "person id : " << person_.id() << std::endl; - std::cout << "person name : " << person_.name() << std::endl; - std::cout << "person stype : " << person_.stype() << std::endl; - std::cout << "person email : " << person_.email() << std::endl; - std::cout << "dog.name : " << person_.dog().name() << std::endl; - std::cout << "house.rooms : " << person_.house().rooms() << std::endl; - std::cout << "------------------------------------------" << std::endl; - std::cout << std::endl; -} - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "person publisher inproc"); - - // enable loopback - eCAL::Util::EnableLoopback(true); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a publisher (topic name "person") - eCAL::protobuf::CPublisher pub("person"); - - // switch all layer off - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - - // set inproc write mode to auto - // and shm write mode too, so we can monitor the content from - // outside if needed - pub.SetLayerMode(eCAL::TLayer::tlayer_inproc, eCAL::TLayer::smode_auto); - pub.SetLayerMode(eCAL::TLayer::tlayer_shm, eCAL::TLayer::smode_auto); - - // create a subscriber (topic name "person") - eCAL::protobuf::CSubscriber sub("person"); - - // add receive callback function (_1 = topic_name, _2 = msg, _3 = time, _4 = clock) - auto callback = std::bind(OnPerson, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - sub.AddReceiveCallback(callback); - - // generate a class instance of Person - pb::People::Person person; - - // enter main loop - auto cnt(0); - while(eCAL::Ok()) - { - // set person object content - person.set_id(++cnt); - person.set_name("Max"); - person.set_stype(pb::People::Person_SType_MALE); - person.set_email("max@mail.net"); - person.mutable_dog()->set_name("Brandy"); - person.mutable_house()->set_rooms(4); - - // send the person object - pub.SetID(cnt%42); - pub.Send(person); - - // sleep 500 ms - eCAL::Process::SleepMS(500); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/person/person_snd_inproc/src/protobuf/animal.proto b/samples/cpp/person/person_snd_inproc/src/protobuf/animal.proto deleted file mode 100644 index 625a78d..0000000 --- a/samples/cpp/person/person_snd_inproc/src/protobuf/animal.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Animal; - -message Dog -{ - string name = 1; - string colour = 2; -} diff --git a/samples/cpp/person/person_snd_inproc/src/protobuf/house.proto b/samples/cpp/person/person_snd_inproc/src/protobuf/house.proto deleted file mode 100644 index fbe8cce..0000000 --- a/samples/cpp/person/person_snd_inproc/src/protobuf/house.proto +++ /dev/null @@ -1,27 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Environment; - -message House -{ - int32 rooms = 1; -} diff --git a/samples/cpp/person/person_snd_multicast/CMakeLists.txt b/samples/cpp/person/person_snd_multicast/CMakeLists.txt deleted file mode 100644 index 6a1f0d8..0000000 --- a/samples/cpp/person/person_snd_multicast/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(person_snd_multicast) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(person_snd_multicast_src - src/person_snd_multicast.cpp -) - -set(person_snd_multicast_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) - -ecal_add_sample(${PROJECT_NAME} ${person_snd_multicast_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_snd_multicast_proto}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/person) diff --git a/samples/cpp/person/person_snd_multicast/src/protobuf/animal.proto b/samples/cpp/person/person_snd_multicast/src/protobuf/animal.proto deleted file mode 100644 index 625a78d..0000000 --- a/samples/cpp/person/person_snd_multicast/src/protobuf/animal.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Animal; - -message Dog -{ - string name = 1; - string colour = 2; -} diff --git a/samples/cpp/person/person_snd_multicast/src/protobuf/house.proto b/samples/cpp/person/person_snd_multicast/src/protobuf/house.proto deleted file mode 100644 index fbe8cce..0000000 --- a/samples/cpp/person/person_snd_multicast/src/protobuf/house.proto +++ /dev/null @@ -1,27 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Environment; - -message House -{ - int32 rooms = 1; -} diff --git a/samples/cpp/person/person_snd_multicast/src/protobuf/person.proto b/samples/cpp/person/person_snd_multicast/src/protobuf/person.proto deleted file mode 100644 index 4200a43..0000000 --- a/samples/cpp/person/person_snd_multicast/src/protobuf/person.proto +++ /dev/null @@ -1,42 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -import "animal.proto"; -import "house.proto"; - -package pb.People; - -message Person -{ - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - - Animal.Dog dog = 5; - Environment.House house = 6; -} diff --git a/samples/cpp/person/person_snd_tcp/src/person_snd_tcp.cpp b/samples/cpp/person/person_snd_tcp/src/person_snd_tcp.cpp deleted file mode 100644 index 1f0f03b..0000000 --- a/samples/cpp/person/person_snd_tcp/src/person_snd_tcp.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include - -#include "person.pb.h" - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "person publisher tcp"); - - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - // create a publisher (topic name "person") - eCAL::protobuf::CPublisher pub("person"); - - // switch all layer off - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - - // switch unicast layer on - pub.SetLayerMode(eCAL::TLayer::tlayer_tcp, eCAL::TLayer::smode_on); - - // generate a class instance of Person - pb::People::Person person; - - // enter main loop - auto cnt(0); - while (eCAL::Ok()) - { - // set person object content - person.set_id(++cnt); - person.set_name("Max"); - person.set_stype(pb::People::Person_SType_MALE); - person.set_email("max@mail.net"); - person.mutable_dog()->set_name("Brandy"); - person.mutable_house()->set_rooms(4); - - // send the person object - pub.Send(person); - - // print content - std::cout << "person id : " << person.id() << std::endl; - std::cout << "person name : " << person.name() << std::endl; - std::cout << "person stype : " << person.stype() << std::endl; - std::cout << "person email : " << person.email() << std::endl; - std::cout << "dog.name : " << person.dog().name() << std::endl; - std::cout << "house.rooms : " << person.house().rooms() << std::endl; - std::cout << std::endl; - - // sleep 500 ms - eCAL::Process::SleepMS(500); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/person/person_snd_tcp/src/protobuf/animal.proto b/samples/cpp/person/person_snd_tcp/src/protobuf/animal.proto deleted file mode 100644 index 625a78d..0000000 --- a/samples/cpp/person/person_snd_tcp/src/protobuf/animal.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Animal; - -message Dog -{ - string name = 1; - string colour = 2; -} diff --git a/samples/cpp/person/person_snd_tcp/src/protobuf/house.proto b/samples/cpp/person/person_snd_tcp/src/protobuf/house.proto deleted file mode 100644 index fbe8cce..0000000 --- a/samples/cpp/person/person_snd_tcp/src/protobuf/house.proto +++ /dev/null @@ -1,27 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Environment; - -message House -{ - int32 rooms = 1; -} diff --git a/samples/cpp/person/person_snd_tcp/src/protobuf/person.proto b/samples/cpp/person/person_snd_tcp/src/protobuf/person.proto deleted file mode 100644 index 4200a43..0000000 --- a/samples/cpp/person/person_snd_tcp/src/protobuf/person.proto +++ /dev/null @@ -1,42 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -import "animal.proto"; -import "house.proto"; - -package pb.People; - -message Person -{ - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - - Animal.Dog dog = 5; - Environment.House house = 6; -} diff --git a/samples/cpp/event/event_rec/CMakeLists.txt b/samples/cpp/pubsub/binary/binary_rec/CMakeLists.txt similarity index 89% rename from samples/cpp/event/event_rec/CMakeLists.txt rename to samples/cpp/pubsub/binary/binary_rec/CMakeLists.txt index 725bf83..8170925 100644 --- a/samples/cpp/event/event_rec/CMakeLists.txt +++ b/samples/cpp/pubsub/binary/binary_rec/CMakeLists.txt @@ -20,15 +20,15 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(event_rec) +project(binary_rec) find_package(eCAL REQUIRED) -set(event_rec_src - src/event_rec.cpp +set(binary_rec_src + src/binary_rec.cpp ) -ecal_add_sample(${PROJECT_NAME} ${event_rec_src}) +ecal_add_sample(${PROJECT_NAME} ${binary_rec_src}) target_link_libraries(${PROJECT_NAME} eCAL::core) @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/event) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/binary) diff --git a/samples/cpp/binary/binary_rec_cb/src/binary_rec_cb.cpp b/samples/cpp/pubsub/binary/binary_rec/src/binary_rec.cpp similarity index 97% rename from samples/cpp/binary/binary_rec_cb/src/binary_rec_cb.cpp rename to samples/cpp/pubsub/binary/binary_rec/src/binary_rec.cpp index 8db5825..f5653c1 100644 --- a/samples/cpp/binary/binary_rec_cb/src/binary_rec_cb.cpp +++ b/samples/cpp/pubsub/binary/binary_rec/src/binary_rec.cpp @@ -43,7 +43,7 @@ void OnReceive(const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackD int main(int argc, char** argv) { // initialize eCAL API - eCAL::Initialize(argc, argv, "binary_rec_cb"); + eCAL::Initialize(argc, argv, "binary_rec"); // subscriber for topic "blob" eCAL::CSubscriber sub("blob"); diff --git a/samples/cpp/binary/binary_snd/CMakeLists.txt b/samples/cpp/pubsub/binary/binary_snd/CMakeLists.txt similarity index 98% rename from samples/cpp/binary/binary_snd/CMakeLists.txt rename to samples/cpp/pubsub/binary/binary_snd/CMakeLists.txt index cbc2ad4..e559a1b 100644 --- a/samples/cpp/binary/binary_snd/CMakeLists.txt +++ b/samples/cpp/pubsub/binary/binary_snd/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/binary) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/binary) diff --git a/samples/cpp/binary/binary_snd/src/binary_snd.cpp b/samples/cpp/pubsub/binary/binary_snd/src/binary_snd.cpp similarity index 100% rename from samples/cpp/binary/binary_snd/src/binary_snd.cpp rename to samples/cpp/pubsub/binary/binary_snd/src/binary_snd.cpp diff --git a/samples/cpp/pingpong/ping/CMakeLists.txt b/samples/cpp/pubsub/binary/ping/CMakeLists.txt similarity index 98% rename from samples/cpp/pingpong/ping/CMakeLists.txt rename to samples/cpp/pubsub/binary/ping/CMakeLists.txt index 62728d0..9147869 100644 --- a/samples/cpp/pingpong/ping/CMakeLists.txt +++ b/samples/cpp/pubsub/binary/ping/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pingpong) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/binary) diff --git a/samples/cpp/pingpong/ping/src/ping.cpp b/samples/cpp/pubsub/binary/ping/src/ping.cpp similarity index 92% rename from samples/cpp/pingpong/ping/src/ping.cpp rename to samples/cpp/pubsub/binary/ping/src/ping.cpp index 6595495..90a931d 100644 --- a/samples/cpp/pingpong/ping/src/ping.cpp +++ b/samples/cpp/pubsub/binary/ping/src/ping.cpp @@ -29,8 +29,9 @@ int main(int argc, char **argv) // initialize eCAL API eCAL::Initialize(argc, argv, "ping"); - eCAL::CPublisher pub_pulse("pulse_send", "long long"); - eCAL::CSubscriber sub_pulse("pulse_reply", "long long"); + eCAL::SDataTypeInformation topic_info{ "long long", "", "" }; + eCAL::CPublisher pub_pulse("pulse_send", topic_info); + eCAL::CSubscriber sub_pulse("pulse_reply", topic_info); std::cout << "-------------------------------" << std::endl; std::cout << " PING PONG TEST " << std::endl; diff --git a/samples/cpp/pingpong/pong/CMakeLists.txt b/samples/cpp/pubsub/binary/pong/CMakeLists.txt similarity index 98% rename from samples/cpp/pingpong/pong/CMakeLists.txt rename to samples/cpp/pubsub/binary/pong/CMakeLists.txt index 9fca542..f76609e 100644 --- a/samples/cpp/pingpong/pong/CMakeLists.txt +++ b/samples/cpp/pubsub/binary/pong/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pingpong) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/binary) diff --git a/samples/cpp/pingpong/pong/src/pong.cpp b/samples/cpp/pubsub/binary/pong/src/pong.cpp similarity index 91% rename from samples/cpp/pingpong/pong/src/pong.cpp rename to samples/cpp/pubsub/binary/pong/src/pong.cpp index 472336b..3150396 100644 --- a/samples/cpp/pingpong/pong/src/pong.cpp +++ b/samples/cpp/pubsub/binary/pong/src/pong.cpp @@ -26,8 +26,9 @@ int main(int argc, char **argv) // initialize eCAL API eCAL::Initialize(argc, argv, "pong"); - eCAL::CSubscriber sub_pulse("pulse_send", "long long"); - eCAL::CPublisher pub_pulse("pulse_reply", "long long"); + eCAL::SDataTypeInformation topic_info{ "long long", "", "" }; + eCAL::CSubscriber sub_pulse("pulse_send", topic_info); + eCAL::CPublisher pub_pulse("pulse_reply", topic_info); std::cout << "-------------------------------" << std::endl; std::cout << " PING PONG TEST " << std::endl; diff --git a/samples/cpp/person/person_rec_events/CMakeLists.txt b/samples/cpp/pubsub/protobuf/person_events_rec/CMakeLists.txt similarity index 85% rename from samples/cpp/person/person_rec_events/CMakeLists.txt rename to samples/cpp/pubsub/protobuf/person_events_rec/CMakeLists.txt index 597e7e1..226946d 100644 --- a/samples/cpp/person/person_rec_events/CMakeLists.txt +++ b/samples/cpp/pubsub/protobuf/person_events_rec/CMakeLists.txt @@ -20,22 +20,22 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(person_rec_events) +project(person_events_rec) find_package(eCAL REQUIRED) find_package(Protobuf REQUIRED) -set(person_rec_events_src - src/person_rec_events.cpp +set(person_events_rec_src + src/person_events_rec.cpp ) -set(person_rec_events_proto +set(person_events_rec_proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto ) -ecal_add_sample(${PROJECT_NAME} ${person_rec_events_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_rec_events_proto}) +ecal_add_sample(${PROJECT_NAME} ${person_events_rec_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_events_rec_proto}) target_link_libraries(${PROJECT_NAME} eCAL::core @@ -46,4 +46,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/person) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/protobuf) diff --git a/samples/cpp/person/person_rec_events/src/person_rec_events.cpp b/samples/cpp/pubsub/protobuf/person_events_rec/src/person_events_rec.cpp similarity index 71% rename from samples/cpp/person/person_rec_events/src/person_rec_events.cpp rename to samples/cpp/pubsub/protobuf/person_events_rec/src/person_events_rec.cpp index 6e5dbc1..3d705a1 100644 --- a/samples/cpp/person/person_rec_events/src/person_rec_events.cpp +++ b/samples/cpp/pubsub/protobuf/person_events_rec/src/person_events_rec.cpp @@ -26,33 +26,31 @@ void OnEvent(const char* topic_name_, const struct eCAL::SSubEventCallbackData* data_) { - std::cout << "topic name : " << topic_name_ << std::endl; + std::cout << "topic name : " << topic_name_ << std::endl; switch (data_->type) { case sub_event_connected: - std::cout << "event : " << "sub_event_connected" << std::endl; + std::cout << "event : " << "sub_event_connected" << std::endl; break; case sub_event_disconnected: - std::cout << "event : " << "sub_event_disconnected" << std::endl; + std::cout << "event : " << "sub_event_disconnected" << std::endl; break; case sub_event_dropped: - std::cout << "event : " << "sub_event_dropped (" << data_->clock << " messages)" << std::endl; - break; - case sub_event_timeout: - std::cout << "event : " << "sub_event_timeout" << std::endl; + std::cout << "event : " << "sub_event_dropped (" << data_->clock << " messages)" << std::endl; break; // not implemented yet case sub_event_corrupted: - std::cout << "event : " << "sub_event_corrupted" << std::endl; + std::cout << "event : " << "sub_event_corrupted" << std::endl; break; case sub_event_update_connection: - std::cout << "event : " << "sub_event_update_connection" << std::endl; - std::cout << " topic_id : " << data_->tid << std::endl; - std::cout << " topic_type : " << data_->ttype << std::endl; + std::cout << "event : " << "sub_event_update_connection" << std::endl; + std::cout << " topic_id : " << data_->tid << std::endl; + std::cout << " topic_encoding : " << data_->tdatatype.encoding << std::endl; + std::cout << " topic_type : " << data_->tdatatype.name << std::endl; //std::cout << " topic_desc : " << data_->tdesc << std::endl; break; default: - std::cout << "event : " << "unknown" << std::endl; + std::cout << "event : " << "unknown" << std::endl; break; } std::cout << std::endl; @@ -69,29 +67,30 @@ int main(int argc, char **argv) // create a subscriber (topic name "person") eCAL::protobuf::CSubscriber sub("person"); - // set receive timeout in ms - sub.SetTimeout(1000); - // add event callback function (_1 = topic_name, _2 = event data struct) auto evt_callback = std::bind(OnEvent, std::placeholders::_1, std::placeholders::_2); sub.AddEventCallback(sub_event_connected, evt_callback); sub.AddEventCallback(sub_event_disconnected, evt_callback); sub.AddEventCallback(sub_event_dropped, evt_callback); - sub.AddEventCallback(sub_event_timeout, evt_callback); sub.AddEventCallback(sub_event_corrupted, evt_callback); sub.AddEventCallback(sub_event_update_connection, evt_callback); // start application and wait for events std::cout << "Please start 'person_snd_events sample." << std::endl << std::endl; - while(eCAL::Ok()) + // run for 10 seconds + auto cnt(0); + while (eCAL::Ok() && (++cnt <= 10)) { // sleep 100 ms - eCAL::Process::SleepMS(100); + eCAL::Process::SleepMS(1000); } // finalize eCAL API eCAL::Finalize(); + // keep alive for 10 seconds + eCAL::Process::SleepMS(10000); + return(0); } diff --git a/samples/cpp/multiple/multiple_rec_cb/src/protobuf/animal.proto b/samples/cpp/pubsub/protobuf/person_events_rec/src/protobuf/animal.proto similarity index 100% rename from samples/cpp/multiple/multiple_rec_cb/src/protobuf/animal.proto rename to samples/cpp/pubsub/protobuf/person_events_rec/src/protobuf/animal.proto diff --git a/samples/cpp/multiple/multiple_rec_cb/src/protobuf/house.proto b/samples/cpp/pubsub/protobuf/person_events_rec/src/protobuf/house.proto similarity index 100% rename from samples/cpp/multiple/multiple_rec_cb/src/protobuf/house.proto rename to samples/cpp/pubsub/protobuf/person_events_rec/src/protobuf/house.proto diff --git a/samples/cpp/multiple/multiple_rec_cb/src/protobuf/person.proto b/samples/cpp/pubsub/protobuf/person_events_rec/src/protobuf/person.proto similarity index 100% rename from samples/cpp/multiple/multiple_rec_cb/src/protobuf/person.proto rename to samples/cpp/pubsub/protobuf/person_events_rec/src/protobuf/person.proto diff --git a/samples/cpp/person/person_snd_tcp/CMakeLists.txt b/samples/cpp/pubsub/protobuf/person_events_snd/CMakeLists.txt similarity index 85% rename from samples/cpp/person/person_snd_tcp/CMakeLists.txt rename to samples/cpp/pubsub/protobuf/person_events_snd/CMakeLists.txt index e7c81ab..5809957 100644 --- a/samples/cpp/person/person_snd_tcp/CMakeLists.txt +++ b/samples/cpp/pubsub/protobuf/person_events_snd/CMakeLists.txt @@ -20,23 +20,22 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(person_snd_tcp) +project(person_events_snd) find_package(eCAL REQUIRED) find_package(Protobuf REQUIRED) -set(person_snd_tcp_src - src/person_snd_tcp.cpp +set(person_events_snd_src + src/person_events_snd.cpp ) -set(person_snd_tcp_proto +set(person_events_snd_proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto ) - -ecal_add_sample(${PROJECT_NAME} ${person_snd_tcp_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_snd_tcp_proto}) +ecal_add_sample(${PROJECT_NAME} ${person_events_snd_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_events_snd_proto}) target_link_libraries(${PROJECT_NAME} eCAL::core @@ -47,4 +46,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/person) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/protobuf) diff --git a/samples/cpp/person/person_snd_events/src/person_snd_events.cpp b/samples/cpp/pubsub/protobuf/person_events_snd/src/person_events_snd.cpp similarity index 77% rename from samples/cpp/person/person_snd_events/src/person_snd_events.cpp rename to samples/cpp/pubsub/protobuf/person_events_snd/src/person_events_snd.cpp index dcae5ae..80ccb3c 100644 --- a/samples/cpp/person/person_snd_events/src/person_snd_events.cpp +++ b/samples/cpp/pubsub/protobuf/person_events_snd/src/person_events_snd.cpp @@ -26,23 +26,24 @@ void OnEvent(const char* topic_name_, const struct eCAL::SPubEventCallbackData* data_) { - std::cout << "topic name : " << topic_name_ << std::endl; + std::cout << "topic name : " << topic_name_ << std::endl; switch (data_->type) { case pub_event_connected: - std::cout << "event : " << "pub_event_connected" << std::endl; + std::cout << "event : " << "pub_event_connected" << std::endl; break; case pub_event_disconnected: - std::cout << "event : " << "pub_event_disconnected" << std::endl; + std::cout << "event : " << "pub_event_disconnected" << std::endl; break; // not implemented yet case pub_event_dropped: - std::cout << "event : " << "pub_event_dropped" << std::endl; + std::cout << "event : " << "pub_event_dropped" << std::endl; break; case pub_event_update_connection: - std::cout << "event : " << "pub_event_update_connection" << std::endl; - std::cout << " topic_id : " << data_->tid << std::endl; - std::cout << " topic_type : " << data_->ttype << std::endl; + std::cout << "event : " << "pub_event_update_connection" << std::endl; + std::cout << " topic_id : " << data_->tid << std::endl; + std::cout << " topic_encoding : " << data_->tdatatype.encoding << std::endl; + std::cout << " topic_type : " << data_->tdatatype.name << std::endl; //std::cout << " topic_desc : " << data_->tdesc << std::endl; break; default: @@ -77,10 +78,10 @@ int main(int argc, char **argv) // enter main loop auto cnt(0); - while (eCAL::Ok()) + while (eCAL::Ok() && (++cnt <= 10)) { // set person object content - person.set_id(++cnt); + person.set_id(cnt); person.set_name("Max"); person.set_stype(pb::People::Person_SType_MALE); person.set_email("max@mail.net"); @@ -91,11 +92,14 @@ int main(int argc, char **argv) pub.Send(person); // sleep 500 ms - eCAL::Process::SleepMS(500); + eCAL::Process::SleepMS(1000); } // finalize eCAL API eCAL::Finalize(); + // keep alive for 10 seconds + eCAL::Process::SleepMS(10000); + return(0); } diff --git a/samples/cpp/multiple/multiple_snd/src/protobuf/animal.proto b/samples/cpp/pubsub/protobuf/person_events_snd/src/protobuf/animal.proto similarity index 100% rename from samples/cpp/multiple/multiple_snd/src/protobuf/animal.proto rename to samples/cpp/pubsub/protobuf/person_events_snd/src/protobuf/animal.proto diff --git a/samples/cpp/multiple/multiple_snd/src/protobuf/house.proto b/samples/cpp/pubsub/protobuf/person_events_snd/src/protobuf/house.proto similarity index 100% rename from samples/cpp/multiple/multiple_snd/src/protobuf/house.proto rename to samples/cpp/pubsub/protobuf/person_events_snd/src/protobuf/house.proto diff --git a/samples/cpp/multiple/multiple_snd/src/protobuf/person.proto b/samples/cpp/pubsub/protobuf/person_events_snd/src/protobuf/person.proto similarity index 100% rename from samples/cpp/multiple/multiple_snd/src/protobuf/person.proto rename to samples/cpp/pubsub/protobuf/person_events_snd/src/protobuf/person.proto diff --git a/samples/cpp/person/person_snd_dyn/CMakeLists.txt b/samples/cpp/pubsub/protobuf/person_loopback/CMakeLists.txt similarity index 86% rename from samples/cpp/person/person_snd_dyn/CMakeLists.txt rename to samples/cpp/pubsub/protobuf/person_loopback/CMakeLists.txt index 21517a9..e56cb98 100644 --- a/samples/cpp/person/person_snd_dyn/CMakeLists.txt +++ b/samples/cpp/pubsub/protobuf/person_loopback/CMakeLists.txt @@ -20,22 +20,22 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(person_snd_dyn) +project(person_loopback) find_package(eCAL REQUIRED) find_package(Protobuf REQUIRED) -set(person_snd_dyn_src - src/person_snd_dyn.cpp +set(person_loopback_src + src/person_loopback.cpp ) -set(person_snd_dyn_proto +set(person_loopback_proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto ) -ecal_add_sample(${PROJECT_NAME} ${person_snd_dyn_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_snd_dyn_proto}) +ecal_add_sample(${PROJECT_NAME} ${person_loopback_src}) +PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${person_loopback_proto}) target_link_libraries(${PROJECT_NAME} eCAL::core @@ -46,4 +46,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/person) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/protobuf) diff --git a/samples/cpp/person/person_snd_multicast/src/person_snd_multicast.cpp b/samples/cpp/pubsub/protobuf/person_loopback/src/person_loopback.cpp similarity index 58% rename from samples/cpp/person/person_snd_multicast/src/person_snd_multicast.cpp rename to samples/cpp/pubsub/protobuf/person_loopback/src/person_loopback.cpp index e8887bc..afbcc2b 100644 --- a/samples/cpp/person/person_snd_multicast/src/person_snd_multicast.cpp +++ b/samples/cpp/pubsub/protobuf/person_loopback/src/person_loopback.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -27,26 +28,40 @@ int main(int argc, char **argv) { // initialize eCAL API - eCAL::Initialize(argc, argv, "person publisher multicast"); + eCAL::Initialize(argc, argv, "person publisher"); // set process state eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); + // enable to receive our own messages + eCAL::Util::EnableLoopback(true); + // create a publisher (topic name "person") eCAL::protobuf::CPublisher pub("person"); - // switch all layer off - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - - // switch unicast layer on - pub.SetLayerMode(eCAL::TLayer::tlayer_udp_mc, eCAL::TLayer::smode_on); - // generate a class instance of Person pb::People::Person person; + eCAL::protobuf::CSubscriber sub("person"); + auto receive_lambda = [&sub](const char* /*topic_name_*/, const pb::People::Person& person_, const long long /*time_*/, const long long /*clock_*/, const long long /*id_*/){ + std::cout << "------------------------------------------" << std::endl; + std::cout << " RECEIVED " << std::endl; + std::cout << "------------------------------------------" << std::endl; + std::cout << "person id : " << person_.id() << std::endl; + std::cout << "person name : " << person_.name() << std::endl; + std::cout << "person stype : " << person_.stype() << std::endl; + std::cout << "person email : " << person_.email() << std::endl; + std::cout << "dog.name : " << person_.dog().name() << std::endl; + std::cout << "house.rooms : " << person_.house().rooms() << std::endl; + std::cout << "------------------------------------------" << std::endl; + std::cout << std::endl; + + }; + sub.AddReceiveCallback(receive_lambda); + // enter main loop - auto cnt(0); - while (eCAL::Ok()) + auto cnt = 0; + while(eCAL::Ok()) { // set person object content person.set_id(++cnt); @@ -60,6 +75,9 @@ int main(int argc, char **argv) pub.Send(person); // print content + std::cout << "------------------------------------------" << std::endl; + std::cout << " SENT " << std::endl; + std::cout << "------------------------------------------" << std::endl; std::cout << "person id : " << person.id() << std::endl; std::cout << "person name : " << person.name() << std::endl; std::cout << "person stype : " << person.stype() << std::endl; @@ -74,6 +92,6 @@ int main(int argc, char **argv) // finalize eCAL API eCAL::Finalize(); - + return(0); } diff --git a/samples/cpp/person/person_rec/src/protobuf/animal.proto b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/animal.proto similarity index 100% rename from samples/cpp/person/person_rec/src/protobuf/animal.proto rename to samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/animal.proto diff --git a/samples/cpp/person/person_rec/src/protobuf/house.proto b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/house.proto similarity index 100% rename from samples/cpp/person/person_rec/src/protobuf/house.proto rename to samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/house.proto diff --git a/samples/cpp/person/person_rec/src/protobuf/person.proto b/samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/person.proto similarity index 100% rename from samples/cpp/person/person_rec/src/protobuf/person.proto rename to samples/cpp/pubsub/protobuf/person_loopback/src/protobuf/person.proto diff --git a/samples/cpp/person/person_rec/CMakeLists.txt b/samples/cpp/pubsub/protobuf/person_rec/CMakeLists.txt similarity index 98% rename from samples/cpp/person/person_rec/CMakeLists.txt rename to samples/cpp/pubsub/protobuf/person_rec/CMakeLists.txt index 35f2e96..f933a33 100644 --- a/samples/cpp/person/person_rec/CMakeLists.txt +++ b/samples/cpp/pubsub/protobuf/person_rec/CMakeLists.txt @@ -46,4 +46,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/person) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/protobuf) diff --git a/samples/cpp/person/person_rec/src/person_rec.cpp b/samples/cpp/pubsub/protobuf/person_rec/src/person_rec.cpp similarity index 100% rename from samples/cpp/person/person_rec/src/person_rec.cpp rename to samples/cpp/pubsub/protobuf/person_rec/src/person_rec.cpp diff --git a/samples/cpp/person/person_rec_events/src/protobuf/animal.proto b/samples/cpp/pubsub/protobuf/person_rec/src/protobuf/animal.proto similarity index 100% rename from samples/cpp/person/person_rec_events/src/protobuf/animal.proto rename to samples/cpp/pubsub/protobuf/person_rec/src/protobuf/animal.proto diff --git a/samples/cpp/person/person_rec_events/src/protobuf/house.proto b/samples/cpp/pubsub/protobuf/person_rec/src/protobuf/house.proto similarity index 100% rename from samples/cpp/person/person_rec_events/src/protobuf/house.proto rename to samples/cpp/pubsub/protobuf/person_rec/src/protobuf/house.proto diff --git a/samples/cpp/person/person_rec_events/src/protobuf/person.proto b/samples/cpp/pubsub/protobuf/person_rec/src/protobuf/person.proto similarity index 100% rename from samples/cpp/person/person_rec_events/src/protobuf/person.proto rename to samples/cpp/pubsub/protobuf/person_rec/src/protobuf/person.proto diff --git a/samples/cpp/person/person_snd/CMakeLists.txt b/samples/cpp/pubsub/protobuf/person_snd/CMakeLists.txt similarity index 98% rename from samples/cpp/person/person_snd/CMakeLists.txt rename to samples/cpp/pubsub/protobuf/person_snd/CMakeLists.txt index 031cc8f..48ca976 100644 --- a/samples/cpp/person/person_snd/CMakeLists.txt +++ b/samples/cpp/pubsub/protobuf/person_snd/CMakeLists.txt @@ -46,4 +46,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/person) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/protobuf) diff --git a/samples/cpp/person/person_snd/src/person_snd.cpp b/samples/cpp/pubsub/protobuf/person_snd/src/person_snd.cpp similarity index 100% rename from samples/cpp/person/person_snd/src/person_snd.cpp rename to samples/cpp/pubsub/protobuf/person_snd/src/person_snd.cpp diff --git a/samples/cpp/person/person_rec_lambda_in_class/src/protobuf/animal.proto b/samples/cpp/pubsub/protobuf/person_snd/src/protobuf/animal.proto similarity index 100% rename from samples/cpp/person/person_rec_lambda_in_class/src/protobuf/animal.proto rename to samples/cpp/pubsub/protobuf/person_snd/src/protobuf/animal.proto diff --git a/samples/cpp/person/person_rec_lambda_in_class/src/protobuf/house.proto b/samples/cpp/pubsub/protobuf/person_snd/src/protobuf/house.proto similarity index 100% rename from samples/cpp/person/person_rec_lambda_in_class/src/protobuf/house.proto rename to samples/cpp/pubsub/protobuf/person_snd/src/protobuf/house.proto diff --git a/samples/cpp/person/person_rec_lambda_in_class/src/protobuf/person.proto b/samples/cpp/pubsub/protobuf/person_snd/src/protobuf/person.proto similarity index 100% rename from samples/cpp/person/person_rec_lambda_in_class/src/protobuf/person.proto rename to samples/cpp/pubsub/protobuf/person_snd/src/protobuf/person.proto diff --git a/samples/cpp/misc/proto_dyn_json/CMakeLists.txt b/samples/cpp/pubsub/protobuf/proto_dyn_json_rec/CMakeLists.txt similarity index 89% rename from samples/cpp/misc/proto_dyn_json/CMakeLists.txt rename to samples/cpp/pubsub/protobuf/proto_dyn_json_rec/CMakeLists.txt index 7081837..4819fb8 100644 --- a/samples/cpp/misc/proto_dyn_json/CMakeLists.txt +++ b/samples/cpp/pubsub/protobuf/proto_dyn_json_rec/CMakeLists.txt @@ -20,15 +20,15 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(proto_dyn_json) +project(proto_dyn_json_rec) find_package(eCAL REQUIRED) -set(proto_dyn_json_src - src/proto_dyn_json.cpp +set(proto_dyn_json_rec_src + src/proto_dyn_json_rec.cpp ) -ecal_add_sample(${PROJECT_NAME} ${proto_dyn_json_src}) +ecal_add_sample(${PROJECT_NAME} ${proto_dyn_json_rec_src}) target_link_libraries(${PROJECT_NAME} eCAL::core) diff --git a/samples/cpp/misc/proto_dyn_json/src/proto_dyn_json.cpp b/samples/cpp/pubsub/protobuf/proto_dyn_json_rec/src/proto_dyn_json_rec.cpp similarity index 100% rename from samples/cpp/misc/proto_dyn_json/src/proto_dyn_json.cpp rename to samples/cpp/pubsub/protobuf/proto_dyn_json_rec/src/proto_dyn_json_rec.cpp diff --git a/samples/cpp/misc/proto_dyn/CMakeLists.txt b/samples/cpp/pubsub/protobuf/proto_dyn_rec/CMakeLists.txt similarity index 90% rename from samples/cpp/misc/proto_dyn/CMakeLists.txt rename to samples/cpp/pubsub/protobuf/proto_dyn_rec/CMakeLists.txt index dc302aa..235ff9f 100644 --- a/samples/cpp/misc/proto_dyn/CMakeLists.txt +++ b/samples/cpp/pubsub/protobuf/proto_dyn_rec/CMakeLists.txt @@ -20,16 +20,16 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -project(proto_dyn) +project(proto_dyn_rec) find_package(eCAL REQUIRED) find_package(Protobuf REQUIRED) -set(proto_dyn_src - src/proto_dyn.cpp +set(proto_dyn_rec_src + src/proto_dyn_rec.cpp ) -ecal_add_sample(${PROJECT_NAME} ${proto_dyn_src}) +ecal_add_sample(${PROJECT_NAME} ${proto_dyn_rec_src}) target_include_directories(${PROJECT_NAME} PRIVATE .) diff --git a/samples/cpp/misc/proto_dyn/src/proto_dyn.cpp b/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp similarity index 100% rename from samples/cpp/misc/proto_dyn/src/proto_dyn.cpp rename to samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp diff --git a/samples/cpp/minimal/minimal_rec/CMakeLists.txt b/samples/cpp/pubsub/string/minimal_rec/CMakeLists.txt similarity index 98% rename from samples/cpp/minimal/minimal_rec/CMakeLists.txt rename to samples/cpp/pubsub/string/minimal_rec/CMakeLists.txt index d16bba4..d622abc 100644 --- a/samples/cpp/minimal/minimal_rec/CMakeLists.txt +++ b/samples/cpp/pubsub/string/minimal_rec/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/minimal) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/string) diff --git a/samples/cpp/minimal/minimal_rec/src/minimal_rec.cpp b/samples/cpp/pubsub/string/minimal_rec/src/minimal_rec.cpp similarity index 100% rename from samples/cpp/minimal/minimal_rec/src/minimal_rec.cpp rename to samples/cpp/pubsub/string/minimal_rec/src/minimal_rec.cpp diff --git a/samples/cpp/minimal/minimal_rec_cb/CMakeLists.txt b/samples/cpp/pubsub/string/minimal_rec_cb/CMakeLists.txt similarity index 98% rename from samples/cpp/minimal/minimal_rec_cb/CMakeLists.txt rename to samples/cpp/pubsub/string/minimal_rec_cb/CMakeLists.txt index b98e0f1..250ca73 100644 --- a/samples/cpp/minimal/minimal_rec_cb/CMakeLists.txt +++ b/samples/cpp/pubsub/string/minimal_rec_cb/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/minimal) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/string) diff --git a/samples/cpp/minimal/minimal_rec_cb/src/minimal_rec_cb.cpp b/samples/cpp/pubsub/string/minimal_rec_cb/src/minimal_rec_cb.cpp similarity index 100% rename from samples/cpp/minimal/minimal_rec_cb/src/minimal_rec_cb.cpp rename to samples/cpp/pubsub/string/minimal_rec_cb/src/minimal_rec_cb.cpp diff --git a/samples/cpp/minimal/minimal_snd/CMakeLists.txt b/samples/cpp/pubsub/string/minimal_snd/CMakeLists.txt similarity index 98% rename from samples/cpp/minimal/minimal_snd/CMakeLists.txt rename to samples/cpp/pubsub/string/minimal_snd/CMakeLists.txt index bc964ae..086cd9d 100644 --- a/samples/cpp/minimal/minimal_snd/CMakeLists.txt +++ b/samples/cpp/pubsub/string/minimal_snd/CMakeLists.txt @@ -36,4 +36,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/minimal) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/pubsub/string) diff --git a/samples/cpp/minimal/minimal_snd/src/minimal_snd.cpp b/samples/cpp/pubsub/string/minimal_snd/src/minimal_snd.cpp similarity index 94% rename from samples/cpp/minimal/minimal_snd/src/minimal_snd.cpp rename to samples/cpp/pubsub/string/minimal_snd/src/minimal_snd.cpp index 687da68..85889a6 100644 --- a/samples/cpp/minimal/minimal_snd/src/minimal_snd.cpp +++ b/samples/cpp/pubsub/string/minimal_snd/src/minimal_snd.cpp @@ -35,6 +35,10 @@ int main(int argc, char **argv) // publisher for topic "Hello" eCAL::string::CPublisher pub("Hello"); + // set some attributes + pub.SetAttribute("Hello", "World"); + pub.SetAttribute("Hallo", "Welt"); + // send updates int cnt = 0; while (eCAL::Ok()) diff --git a/samples/cpp/services/ecalplayer_client/CMakeLists.txt b/samples/cpp/services/ecalplayer_client/CMakeLists.txt deleted file mode 100644 index f8a9d1b..0000000 --- a/samples/cpp/services/ecalplayer_client/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(ecalplayer_client) - -find_package(eCAL REQUIRED) - -set(ecalplayer_client_src - src/ecalplayer_client.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${ecalplayer_client_src}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - eCAL::app_pb -) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) diff --git a/samples/cpp/services/ecalplayer_client/src/ecalplayer_client.cpp b/samples/cpp/services/ecalplayer_client/src/ecalplayer_client.cpp deleted file mode 100644 index 033dd2f..0000000 --- a/samples/cpp/services/ecalplayer_client/src/ecalplayer_client.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100 4505 4800) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include -#include -#include - -// callback for player service response -void OnPlayerResponse(const struct eCAL::SServiceResponse& service_response_) -{ - switch (service_response_.call_state) - { - // service successful executed - case call_state_executed: - { - if (service_response_.method_name == "GetConfig") - { - eCAL::pb::play::GetConfigResponse response; - response.ParseFromString(service_response_.response); - std::cout << "PlayerService " << service_response_.method_name << " called successfully on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - else if (service_response_.method_name == "Response") - { - eCAL::pb::play::Response response; - response.ParseFromString(service_response_.response); - std::cout << "PlayerService " << service_response_.method_name << " called successfully on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - else if (service_response_.method_name == "GetState") - { - eCAL::pb::play::State response; - response.ParseFromString(service_response_.response); - std::cout << "PlayerService " << service_response_.method_name << " called successfully on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - else - { - std::cout << "PlayerService " << service_response_.method_name << " received unknown message on host " << service_response_.host_name << std::endl; - } - } - break; - // service execution failed - case call_state_failed: - { - eCAL::pb::play::Response response; - response.ParseFromString(service_response_.response); - std::cout << "PlayerService " << service_response_.method_name << " failed with \"" << response.error() << "\" on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - break; - default: - break; - } -} - -// main entry -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "ecalplayer client"); - - // create player service client - eCAL::protobuf::CServiceClient player_service; - player_service.AddResponseCallback(OnPlayerResponse); - - // sleep for service matching - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - // requests - eCAL::pb::play::CommandRequest command_request; - eCAL::pb::play::GetConfigRequest get_config_request; - eCAL::pb::play::SetConfigRequest set_config_request; - - while (eCAL::Ok()) - { - // reset requests - command_request.Clear(); - get_config_request.Clear(); - set_config_request.Clear(); - - //////////////////////////////////// - // "GetConfig" // - //////////////////////////////////// - std::cout << "eCAL::pb::play::EcalPlayService:GetConfig()" << std::endl; - player_service.Call("GetConfig", get_config_request); - std::cout << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - //////////////////////////////////// - // "SetConfig" // - //////////////////////////////////// - std::cout << "eCAL::pb::play::EcalPlayService:SetConfig()" << std::endl; - auto config = set_config_request.mutable_config()->mutable_items(); - - (*config)["measurement_path"] = "C:\\meas01"; // Current measurement path (or empty string if no measurement is loaded) - (*config)["limit_play_speed"] = "true"; // Whether the playback is limited to the play_speed value (if false, the player publishes all frames as fast as possible) - (*config)["play_speed"] = "2.0"; // Play speed factor (1.0 indicates realtime playback) - (*config)["frame_dropping_allowed"] = "false"; // Whether the player will drop frames instead of slowing down the playback - (*config)["enforce_delay_accuracy"] = "false"; // Whether the player tries to always keep the correct amount of time between two frames, even if this may slow down the measurement - (*config)["repeat"] = "true"; // Repeat playback from the beginning if the end has been reached - (*config)["limit_interval_start_rel_secs"] = "1.0"; // Start the playback from this time (relative value in seconds, 0.0 indicates the begin of the measurement) - (*config)["limit_interval_end_rel_secs"] = "8.0"; // End the playback at this time (relative value in seconds) - - player_service.Call("SetConfig", set_config_request); - std::cout << set_config_request.DebugString() << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - player_service.Call("GetState", eCAL::pb::play::Empty()); - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - //////////////////////////////////// - // "SetCommand" // - //////////////////////////////////// - - // "SetCommand - initialize" - std::cout << "eCAL::pb::play::EcalPlayService:SetCommand() - initialize" << std::endl; - command_request.set_command(eCAL::pb::play::CommandRequest::initialize); - player_service.Call("SetCommand", command_request); - std::cout << command_request.DebugString() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - command_request.Clear(); - - // "SetCommand - jump_to" - std::cout << "eCAL::pb::play::EcalPlayService:SetCommand() - jum_to 2.0s" << std::endl; - command_request.set_command(eCAL::pb::play::CommandRequest::jump_to); - command_request.set_rel_time_secs(2.0); - player_service.Call("SetCommand", command_request); - std::cout << command_request.DebugString() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - command_request.Clear(); - - // "SetCommand - play" - std::cout << "eCAL::pb::play::EcalPlayService:SetCommand() - play" << std::endl; - command_request.set_command(eCAL::pb::play::CommandRequest::play); - player_service.Call("SetCommand", command_request); - std::cout << command_request.DebugString() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - command_request.Clear(); - - // "SetCommand - pause" - std::cout << "eCALPB.Play.GUIService:SetCommand() - pause" << std::endl; - command_request.set_command(eCAL::pb::play::CommandRequest::pause); - player_service.Call("SetCommand", command_request); - std::cout << command_request.DebugString() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - command_request.Clear(); - - // "SetCommand - step" - std::cout << "eCALPB.Play.GUIService:SetCommand() - step" << std::endl; - command_request.set_command(eCAL::pb::play::CommandRequest::step); - player_service.Call("SetCommand", command_request); - std::cout << command_request.DebugString() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - command_request.Clear(); - - // "SetCommand - step_channel" - std::cout << "eCALPB.Play.GUIService:SetCommand() - step_channel \"VehiclePosePb\"" << std::endl; - command_request.set_command(eCAL::pb::play::CommandRequest::step_channel); - command_request.set_step_reference_channel("VehiclePosePb"); - player_service.Call("SetCommand", command_request); - std::cout << command_request.DebugString() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - command_request.Clear(); - - // "SetCommand - de_initialize" - std::cout << "eCAL::pb::play::EcalPlayService:SetCommand() - de_initialize" << std::endl; - command_request.set_command(eCAL::pb::play::CommandRequest::de_initialize); - player_service.Call("SetCommand", command_request); - std::cout << command_request.DebugString() << std::endl; - - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - command_request.Clear(); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/services/ecalplayer_gui_client/CMakeLists.txt b/samples/cpp/services/ecalplayer_gui_client/CMakeLists.txt deleted file mode 100644 index 4fcfd18..0000000 --- a/samples/cpp/services/ecalplayer_gui_client/CMakeLists.txt +++ /dev/null @@ -1,111 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(ecalplayer_gui_client) - -find_package(eCAL REQUIRED) -find_package(Qt5 COMPONENTS - Core - Widgets -REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC OFF) # Reason for being turned off: AutoUIC will prevent VS from detecting changes in .ui files -set(CMAKE_AUTORCC OFF) # Reason for being turned off: AutoRCC will create an entirely new project in VS which clutters the solution appearance. Additionally, we cannot assign a source group to the generated .cpp files which will clutter the project. -set(CMAKE_INCLUDE_CURRENT_DIR ON) - - -set(source_files - src/ecalplayer_gui_client.cpp - src/ecalplayer_gui_client.h - src/main.cpp -) -set(qt_resource_files -) -set(ui_files - src/main_window.ui -) - -# compile qt resource files and ui files -qt5_add_resources(autogen_resources ${qt_resource_files}) -qt5_wrap_ui (autogen_ui ${ui_files}) - -ecal_add_sample (${PROJECT_NAME} - ${source_files} - ${qt_resource_files} - ${win32_resource_files} - ${ui_files} -) - -if(WIN32) - set_target_properties(${PROJECT_NAME} PROPERTIES - LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE" - LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE" - LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup" - LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") -endif() - - -target_include_directories(${PROJECT_NAME} PRIVATE src) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - eCAL::app_pb - Qt5::Widgets -) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -create_targets_protobuf() - -if(WIN32) - # Deploy Qt DLLs in the binary folder. This is necessary for starting the application from whithin the IDE without having to copy QtCore.dll, QtWidgets.dll etc. by hand each time - qt_add_windeployqt_postbuild(--no-system-d3d-compiler --no-compiler-runtime --no-opengl-sw --pdb "$") -endif() - -if(MSVC) - set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/wd4127 /wd4714") -endif() - -# Create a source tree that mirrors the filesystem -source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" - FILES - ${source_files} - ${win32_resource_files} - ${ui_files} -) - -source_group(resources FILES - ${qt_resource_files} -) - -# Also create a group for autogenerated files. The autogenerated ui files are not necessary as they are only header files. We add them anyhow, just for completeness. -source_group( autogen FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_automoc.cpp - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_autogen/mocs_compilation.cpp - ${autogen_ui} - ${autogen_resources} -) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) diff --git a/samples/cpp/services/ecalplayer_gui_client/src/ecalplayer_gui_client.cpp b/samples/cpp/services/ecalplayer_gui_client/src/ecalplayer_gui_client.cpp deleted file mode 100644 index da45267..0000000 --- a/samples/cpp/services/ecalplayer_gui_client/src/ecalplayer_gui_client.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include "ecalplayer_gui_client.h" - -#include -#include - -EcalplayGuiClient::EcalplayGuiClient(QWidget *parent) - : QMainWindow(parent) -{ - ui_.setupUi(this); - - // initialize eCAL API - eCAL::Initialize(0, nullptr, "ecalplayer gui client"); - - // create player service client - player_service_.AddResponseCallback([this](const struct eCAL::SServiceResponse& service_response) {this->onPlayerResponse(service_response); }); - - connect(ui_.get_config_request_button, &QPushButton::clicked, this, &EcalplayGuiClient::getConfigRequest); - connect(ui_.set_config_request_button, &QPushButton::clicked, this, &EcalplayGuiClient::setConfigRequest); - connect(ui_.command_request_button, &QPushButton::clicked, this, &EcalplayGuiClient::commandRequest); - connect(ui_.response_clear_button, &QPushButton::clicked, ui_.response_texteedit, &QTextEdit::clear); - connect(this, &EcalplayGuiClient::setResponseSignal, ui_.response_texteedit, &QTextEdit::setText ,Qt::ConnectionType::QueuedConnection); -} - -EcalplayGuiClient::~EcalplayGuiClient() -{} - -//////////////////////////////////////////////////////////////////////////////// -//// Request //// -//////////////////////////////////////////////////////////////////////////////// - -void EcalplayGuiClient::getConfigRequest() -{ - eCAL::pb::play::GetConfigRequest get_config_request; - player_service_.Call("GetConfig", get_config_request); -} - -void EcalplayGuiClient::setConfigRequest() -{ - eCAL::pb::play::SetConfigRequest set_config_request; - auto config = set_config_request.mutable_config()->mutable_items(); - - if (ui_.set_config_measurement_path_checkbox->isChecked()) - { - (*config)["measurement_path"] = ui_.set_config_measurement_path_lineedit->text().toStdString(); - } - - if (ui_.set_config_limit_play_speed_checkbox->isChecked()) - { - (*config)["limit_play_speed"] = ui_.set_config_limit_play_speed_lineedit->text().toStdString(); - } - - if (ui_.set_config_play_speed_checkbox->isChecked()) - { - (*config)["play_speed"] = ui_.set_config_play_speed_lineedit->text().toStdString(); - } - - if (ui_.set_config_frame_dropping_allowed_checkbox->isChecked()) - { - (*config)["frame_dropping_allowed"] = ui_.set_config_frame_dropping_allowed_lineedit->text().toStdString(); - } - - if (ui_.set_config_enforce_delay_accuracy_checkbox->isChecked()) - { - (*config)["enforce_delay_accuracy"] = ui_.set_config_enforce_delay_accuracy_lineedit->text().toStdString(); - } - - if (ui_.set_config_repeat_checkbox->isChecked()) - { - (*config)["repeat"] = ui_.set_config_repeat_lineedit->text().toStdString(); - } - - if (ui_.set_config_limit_interval_start_rel_secs_checkbox->isChecked()) - { - (*config)["limit_interval_start_rel_secs"] = ui_.set_config_limit_interval_start_rel_secs_lineedit->text().toStdString(); - } - - if (ui_.set_config_limit_interval_end_rel_secs_checkbox->isChecked()) - { - (*config)["limit_interval_end_rel_secs"] = ui_.set_config_limit_interval_end_rel_secs_lineedit->text().toStdString(); - } - - player_service_.Call("SetConfig", set_config_request); -} - -void EcalplayGuiClient::commandRequest() -{ - eCAL::pb::play::CommandRequest command_request; - - QString command_string = ui_.command_request_command_combobox->currentText(); - if (command_string == "none") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_none); - } - else if (command_string == "initialize") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_initialize); - } - else if (command_string == "de_initialize") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_de_initialize); - } - else if (command_string == "jump_to") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_jump_to); - } - else if (command_string == "play") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_play); - } - else if (command_string == "pause") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_pause); - } - else if (command_string == "step") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_step); - } - else if (command_string == "step_channel") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_step_channel); - } - else if (command_string == "exit") - { - command_request.set_command(eCAL::pb::play::CommandRequest_eCommand::CommandRequest_eCommand_exit); - } - else - { - QMessageBox error_message( - QMessageBox::Icon::Critical - , tr("Error") - , tr("Unknown command: ") + command_string - , QMessageBox::Button::Ok - , this); - error_message.exec(); - - return; - } - - if (ui_.command_request_channel_mapping_checkbox->isChecked()) - { - auto channel_map = command_request.mutable_channel_mapping(); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - QStringList lines = ui_.command_request_channel_mapping_textedit->toPlainText().split("\n", Qt::SplitBehaviorFlags::SkipEmptyParts); -#else // QT_VERSION - QStringList lines = ui_.command_request_channel_mapping_textedit->toPlainText().split("\n", QString::SplitBehavior::SkipEmptyParts); -#endif // QT_VERSION - for (auto& line : lines) - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - QStringList mapping_list = line.split("=", Qt::SplitBehaviorFlags::SkipEmptyParts); -#else // QT_VERSION - QStringList mapping_list = line.split("=", QString::SplitBehavior::SkipEmptyParts); -#endif // QT_VERSION - if (mapping_list.size() == 2) - { - (*channel_map)[mapping_list.at(0).trimmed().toStdString()] = mapping_list.at(1).trimmed().toStdString(); - } - } - } - - if (ui_.command_request_step_reference_channel_checkbox->isChecked()) - { - command_request.set_step_reference_channel(ui_.command_request_step_reference_channel_lineedit->text().toStdString()); - } - - if (ui_.command_request_rel_time_secs_checkbox->isChecked()) - { - command_request.set_rel_time_secs(ui_.command_request_rel_time_secs_spinbox->value()); - } - - player_service_.Call("SetCommand", command_request); -} - - -//////////////////////////////////////////////////////////////////////////////// -//// Response //// -//////////////////////////////////////////////////////////////////////////////// - -void EcalplayGuiClient::onPlayerResponse(const struct eCAL::SServiceResponse& service_response_) -{ - QString response_string; - QTextStream response_stream(&response_string); - - switch (service_response_.call_state) - { - // service successful executed - case call_state_executed: - { - if (service_response_.method_name == "GetConfig") - { - eCAL::pb::play::GetConfigResponse response; - response.ParseFromString(service_response_.response); - - response_stream << "PlayerService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - else - { - eCAL::pb::play::Response response; - response.ParseFromString(service_response_.response); - response_stream << "PlayerService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - break; - } - // service execution failed - case call_state_failed: - { - eCAL::pb::play::Response response; - response.ParseFromString(service_response_.response); - response_stream << "PlayerService " << service_response_.method_name.c_str() << " failed with \"" << response.error().c_str() << "\" on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - break; - } - default: - break; - } - - emit setResponseSignal(response_string); - -} - diff --git a/samples/cpp/services/ecalplayer_gui_client/src/main.cpp b/samples/cpp/services/ecalplayer_gui_client/src/main.cpp deleted file mode 100644 index 8e9411d..0000000 --- a/samples/cpp/services/ecalplayer_gui_client/src/main.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include "ecalplayer_gui_client.h" -#include -#include - -int main(int argc, char *argv[]) -{ - - // Just make sure that eCAL is initialized - eCAL::Initialize(0, nullptr, "EcalPlayGuiClient", eCAL::Init::Default); - - QApplication a(argc, argv); - - a.setOrganizationName ("Continental"); - a.setOrganizationDomain ("continental-corporation.com"); - a.setApplicationName ("ecalplay_gui_client"); - a.setApplicationDisplayName("eCAL Player GUI client"); - - EcalplayGuiClient* w = new EcalplayGuiClient(); - w->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose); - - w->show(); - int return_code = a.exec(); - - eCAL::Finalize(); - - return return_code; -} diff --git a/samples/cpp/services/ecalplayer_gui_client/src/main_window.ui b/samples/cpp/services/ecalplayer_gui_client/src/main_window.ui deleted file mode 100644 index de45abc..0000000 --- a/samples/cpp/services/ecalplayer_gui_client/src/main_window.ui +++ /dev/null @@ -1,543 +0,0 @@ - - - EcalplayGuiServiceMainWindow - - - - 0 - 0 - 688 - 799 - - - - - 0 - 0 - - - - - :/ecalplay/APP_ICON:/ecalplay/APP_ICON - - - true - - - - - - - Qt::Vertical - - - false - - - - - 0 - 0 - - - - 0 - - - - GetConfigRequest - - - - - - No settings - - - Qt::AlignCenter - - - - - - - Send request - - - - - - - - SetConfigRequest - - - - - - - - measurement_path - - - - - - - false - - - - - - - limit_play_speed - - - - - - - false - - - - - - - play_speed - - - - - - - false - - - - - - - frame_dropping_allowed - - - - - - - false - - - - - - - enforce_delay_accuracy - - - - - - - false - - - - - - - limit_interval_start_rel_secs - - - - - - - false - - - - - - - limit_interval_end_rel_secs - - - - - - - false - - - - - - - repeat - - - - - - - false - - - - - - - - - Send request - - - - - - - - CommandRequest - - - - - - - - command - - - - - - - - none - - - - - initialize - - - - - de_initialize - - - - - jump_to - - - - - play - - - - - pause - - - - - step - - - - - step_channel - - - - - exit - - - - - - - - channel_mapping - - - - - - - false - - - Channel mapping in form "source = target" with one mapping per line - - - - - - - step_reference_channel - - - - - - - false - - - - - - - rel_time_secs - - - - - - - false - - - false - - - 3 - - - -10000.000000000000000 - - - 10000.000000000000000 - - - - - - - - - Send request - - - - - - - - - Response - - - - - - - Courier New - - - - true - - - - - - - Clear - - - - - - - - - - - - - - - set_config_measurement_path_checkbox - toggled(bool) - set_config_measurement_path_lineedit - setEnabled(bool) - - - 93 - 44 - - - 199 - 49 - - - - - set_config_limit_play_speed_checkbox - toggled(bool) - set_config_limit_play_speed_lineedit - setEnabled(bool) - - - 94 - 76 - - - 269 - 73 - - - - - set_config_play_speed_checkbox - toggled(bool) - set_config_play_speed_lineedit - setEnabled(bool) - - - 77 - 105 - - - 208 - 107 - - - - - set_config_frame_dropping_allowed_checkbox - toggled(bool) - set_config_frame_dropping_allowed_lineedit - setEnabled(bool) - - - 137 - 128 - - - 214 - 130 - - - - - set_config_enforce_delay_accuracy_checkbox - toggled(bool) - set_config_enforce_delay_accuracy_lineedit - setEnabled(bool) - - - 123 - 156 - - - 253 - 156 - - - - - set_config_limit_interval_start_rel_secs_checkbox - toggled(bool) - set_config_limit_interval_start_rel_secs_lineedit - setEnabled(bool) - - - 149 - 213 - - - 440 - 216 - - - - - set_config_limit_interval_end_rel_secs_checkbox - toggled(bool) - set_config_limit_interval_end_rel_secs_lineedit - setEnabled(bool) - - - 150 - 239 - - - 400 - 242 - - - - - command_request_channel_mapping_checkbox - toggled(bool) - command_request_channel_mapping_textedit - setEnabled(bool) - - - 114 - 69 - - - 119 - 69 - - - - - command_request_step_reference_channel_checkbox - toggled(bool) - command_request_step_reference_channel_lineedit - setEnabled(bool) - - - 119 - 69 - - - 119 - 69 - - - - - command_request_rel_time_secs_checkbox - toggled(bool) - command_request_rel_time_secs_spinbox - setEnabled(bool) - - - 117 - 69 - - - 119 - 69 - - - - - set_config_repeat_checkbox - toggled(bool) - set_config_repeat_lineedit - setEnabled(bool) - - - 58 - 180 - - - 251 - 179 - - - - - diff --git a/samples/cpp/services/ecalsys_client/src/ecalsys_client.cpp b/samples/cpp/services/ecalsys_client/src/ecalsys_client.cpp deleted file mode 100644 index 986f5bb..0000000 --- a/samples/cpp/services/ecalsys_client/src/ecalsys_client.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100 4505 4800) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include -#include -#include - -// callback for sys service response -void OnSysResponse(const struct eCAL::SServiceResponse& service_response_) -{ - switch (service_response_.call_state) - { - // service successful executed - case call_state_executed: - { - eCAL::pb::sys::Response response; - response.ParseFromString(service_response_.response); - std::cout << "SysService " << service_response_.method_name << " called successfully on host " << service_response_.host_name << std::endl; - } - break; - // service execution failed - case call_state_failed: - { - eCAL::pb::sys::Response response; - response.ParseFromString(service_response_.response); - std::cout << "SysService " << service_response_.method_name << " failed with \"" << response.error() << "\" on host " << service_response_.host_name << std::endl; - } - break; - default: - break; - } -} - -// main entry -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "ecalsys client"); - - // create player service client - eCAL::protobuf::CServiceClient sys_service; - sys_service.AddResponseCallback(OnSysResponse); - - // sleep for service matching - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - // requests - eCAL::pb::sys::TaskRequest trequest; - - // "StartTasks - All" - std::cout << "eCALPB.Sys.Service:StartTasks()" << std::endl; - trequest.set_all(true); - sys_service.Call("StartTasks", trequest); - std::cout << trequest.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // "RestartTasks - All" - std::cout << "eCALPB.Sys.Service:RestartTasks()" << std::endl; - trequest.set_all(true); - sys_service.Call("RestartTasks", trequest); - std::cout << trequest.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // "StopTasks - All" - std::cout << "eCALPB.Sys.Service:StopTasks()" << std::endl; - trequest.set_all(true); - sys_service.Call("StopTasks", trequest); - std::cout << trequest.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/services/latency_client/CMakeLists.txt b/samples/cpp/services/latency_client/CMakeLists.txt index 51c560d..999b78a 100644 --- a/samples/cpp/services/latency_client/CMakeLists.txt +++ b/samples/cpp/services/latency_client/CMakeLists.txt @@ -38,4 +38,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/latency) diff --git a/samples/cpp/services/latency_server/CMakeLists.txt b/samples/cpp/services/latency_server/CMakeLists.txt index 9d6938d..bd769ab 100644 --- a/samples/cpp/services/latency_server/CMakeLists.txt +++ b/samples/cpp/services/latency_server/CMakeLists.txt @@ -38,4 +38,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/latency) diff --git a/samples/cpp/services/math_client/CMakeLists.txt b/samples/cpp/services/math_client/CMakeLists.txt index 1712bfc..2f261e1 100644 --- a/samples/cpp/services/math_client/CMakeLists.txt +++ b/samples/cpp/services/math_client/CMakeLists.txt @@ -45,4 +45,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/math) diff --git a/samples/cpp/services/math_client/src/math_client.cpp b/samples/cpp/services/math_client/src/math_client.cpp index ed0eae5..2a5d93b 100644 --- a/samples/cpp/services/math_client/src/math_client.cpp +++ b/samples/cpp/services/math_client/src/math_client.cpp @@ -53,12 +53,12 @@ void OnClientState(const eCAL::SClientEventCallbackData* data_) break; } - std::cout << "Server Hostname : " << data_->attr.hname << std::endl; - std::cout << "Server Name : " << data_->attr.sname << std::endl; - std::cout << "Server Process : " << data_->attr.pname << std::endl; - std::cout << "Server PID : " << data_->attr.pid << std::endl; - std::cout << "Server TCP Port : " << data_->attr.tcp_port << std::endl; - std::cout << "---------------------------------" << std::endl << std::endl; + std::cout << "Server Hostname : " << data_->attr.hname << std::endl; + std::cout << "Server Name : " << data_->attr.sname << std::endl; + std::cout << "Server Process : " << data_->attr.pname << std::endl; + std::cout << "Server PID : " << data_->attr.pid << std::endl; + std::cout << "Server TCP Port (V1) : " << data_->attr.tcp_port_v1 << std::endl; + std::cout << "---------------------------------" << std::endl << std::endl; } // callback for math service response diff --git a/samples/cpp/services/math_server/CMakeLists.txt b/samples/cpp/services/math_server/CMakeLists.txt index ab51205..c0d0b97 100644 --- a/samples/cpp/services/math_server/CMakeLists.txt +++ b/samples/cpp/services/math_server/CMakeLists.txt @@ -45,4 +45,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/math) diff --git a/samples/cpp/services/minimal_client/CMakeLists.txt b/samples/cpp/services/minimal_client/CMakeLists.txt index e0b6143..ce2abf3 100644 --- a/samples/cpp/services/minimal_client/CMakeLists.txt +++ b/samples/cpp/services/minimal_client/CMakeLists.txt @@ -38,4 +38,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/minimal) diff --git a/samples/cpp/services/minimal_server/CMakeLists.txt b/samples/cpp/services/minimal_server/CMakeLists.txt index 2a3e020..730a5ea 100644 --- a/samples/cpp/services/minimal_server/CMakeLists.txt +++ b/samples/cpp/services/minimal_server/CMakeLists.txt @@ -38,4 +38,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/minimal) diff --git a/samples/cpp/services/ping_client/CMakeLists.txt b/samples/cpp/services/ping_client/CMakeLists.txt index cd7d5ad..e945397 100644 --- a/samples/cpp/services/ping_client/CMakeLists.txt +++ b/samples/cpp/services/ping_client/CMakeLists.txt @@ -45,4 +45,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/ping) diff --git a/samples/cpp/services/ping_client_dyn/CMakeLists.txt b/samples/cpp/services/ping_client_dyn/CMakeLists.txt deleted file mode 100644 index 355a9b3..0000000 --- a/samples/cpp/services/ping_client_dyn/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(ping_client_dyn) - -find_package(eCAL REQUIRED) -find_package(Protobuf REQUIRED) - -set(ping_client_dyn_src - src/ping_client_dyn.cpp - src/proto_json_conv.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${ping_client_dyn_src}) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - 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/services) diff --git a/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp b/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp deleted file mode 100644 index 47efbf0..0000000 --- a/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include - -#include -#include -#include -#include -#include - -#include "proto_json_conv.h" - -// main entry -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "ping client dynamic"); - - // create ping service client - const std::string service_name("ping service"); - const std::string method_name ("Ping"); - eCAL::CServiceClient ping_client(service_name); - - // waiting for service - while (!ping_client.IsConnected()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - std::cout << "Waiting for the service .." << std::endl; - } - - // get service method type names - std::string req_type, resp_type; - if (!eCAL::Util::GetServiceTypeNames(service_name, method_name, req_type, resp_type)) - { - throw std::runtime_error("Could not get service type names !"); - } - - // get service method type descriptions - std::string req_desc, resp_desc; - if (!eCAL::Util::GetServiceDescription(service_name, method_name, req_desc, resp_desc)) - { - throw std::runtime_error("Could not get service type descriptions !"); - } - - // create dynamic protobuf message decoder to create request and response message objects - std::string error_s; - - // create the request message object - eCAL::protobuf::CProtoDynDecoder req_decoder; - std::shared_ptr req_msg(req_decoder.GetProtoMessageFromDescriptor(req_desc, req_type, error_s)); - if (!req_msg) throw std::runtime_error("Could not create request message object: " + error_s); - - // create the response message object - eCAL::protobuf::CProtoDynDecoder resp_decoder; - std::shared_ptr resp_msg(resp_decoder.GetProtoMessageFromDescriptor(resp_desc, resp_type, error_s)); - if (!resp_msg) throw std::runtime_error("Could not create response message object: " + error_s); - - int cnt(0); - while(eCAL::Ok() && ping_client.IsConnected()) - { - // create JSON request - std::string req_json = "{\"message\": \"HELLO WORLD FROM DYNAMIC PING CLIENT (" + std::to_string(++cnt) + ")\"}"; - std::string ping_request = GetSerialzedMessageFromJSON(req_msg.get(), req_json); - - if (!ping_request.empty()) - { - // call Ping service method - eCAL::ServiceResponseVecT service_response_vec; - if (ping_client.Call("Ping", ping_request, -1, &service_response_vec)) - { - std::cout << std::endl << "PingService::Ping method called with message (JSON) : " << req_json << std::endl; - - for (auto service_response : service_response_vec) - { - switch (service_response.call_state) - { - // service successful executed - case call_state_executed: - { - std::string resp_json = GetJSONFromSerialzedMessage(resp_msg.get(), service_response.response); - std::cout << "Received response PingService / Ping (JSON) : " << resp_json << " from host " << service_response.host_name << std::endl; - } - break; - // service execution failed - case call_state_failed: - std::cout << "Received error PingService / Ping : " << service_response.error_msg << " from host " << service_response.host_name << std::endl; - break; - default: - break; - } - } - } - else - { - std::cout << "PingService::Ping method call failed .." << std::endl << std::endl; - } - } - - // sleep a second - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - } - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/services/ping_client_dyn/src/proto_json_conv.cpp b/samples/cpp/services/ping_client_dyn/src/proto_json_conv.cpp deleted file mode 100644 index de61d71..0000000 --- a/samples/cpp/services/ping_client_dyn/src/proto_json_conv.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include - -#include -#include - -std::string GetSerialzedMessageFromJSON(google::protobuf::Message* msg_proto_, const std::string& msg_json_) -{ - if (!msg_proto_) - { - std::cerr << "Google message pointer empty." << std::endl; - return ""; - } - - // read JSON string into message object - google::protobuf::util::Status status = google::protobuf::util::JsonStringToMessage(msg_json_, msg_proto_); - if (!status.ok()) - { - std::cerr << "Could not convert JSON to google message." << std::endl; - return ""; - } - - return msg_proto_->SerializeAsString(); -} - -std::string GetJSONFromSerialzedMessage(google::protobuf::Message* msg_proto_, const std::string& msg_ser_) -{ - if (!msg_proto_) - { - std::cerr << "Google message pointer empty." << std::endl; - return ""; - } - - // read serialized message string into message object - if (!msg_proto_->ParseFromString(msg_ser_)) - { - std::cerr << "Could not parse google message content from string." << std::endl; - return ""; - } - - // convert message object to JSON string - std::string msg_json; - google::protobuf::util::Status status = google::protobuf::util::MessageToJsonString(*msg_proto_, &msg_json); - if (!status.ok()) - { - std::cerr << "Could not convert google message to JSON." << std::endl; - return ""; - } - - return msg_json; -} diff --git a/samples/cpp/services/ping_client_dyn/src/proto_json_conv.h b/samples/cpp/services/ping_client_dyn/src/proto_json_conv.h deleted file mode 100644 index d796f88..0000000 --- a/samples/cpp/services/ping_client_dyn/src/proto_json_conv.h +++ /dev/null @@ -1,58 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4100 4127 4146 4505 4800 4189 4592) // disable proto warnings -#endif -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - -#include - -/** - * @brief Create a serialized protobuf message from a google message object and a JSON string. - * - * @param msg_proto_ Protobuf message object. - * @param msg_json_ JSON string. - * - * @return serialized google message string or empty string if conversion failed -**/ -std::string GetSerialzedMessageFromJSON(google::protobuf::Message* msg_proto_, const std::string& msg_json_); - -/** - * @brief Create a JSON string from a google message object and a serialized protobuf message. - * - * @param msg_proto_ Protobuf message object. - * @param msg_ser_ Serialized google protobuf message. - * - * @return JSON string or empty string if conversion failed -**/ -std::string GetJSONFromSerialzedMessage(google::protobuf::Message* msg_proto_, const std::string& msg_ser_); diff --git a/samples/cpp/services/ping_server/CMakeLists.txt b/samples/cpp/services/ping_server/CMakeLists.txt index e74406e..591c135 100644 --- a/samples/cpp/services/ping_server/CMakeLists.txt +++ b/samples/cpp/services/ping_server/CMakeLists.txt @@ -45,4 +45,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_sample(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services/ping) diff --git a/samples/cpp/services/rec_client_service_cli/CMakeLists.txt b/samples/cpp/services/rec_client_service_cli/CMakeLists.txt deleted file mode 100644 index e8a4ffd..0000000 --- a/samples/cpp/services/rec_client_service_cli/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -cmake_minimum_required(VERSION 3.10) - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(rec_client_service_cli) - -find_package(eCAL REQUIRED) - -set(ecalrecorder_client_src - src/ecalrecorder_client.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${ecalrecorder_client_src}) - -target_include_directories(${PROJECT_NAME} PRIVATE .) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - eCAL::app_pb) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) diff --git a/samples/cpp/services/rec_client_service_cli/src/ecalrecorder_client.cpp b/samples/cpp/services/rec_client_service_cli/src/ecalrecorder_client.cpp deleted file mode 100644 index 4f42bb0..0000000 --- a/samples/cpp/services/rec_client_service_cli/src/ecalrecorder_client.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100 4505 4800) -#endif -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include -#include -#include - -// callback for recorder service response -void OnRecorderResponse(const struct eCAL::SServiceResponse& service_response_) -{ - switch (service_response_.call_state) - { - // service successful executed - case call_state_executed: - { - if (service_response_.method_name == "GetConfig") - { - eCAL::pb::rec_client::GetConfigResponse response; - response.ParseFromString(service_response_.response); - std::cout << "RecorderService " << service_response_.method_name << " called successfully on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - else - { - eCAL::pb::rec_client::Response response; - response.ParseFromString(service_response_.response); - std::cout << "RecorderService " << service_response_.method_name << " called successfully on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - } - break; - // service execution failed - case call_state_failed: - { - if (service_response_.method_name == "GetConfig") - { - eCAL::pb::rec_client::GetConfigResponse response; - response.ParseFromString(service_response_.response); - std::cout << "RecorderService " << service_response_.method_name << " failed with \"" << response.error() << "\" on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - - if (service_response_.method_name == "SetConfig") - { - eCAL::pb::rec_client::Response response; - response.ParseFromString(service_response_.response); - std::cout << "RecorderService " << service_response_.method_name << " failed with \"" << response.error() << "\" on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - - if (service_response_.method_name == "SetCommand") - { - eCAL::pb::rec_client::Response response; - response.ParseFromString(service_response_.response); - std::cout << "RecorderService " << service_response_.method_name << " failed with \"" << response.error() << "\" on host " << service_response_.host_name << std::endl; - std::cout << response.DebugString(); - } - } - break; - default: - break; - } -} - -// main entry -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "RecClientServiceCli"); - - // create recorder service client - eCAL::protobuf::CServiceClient recorder_service; - recorder_service.AddResponseCallback(OnRecorderResponse); - - // waiting for service - while (!recorder_service.IsConnected()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - std::cout << "Waiting for the service .." << std::endl; - } - - // "GetConfig" - eCAL::pb::rec_client::GetConfigRequest get_config_request; - std::cout << "eCAL.pb.rec.EcalRecService:GetConfig()" << std::endl; - recorder_service.Call("GetConfig", get_config_request); - std::cout << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - // "SetConfig" - eCAL::pb::rec_client::SetConfigRequest set_config_request; - std::cout << "eCAL.pb.rec.EcalRecService:SetConfig()" << std::endl; - auto config = set_config_request.mutable_config()->mutable_items(); - - (*config)["max_pre_buffer_length_secs"] = "42"; // The maximum amount of time to keep in the pre-buffer - (*config)["pre_buffering_enabled"] = "true"; // Whether pre-buffering is enabled - (*config)["host_filter"] = ""; // List of hosts (\n separated). The recorder will only record channels published by these hosts. If empty, all hosts are allowed. - (*config)["record_mode"] = "blacklist"; // Whether to record all topics or use a blacklist / whitelist to only record some topics. Changing the mode will clear the listed_topics, so it is advisable to also provide a new listed_topics list. - (*config)["listed_topics"] = "topic1\ntopic2\ntopic3"; // Whitelist / blacklist, when topic_mode is set accordingly (\n separated). If topic_mode is "all", this setting will be ignored. - (*config)["enabled_addons"] = ""; // List of addon-IDs that shall be enabled (\n separated). - - recorder_service.Call("SetConfig", set_config_request); - std::cout << set_config_request.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - // state request - eCAL::pb::rec_client::CommandRequest state_request; - - (*state_request.mutable_command_params()->mutable_items())["meas_id"] = "1234"; // An ID to identify the measurement later on - (*state_request.mutable_command_params()->mutable_items())["meas_root_dir"] = "$TARGET{OSSELECT WIN \"C:\" LINUX \"$TARGET{ENV HOME}\"}/ecal_meas"; // The root directory to save the measurement to (un-evaluated format) - (*state_request.mutable_command_params()->mutable_items())["meas_name"] = "meas_${TIME}"; // The name of the measurement (un-evaluated format) - (*state_request.mutable_command_params()->mutable_items())["description"] = "This is my description :)"; // The description that will be saved to the measurement's doc folder (un-evaluated format). - (*state_request.mutable_command_params()->mutable_items())["max_file_size_mib"] = "775"; // The maximum HDF5 file size (When exceeding the file size, the measurement will be splitted into multiple files). - (*state_request.mutable_command_params()->mutable_items())["one_file_per_topic"] = "false"; // Whether the recorder shall create 1 hdf5 file per channel - - - // "initialize" - std::cout << "eCAL.pb.rec.EcalRecService:SetCommand()" << std::endl; - state_request.set_command(eCAL::pb::rec_client::CommandRequest::initialize); - recorder_service.Call("SetCommand", state_request); - std::cout << state_request.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // "de_initialize" - std::cout << "eCAL.pb.rec.EcalRecService:SetCommand()" << std::endl; - state_request.set_command(eCAL::pb::rec_client::CommandRequest::de_initialize); - recorder_service.Call("SetCommand", state_request); - std::cout << state_request.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // "start_recording" - std::cout << "eCAL.pb.rec.EcalRecService:SetCommand()" << std::endl; - state_request.set_command(eCAL::pb::rec_client::CommandRequest::start_recording); - recorder_service.Call("SetCommand", state_request); - std::cout << state_request.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // "stop_recording" - std::cout << "eCAL.pb.rec.EcalRecService:SetCommand()" << std::endl; - state_request.set_command(eCAL::pb::rec_client::CommandRequest::stop_recording); - recorder_service.Call("SetCommand", state_request); - std::cout << state_request.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // "save_pre_buffer" - std::cout << "eCAL.pb.rec.EcalRecService:SetCommand()" << std::endl; - state_request.set_command(eCAL::pb::rec_client::CommandRequest::save_pre_buffer); - recorder_service.Call("SetCommand", state_request); - std::cout << state_request.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // "exit" - std::cout << "eCAL.pb.rec.EcalRecService:SetCommand()" << std::endl; - state_request.set_command(eCAL::pb::rec_client::CommandRequest::exit); - recorder_service.Call("SetCommand", state_request); - std::cout << state_request.DebugString() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - // finalize eCAL API - eCAL::Finalize(); - - return(0); -} diff --git a/samples/cpp/services/rec_client_service_gui/CMakeLists.txt b/samples/cpp/services/rec_client_service_gui/CMakeLists.txt deleted file mode 100644 index 29c3706..0000000 --- a/samples/cpp/services/rec_client_service_gui/CMakeLists.txt +++ /dev/null @@ -1,107 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2018 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(rec_client_service_gui) - -find_package(eCAL REQUIRED) -find_package(Qt5 COMPONENTS - Core - Widgets -REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC OFF) # Reason for being turned off: AutoUIC will prevent VS from detecting changes in .ui files -set(CMAKE_AUTORCC OFF) # Reason for being turned off: AutoRCC will create an entirely new project in VS which clutters the solution appearance. Additionally, we cannot assign a source group to the generated .cpp files which will clutter the project. -set(CMAKE_INCLUDE_CURRENT_DIR ON) - - -set(source_files - src/EcalrecGuiClient.cpp - src/EcalrecGuiClient.h - src/main.cpp -) -set(qt_resource_files -) -set(ui_files - src/MainWindow.ui -) - -# compile qt resource files and ui files -qt5_add_resources(autogen_resources ${qt_resource_files}) -qt5_wrap_ui (autogen_ui ${ui_files}) - -ecal_add_sample (${PROJECT_NAME} - ${source_files} - ${qt_resource_files} - ${win32_resource_files} - ${ui_files} -) - -if(WIN32) - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") -endif(WIN32) - - -target_include_directories(${PROJECT_NAME} PRIVATE src) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - eCAL::app_pb - Qt5::Widgets -) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -create_targets_protobuf() - -if(WIN32) - # Deploy Qt DLLs in the binary folder. This is necessary for starting the application from whithin the IDE without having to copy QtCore.dll, QtWidgets.dll etc. by hand each time - qt_add_windeployqt_postbuild(--no-system-d3d-compiler --no-compiler-runtime --no-opengl-sw --pdb "$") -endif(WIN32) - -if(MSVC) - set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/wd4127 /wd4714") -endif() - -# Create a source tree that mirrors the filesystem -source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" - FILES - ${source_files} - ${qt_resource_files} - ${win32_resource_files} - ${ui_files} -) - -# Also create a group for autogenerated files. The autogenerated ui files are not necessary as they are only header files. We add them anyhow, just for completeness. -source_group( autogen FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_automoc.cpp - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_autogen/mocs_compilation.cpp - ${autogen_ui} - ${autogen_resources} -) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) diff --git a/samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.cpp b/samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.cpp deleted file mode 100644 index e16fac7..0000000 --- a/samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2018 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 "EcalrecGuiClient.h" - -#include -#include - -EcalrecGuiClient::EcalrecGuiClient(QWidget *parent) - : QMainWindow(parent) -{ - ui_.setupUi(this); - - // initialize eCAL API - eCAL::Initialize(0, nullptr, "RecClientServiceGui"); - - // create player service client - recorder_service_.AddResponseCallback([this](const struct eCAL::SServiceResponse& service_response) {this->onRecorderResponse(service_response); }); - - connect(ui_.hostname_lineedit, &QLineEdit::editingFinished, this, [this]() {recorder_service_.SetHostName(ui_.hostname_lineedit->text().toStdString()); }); - - connect(ui_.get_config_request_button, &QPushButton::clicked, this, &EcalrecGuiClient::getConfigRequest); - connect(ui_.set_config_request_button, &QPushButton::clicked, this, &EcalrecGuiClient::setConfigRequest); - connect(ui_.command_request_button, &QPushButton::clicked, this, &EcalrecGuiClient::commandRequest); - connect(ui_.get_state_request_button, &QPushButton::clicked, this, &EcalrecGuiClient::getStateRequest); - connect(ui_.response_clear_button, &QPushButton::clicked, ui_.response_texteedit, &QTextEdit::clear); - connect(this, &EcalrecGuiClient::setResponseSignal, ui_.response_texteedit, &QTextEdit::setText ,Qt::ConnectionType::QueuedConnection); -} - -EcalrecGuiClient::~EcalrecGuiClient() -{} - -//////////////////////////////////////////////////////////////////////////////// -//// Request //// -//////////////////////////////////////////////////////////////////////////////// - -void EcalrecGuiClient::getConfigRequest() -{ - eCAL::pb::rec_client::GetConfigRequest get_config_request; - recorder_service_.Call("GetConfig", get_config_request); -} - -void EcalrecGuiClient::setConfigRequest() -{ - eCAL::pb::rec_client::SetConfigRequest set_config_request; - auto config = set_config_request.mutable_config()->mutable_items(); - - if (ui_.set_config_max_pre_buffer_length_secs_checkbox->isChecked()) - { - (*config)["max_pre_buffer_length_secs"] = ui_.set_config_max_pre_buffer_length_secs_lineedit->text().toStdString(); - } - - if (ui_.set_config_pre_buffering_enabled_checkbox->isChecked()) - { - (*config)["pre_buffering_enabled"] = ui_.set_config_pre_buffering_enabled_lineedit->text().toStdString(); - } - - if (ui_.set_config_host_filter_checkbox->isChecked()) - { - (*config)["host_filter"] = ui_.set_config_host_filter_textedit->toPlainText().toStdString(); - } - - if (ui_.set_config_record_mode_checkbox->isChecked()) - { - (*config)["record_mode"] = ui_.set_config_record_mode_lineedit->text().toStdString(); - } - - if (ui_.set_config_listed_topics_checkbox->isChecked()) - { - (*config)["listed_topics"] = ui_.set_config_listed_topics_textedit->toPlainText().toStdString(); - } - - if (ui_.set_config_enabled_addons_checkbox->isChecked()) - { - (*config)["enabled_addons"] = ui_.set_config_enabled_addons_textedit->toPlainText().toStdString(); - } - - recorder_service_.Call("SetConfig", set_config_request); -} - -void EcalrecGuiClient::commandRequest() -{ - eCAL::pb::rec_client::CommandRequest command_request; - - QString command_string = ui_.command_request_command_combobox->currentText(); - if (command_string == "none") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_none); - } - else if (command_string == "initialize") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_initialize); - } - else if (command_string == "de_initialize") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_de_initialize); - } - else if (command_string == "start_recording") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_start_recording); - } - else if (command_string == "stop_recording") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_stop_recording); - } - else if (command_string == "save_pre_buffer") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_save_pre_buffer); - } - else if (command_string == "upload_measurement") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_upload_measurement); - } - else if (command_string == "exit") - { - command_request.set_command(eCAL::pb::rec_client::CommandRequest_Command::CommandRequest_Command_exit); - } - - auto command_params = command_request.mutable_command_params()->mutable_items(); - - if (ui_.command_request_meas_id_checkbox->isChecked()) - { - (*command_params)["meas_id"] = ui_.command_request_meas_id_lineedit->text().toStdString(); - } - - if (ui_.command_request_meas_root_dir_checkbox->isChecked()) - { - (*command_params)["meas_root_dir"] = ui_.command_request_meas_root_dir_lineedit->text().toStdString(); - } - - if (ui_.command_request_meas_name_checkbox->isChecked()) - { - (*command_params)["meas_name"] = ui_.command_request_meas_name_lineedit->text().toStdString(); - } - - if (ui_.command_request_description_checkbox->isChecked()) - { - (*command_params)["description"] = ui_.command_request_description_textedit->toPlainText().toStdString(); - } - - if (ui_.command_request_max_file_size_mib_checkbox->isChecked()) - { - (*command_params)["max_file_size_mib"] = ui_.command_request_max_file_size_mib_lineedit->text().toStdString(); - } - - if (ui_.command_one_file_per_topic_checkbox->isChecked()) - { - (*command_params)["one_file_per_topic"] = ui_.command_one_file_per_topic_lineedit->text().toStdString(); - } - - if (ui_.command_request_protocol_checkbox->isChecked()) - { - (*command_params)["protocol"] = ui_.command_request_protocol_lineedit->text().toStdString(); - } - - if (ui_.command_request_username_checkbox->isChecked()) - { - (*command_params)["username"] = ui_.command_request_username_lineedit->text().toStdString(); - } - - if (ui_.command_request_password_checkbox->isChecked()) - { - (*command_params)["password"] = ui_.command_request_password_lineedit->text().toStdString(); - } - - if (ui_.command_request_host_checkbox->isChecked()) - { - (*command_params)["host"] = ui_.command_request_host_lineedit->text().toStdString(); - } - - if (ui_.command_request_port_checkbox->isChecked()) - { - (*command_params)["port"] = ui_.command_request_port_lineedit->text().toStdString(); - } - - if (ui_.command_request_upload_path_checkbox->isChecked()) - { - (*command_params)["upload_path"] = ui_.command_request_upload_path_lineedit->text().toStdString(); - } - - if (ui_.command_request_upload_metadata_files_checkbox->isChecked()) - { - (*command_params)["upload_metadata_files"] = ui_.command_request_upload_metadata_files_lineedit->text().toStdString(); - } - - if (ui_.command_request_delete_after_upload_checkbox->isChecked()) - { - (*command_params)["delete_after_upload"] = ui_.command_request_delete_after_upload_lineedit->text().toStdString(); - } - - recorder_service_.Call("SetCommand", command_request); -} - -void EcalrecGuiClient::getStateRequest() -{ - eCAL::pb::rec_client::GetStateRequest get_state_request; - recorder_service_.Call("GetState", get_state_request); -} - - -//////////////////////////////////////////////////////////////////////////////// -//// Response //// -//////////////////////////////////////////////////////////////////////////////// - -void EcalrecGuiClient::onRecorderResponse(const struct eCAL::SServiceResponse& service_response_) -{ - QString response_string; - QTextStream response_stream(&response_string); - - switch (service_response_.call_state) - { - // service successful executed - case call_state_executed: - { - if (service_response_.method_name == "GetConfig") - { - eCAL::pb::rec_client::GetConfigResponse response; - response.ParseFromString(service_response_.response); - - response_stream << "RecorderService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - else if (service_response_.method_name == "GetState") - { - eCAL::pb::rec_client::State response; - response.ParseFromString(service_response_.response); - - response_stream << "RecorderService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - else - { - eCAL::pb::rec_client::Response response; - response.ParseFromString(service_response_.response); - response_stream << "RecorderService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - break; - } - // service execution failed - case call_state_failed: - { - eCAL::pb::rec_client::Response response; - response.ParseFromString(service_response_.response); - response_stream << "RecorderService " << service_response_.method_name.c_str() << " failed with \"" << response.error().c_str() << "\" on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - break; - } - default: - break; - } - - emit setResponseSignal(response_string); - -} - diff --git a/samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.h b/samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.h deleted file mode 100644 index 10bc273..0000000 --- a/samples/cpp/services/rec_client_service_gui/src/EcalrecGuiClient.h +++ /dev/null @@ -1,67 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#include - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100 4505 4800) -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ui_MainWindow.h" - -class EcalrecGuiClient : public QMainWindow -{ - Q_OBJECT - -public: - EcalrecGuiClient(QWidget *parent = Q_NULLPTR); - ~EcalrecGuiClient(); - -private slots: - void getConfigRequest(); - void setConfigRequest(); - void commandRequest(); - void getStateRequest(); - -signals: - void setResponseSignal(QString response); - -private: - void onRecorderResponse(const struct eCAL::SServiceResponse& service_response_); - - - - - - -private: - Ui::EcalrecGuiServiceMainWindow ui_; - - eCAL::protobuf::CServiceClient recorder_service_; -}; diff --git a/samples/cpp/services/rec_client_service_gui/src/MainWindow.ui b/samples/cpp/services/rec_client_service_gui/src/MainWindow.ui deleted file mode 100644 index 57e67d9..0000000 --- a/samples/cpp/services/rec_client_service_gui/src/MainWindow.ui +++ /dev/null @@ -1,967 +0,0 @@ - - - EcalrecGuiServiceMainWindow - - - - 0 - 0 - 1024 - 731 - - - - - 0 - 0 - - - - - :/ecalplay/APP_ICON:/ecalplay/APP_ICON - - - true - - - - - - - - - Hostname: - - - - - - - - - - - - Qt::Horizontal - - - - - 0 - 0 - - - - 2 - - - - GetConfigRequest - - - - - - No settings - - - Qt::AlignCenter - - - - - - - Send request - - - - - - - - SetConfigRequest - - - - - - - - max_pre_buffer_length_secs - - - - - - - false - - - - - - - - - - pre_buffering_enabled - - - - - - - false - - - - - - - - - - host_filter - - - - - - - false - - - - - - - record_mode - - - - - - - false - - - - - - - listed_topics - - - - - - - false - - - - - - - enabled_addons - - - - - - - false - - - - - - - - - Send request - - - - - - - - CommandRequest - - - - - - - - command - - - - - - - - 0 - 0 - - - - - none - - - - - initialize - - - - - de_initialize - - - - - start_recording - - - - - stop_recording - - - - - save_pre_buffer - - - - - upload_measurement - - - - - add_comment - - - - - exit - - - - - - - - - - - - meas_id - - - - - - - false - - - - - - - - - Start recording / Save buffer - - - - - - - - meas_name - - - - - - - false - - - - - - - meas_root_dir - - - - - - - max_file_size_mib - - - - - - - false - - - - - - - false - - - - - - - description - - - - - - - false - - - - - - - one_file_per_topic - - - - - - - false - - - - - - - - - - - - Upload measurement - - - - - - - - username - - - - - - - password - - - - - - - false - - - - - - - false - - - - - - - host - - - - - - - false - - - - - - - upload_metadata_files - - - - - - - port - - - - - - - upload_path - - - - - - - false - - - - - - - false - - - - - - - false - - - - - - - protocol - - - - - - - false - - - - - - - delete_after_upload - - - - - - - false - - - - - - - - - - - - Add comment - - - - - - comment - - - - - - - false - - - - - - - - - - Send request - - - - - - - - GetStateRequest - - - - - - No Settings - - - Qt::AlignCenter - - - - - - - Send Request - - - - - - - - - Response - - - - - - - Courier New - - - - true - - - - - - - Clear - - - - - - - - - - - - - get_config_request_button - set_config_max_pre_buffer_length_secs_checkbox - set_config_max_pre_buffer_length_secs_lineedit - set_config_pre_buffering_enabled_checkbox - set_config_pre_buffering_enabled_lineedit - set_config_host_filter_checkbox - set_config_host_filter_textedit - set_config_record_mode_checkbox - set_config_record_mode_lineedit - set_config_listed_topics_checkbox - set_config_listed_topics_textedit - set_config_enabled_addons_checkbox - set_config_enabled_addons_textedit - set_config_request_button - command_request_command_combobox - command_request_meas_id_checkbox - command_request_meas_id_lineedit - command_request_meas_root_dir_checkbox - command_request_meas_root_dir_lineedit - command_request_meas_name_checkbox - command_request_meas_name_lineedit - command_request_description_checkbox - command_request_description_textedit - command_request_max_file_size_mib_checkbox - command_request_max_file_size_mib_lineedit - get_state_request_button - command_one_file_per_topic_checkbox - command_one_file_per_topic_lineedit - command_request_protocol_checkbox - command_request_protocol_lineedit - command_request_username_checkbox - command_request_username_lineedit - command_request_password_checkbox - command_request_password_lineedit - command_request_host_checkbox - command_request_host_lineedit - command_request_port_checkbox - command_request_port_lineedit - command_request_upload_path_checkbox - command_request_upload_path_lineedit - command_request_upload_metadata_files_checkbox - command_request_upload_metadata_files_lineedit - command_request_delete_after_upload_checkbox - command_request_delete_after_upload_lineedit - command_request_comment_checkbox - command_request_comment_textedit - command_request_button - response_clear_button - response_texteedit - tabWidget - hostname_lineedit - - - - - set_config_max_pre_buffer_length_secs_checkbox - toggled(bool) - set_config_max_pre_buffer_length_secs_lineedit - setEnabled(bool) - - - 135 - 85 - - - 612 - 86 - - - - - set_config_pre_buffering_enabled_checkbox - toggled(bool) - set_config_pre_buffering_enabled_lineedit - setEnabled(bool) - - - 143 - 110 - - - 612 - 111 - - - - - set_config_host_filter_checkbox - toggled(bool) - set_config_host_filter_textedit - setEnabled(bool) - - - 86 - 135 - - - 461 - 258 - - - - - set_config_listed_topics_checkbox - toggled(bool) - set_config_listed_topics_textedit - setEnabled(bool) - - - 97 - 326 - - - 461 - 428 - - - - - set_config_record_mode_checkbox - toggled(bool) - set_config_record_mode_lineedit - setEnabled(bool) - - - 101 - 301 - - - 612 - 302 - - - - - command_request_meas_root_dir_checkbox - toggled(bool) - command_request_meas_root_dir_lineedit - setEnabled(bool) - - - 134 - 165 - - - 246 - 149 - - - - - command_request_max_file_size_mib_checkbox - toggled(bool) - command_request_max_file_size_mib_lineedit - setEnabled(bool) - - - 133 - 281 - - - 469 - 282 - - - - - command_request_description_checkbox - toggled(bool) - command_request_description_textedit - setEnabled(bool) - - - 111 - 236 - - - 285 - 249 - - - - - command_request_meas_name_checkbox - toggled(bool) - command_request_meas_name_lineedit - setEnabled(bool) - - - 100 - 189 - - - 312 - 191 - - - - - command_request_protocol_checkbox - toggled(bool) - command_request_protocol_lineedit - setEnabled(bool) - - - 118 - 342 - - - 546 - 343 - - - - - command_request_username_checkbox - toggled(bool) - command_request_username_lineedit - setEnabled(bool) - - - 122 - 368 - - - 539 - 369 - - - - - command_request_password_checkbox - toggled(bool) - command_request_password_lineedit - setEnabled(bool) - - - 163 - 394 - - - 523 - 395 - - - - - command_request_host_checkbox - toggled(bool) - command_request_host_lineedit - setEnabled(bool) - - - 142 - 420 - - - 519 - 421 - - - - - command_request_port_checkbox - toggled(bool) - command_request_port_lineedit - setEnabled(bool) - - - 163 - 446 - - - 532 - 447 - - - - - command_request_upload_path_checkbox - toggled(bool) - command_request_upload_path_lineedit - setEnabled(bool) - - - 163 - 472 - - - 519 - 473 - - - - - command_request_upload_metadata_files_checkbox - toggled(bool) - command_request_upload_metadata_files_lineedit - setEnabled(bool) - - - 163 - 498 - - - 521 - 499 - - - - - command_request_meas_id_checkbox - toggled(bool) - command_request_meas_id_lineedit - setEnabled(bool) - - - 78 - 114 - - - 345 - 115 - - - - - set_config_enabled_addons_checkbox - toggled(bool) - set_config_enabled_addons_textedit - setEnabled(bool) - - - 117 - 491 - - - 341 - 483 - - - - - command_request_comment_checkbox - toggled(bool) - command_request_comment_textedit - setEnabled(bool) - - - 73 - 587 - - - 381 - 611 - - - - - command_request_delete_after_upload_checkbox - toggled(bool) - command_request_delete_after_upload_lineedit - setEnabled(bool) - - - 148 - 668 - - - 405 - 670 - - - - - command_one_file_per_topic_checkbox - toggled(bool) - command_one_file_per_topic_lineedit - setEnabled(bool) - - - 102 - 371 - - - 216 - 516 - - - - - diff --git a/samples/cpp/services/rec_client_service_gui/src/main.cpp b/samples/cpp/services/rec_client_service_gui/src/main.cpp deleted file mode 100644 index 037e0b7..0000000 --- a/samples/cpp/services/rec_client_service_gui/src/main.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include "EcalrecGuiClient.h" -#include -#include - -int main(int argc, char *argv[]) -{ - - // Just make sure that eCAL is initialized - eCAL::Initialize(0, nullptr, "EcalRecGuiClient", eCAL::Init::Default); - - QApplication a(argc, argv); - - a.setOrganizationName ("Continental"); - a.setOrganizationDomain ("continental-corporation.com"); - a.setApplicationName ("ecalrec_gui_client"); - a.setApplicationDisplayName("eCAL Recorder GUI client"); - - EcalrecGuiClient* w = new EcalrecGuiClient(); - w->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose); - - w->show(); - int return_code = a.exec(); - - eCAL::Finalize(); - - return return_code; -} diff --git a/samples/cpp/services/rec_server_service_gui/CMakeLists.txt b/samples/cpp/services/rec_server_service_gui/CMakeLists.txt deleted file mode 100644 index 636ae85..0000000 --- a/samples/cpp/services/rec_server_service_gui/CMakeLists.txt +++ /dev/null @@ -1,106 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2018 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(rec_server_service_gui) - -find_package(eCAL REQUIRED) -find_package(Qt5 COMPONENTS - Core - Widgets -REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC OFF) # Reason for being turned off: AutoUIC will prevent VS from detecting changes in .ui files -set(CMAKE_AUTORCC OFF) # Reason for being turned off: AutoRCC will create an entirely new project in VS which clutters the solution appearance. Additionally, we cannot assign a source group to the generated .cpp files which will clutter the project. -set(CMAKE_INCLUDE_CURRENT_DIR ON) - - -set(source_files - src/rec_server_service_gui.cpp - src/rec_server_service_gui.h - src/main.cpp -) -set(qt_resource_files -) -set(ui_files - src/rec_server_service_gui.ui -) - -# compile qt resource files and ui files -qt5_add_resources(autogen_resources ${qt_resource_files}) -qt5_wrap_ui (autogen_ui ${ui_files}) - -ecal_add_sample (${PROJECT_NAME} - ${source_files} - ${qt_resource_files} - ${win32_resource_files} - ${ui_files} -) - -if(WIN32) - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") -endif(WIN32) -if(MSVC) - set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/wd4127 /wd4714") -ENDIF(MSVC) - -target_include_directories(${PROJECT_NAME} PRIVATE src) - -target_link_libraries(${PROJECT_NAME} - eCAL::core - eCAL::core_pb - eCAL::app_pb - Qt5::Widgets -) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -create_targets_protobuf() - -if(WIN32) - # Deploy Qt DLLs in the binary folder. This is necessary for starting the application from whithin the IDE without having to copy QtCore.dll, QtWidgets.dll etc. by hand each time - qt_add_windeployqt_postbuild(--no-system-d3d-compiler --no-compiler-runtime --no-opengl-sw --pdb "$") -endif(WIN32) - -# Create a source tree that mirrors the filesystem -source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" - FILES - ${source_files} - ${qt_resource_files} - ${win32_resource_files} - ${ui_files} -) - -# Also create a group for autogenerated files. The autogenerated ui files are not necessary as they are only header files. We add them anyhow, just for completeness. -source_group( autogen FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_automoc.cpp - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_autogen/mocs_compilation.cpp - ${autogen_ui} - ${autogen_resources} -) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/services) diff --git a/samples/cpp/services/rec_server_service_gui/src/main.cpp b/samples/cpp/services/rec_server_service_gui/src/main.cpp deleted file mode 100644 index c794d6f..0000000 --- a/samples/cpp/services/rec_server_service_gui/src/main.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include "rec_server_service_gui.h" -#include -#include - -int main(int argc, char *argv[]) -{ - - // Just make sure that eCAL is initialized - eCAL::Initialize(0, nullptr, "EcalRecGuiClient", eCAL::Init::Default); - - QApplication a(argc, argv); - - a.setOrganizationName ("Continental"); - a.setOrganizationDomain ("continental-corporation.com"); - a.setApplicationName ("ecalrec_gui_client"); - a.setApplicationDisplayName("eCAL Recorder GUI client"); - - RecServerServiceGui* w = new RecServerServiceGui(); - w->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose); - - w->show(); - int return_code = a.exec(); - - eCAL::Finalize(); - - return return_code; -} diff --git a/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.cpp b/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.cpp deleted file mode 100644 index 6c78a24..0000000 --- a/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2018 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 "rec_server_service_gui.h" - -#include -#include -#include - -RecServerServiceGui::RecServerServiceGui(QWidget *parent) - : QMainWindow(parent) -{ - ui_.setupUi(this); - - // initialize eCAL API - eCAL::Initialize(0, nullptr, "RecServerServiceGui"); - - // create player service client - recorder_service_.AddResponseCallback([this](const struct eCAL::SServiceResponse& service_response) {this->onRecorderResponse(service_response); }); - - connect(ui_.hostname_lineedit, &QLineEdit::editingFinished, this, [this]() {recorder_service_.SetHostName(ui_.hostname_lineedit->text().toStdString()); }); - - connect(ui_.send_request_button, &QPushButton::clicked, this, &RecServerServiceGui::sendRequest); - connect(ui_.response_clear_button, &QPushButton::clicked, ui_.response_texteedit, &QTextEdit::clear); - connect(this, &RecServerServiceGui::setResponseSignal, ui_.response_texteedit, &QTextEdit::setText ,Qt::ConnectionType::QueuedConnection); -} - -RecServerServiceGui::~RecServerServiceGui() -{} - -//////////////////////////////////////////////////////////////////////////////// -//// Request //// -//////////////////////////////////////////////////////////////////////////////// - -void RecServerServiceGui::sendRequest() -{ - if (ui_.request_get_status_radiobutton->isChecked()) getStatus(); - else if (ui_.request_load_config_file_radiobutton->isChecked()) loadConfigFile(); - else if (ui_.request_get_config_radiobutton->isChecked()) getConfig(); - else if (ui_.request_activate_radiobutton->isChecked()) activate(); - else if (ui_.request_deactivate_radiobutton->isChecked()) deActivate(); - else if (ui_.request_start_recording_radiobutton->isChecked()) startRecording(); - else if (ui_.request_stop_recording_radiobutton->isChecked()) stopRecording(); - else if (ui_.request_save_buffer_radiobutton->isChecked()) saveBuffer(); - else if (ui_.request_upload_measurement_radiobutton->isChecked()) uploadMeasurement(); - else if (ui_.request_delete_measurement_radiobutton->isChecked()) deleteMeasurement(); - else if (ui_.request_add_comment_radiobutton->isChecked()) addComment(); -} - -void RecServerServiceGui::getStatus() -{ - eCAL::pb::rec_server::GenericRequest request; - bool success = recorder_service_.Call("GetStatus", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::loadConfigFile() -{ - eCAL::pb::rec_server::LoadConfigRequest request; - request.set_config_path(ui_.request_load_config_file_param_config_path_lineedit->text().toStdString()); - bool success = recorder_service_.Call("LoadConfigFile", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::getConfig() -{ - eCAL::pb::rec_server::GenericRequest request; - bool success = recorder_service_.Call("GetConfig", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - - -void RecServerServiceGui::activate() -{ - eCAL::pb::rec_server::GenericRequest request; - bool success = recorder_service_.Call("Activate", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::deActivate() -{ - eCAL::pb::rec_server::GenericRequest request; - bool success = recorder_service_.Call("DeActivate", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::startRecording() -{ - eCAL::pb::rec_server::GenericRequest request; - bool success = recorder_service_.Call("StartRecording", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::stopRecording() -{ - eCAL::pb::rec_server::GenericRequest request; - bool success = recorder_service_.Call("StopRecording", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::saveBuffer() -{ - eCAL::pb::rec_server::GenericRequest request; - bool success = recorder_service_.Call("SaveBuffer", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::uploadMeasurement() -{ - eCAL::pb::rec_server::GenericMeasurementRequest request; - - if(!ui_.request_upload_measurement_param_meas_id_lineedit->text().isEmpty()) - { - try - { - int64_t meas_id = std::stoll(ui_.request_upload_measurement_param_meas_id_lineedit->text().toStdString()); - request.set_meas_id(meas_id); - } - catch(...) - { - QMessageBox error_message; - error_message.setIcon (QMessageBox::Icon::Critical); - error_message.setWindowTitle("Error"); - error_message.setText ("Unable to parse meas_id"); - error_message.exec(); - - return; - } - } - - bool success = recorder_service_.Call("UploadMeasurement", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::deleteMeasurement() -{ - eCAL::pb::rec_server::GenericMeasurementRequest request; - - if(!ui_.request_delete_measurement_lineedit->text().isEmpty()) - { - try - { - int64_t meas_id = std::stoll(ui_.request_delete_measurement_lineedit->text().toStdString()); - request.set_meas_id(meas_id); - } - catch(...) - { - QMessageBox error_message; - error_message.setIcon (QMessageBox::Icon::Critical); - error_message.setWindowTitle("Error"); - error_message.setText ("Unable to parse meas_id"); - error_message.exec(); - - return; - } - } - - bool success = recorder_service_.Call("DeleteMeasurement", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -void RecServerServiceGui::addComment() -{ - eCAL::pb::rec_server::AddCommentRequest request; - - if(!ui_.request_add_comment_param_meas_id_lineedit->text().isEmpty()) - { - try - { - int64_t meas_id = std::stoll(ui_.request_add_comment_param_meas_id_lineedit->text().toStdString()); - request.set_meas_id(meas_id); - } - catch(...) - { - QMessageBox error_message; - error_message.setIcon (QMessageBox::Icon::Critical); - error_message.setWindowTitle("Error"); - error_message.setText ("Unable to parse meas_id"); - error_message.exec(); - - return; - } - } - - request.set_comment(ui_.request_add_comment_param_comment_textedit->toPlainText().toStdString()); - - bool success = recorder_service_.Call("AddComment", request); - - if (!success) - ui_.response_texteedit->setText("Unable to call service"); -} - -//////////////////////////////////////////////////////////////////////////////// -//// Response //// -//////////////////////////////////////////////////////////////////////////////// - -void RecServerServiceGui::onRecorderResponse(const struct eCAL::SServiceResponse& service_response_) -{ - QString response_string; - QTextStream response_stream(&response_string); - - switch (service_response_.call_state) - { - - // service successful executed - case call_state_executed: - { - if ((service_response_.method_name == "StartRecording") - || (service_response_.method_name == "SaveBuffer")) - { - eCAL::pb::rec_server::JobStartedResponse response; - response.ParseFromString(service_response_.response); - - response_stream << "RecorderService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - else if (service_response_.method_name == "GetStatus") - { - eCAL::pb::rec_server::Status response; - response.ParseFromString(service_response_.response); - - response_stream << "RecorderService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - else if (service_response_.method_name == "GetConfig") - { - eCAL::pb::rec_server::RecServerConfig response; - response.ParseFromString(service_response_.response); - - response_stream << "RecorderService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - else - { - eCAL::pb::rec_server::ServiceResult response; - response.ParseFromString(service_response_.response); - response_stream << "RecorderService " << service_response_.method_name.c_str() << " called successfully on host " << service_response_.host_name.c_str() << "\n"; - response_stream << "------------------------------------------------\n\n"; - response_stream << response.DebugString().c_str(); - } - break; - } - - // service execution failed - case call_state_failed: - { - //eCAL::pb::Response response; - //response.ParseFromString(response_); - //response_stream << "RecorderService " << service_response_.method_name.c_str() << " failed with \"" << response.error().c_str() << "\" on host " << service_response_.host_name.c_str() << "\n"; - //response_stream << "------------------------------------------------\n\n"; - //response_stream << response.DebugString().c_str(); - response_stream << "Service call failed.\n"; - break; - } - default: - break; - } - - emit setResponseSignal(response_string); - -} - diff --git a/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.h b/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.h deleted file mode 100644 index 4c10918..0000000 --- a/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.h +++ /dev/null @@ -1,77 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#pragma once - -#include - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100 4505 4800) -#endif -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ui_rec_server_service_gui.h" - -class RecServerServiceGui : public QMainWindow -{ - Q_OBJECT - -public: - RecServerServiceGui(QWidget *parent = Q_NULLPTR); - ~RecServerServiceGui(); - -private slots: - void sendRequest(); - - void getStatus(); - void loadConfigFile(); - void getConfig(); - void activate(); - void deActivate(); - void startRecording(); - void stopRecording(); - void saveBuffer(); - void uploadMeasurement(); - void deleteMeasurement(); - void addComment(); - -signals: - void setResponseSignal(QString response); - -private: - void onRecorderResponse(const struct eCAL::SServiceResponse& service_response_); - - - - - - -private: - Ui::RecServerServiceGui ui_; - - eCAL::protobuf::CServiceClient recorder_service_; -}; diff --git a/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.ui b/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.ui deleted file mode 100644 index 79e6fa6..0000000 --- a/samples/cpp/services/rec_server_service_gui/src/rec_server_service_gui.ui +++ /dev/null @@ -1,442 +0,0 @@ - - - RecServerServiceGui - - - - 0 - 0 - 772 - 518 - - - - - 0 - 0 - - - - - :/ecalplay/APP_ICON:/ecalplay/APP_ICON - - - true - - - - - - - - - Hostname: - - - - - - - - - - - - Qt::Horizontal - - - - Request - - - - - - false - - - - - - - false - - - config_path: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - - - - - StopRecording - - - - - - - SaveBuffer - - - - - - - DeleteMeasurement - - - - - - - false - - - Qt::ImhDigitsOnly|Qt::ImhPreferNumbers - - - - - - - false - - - comment: - - - Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing - - - - - - - GetStatus - - - true - - - - - - - StartRecording - - - - - - - UploadMeasurement - - - - - - - LoadConfigFile - - - - - - - false - - - meas_id: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - Qt::ImhDigitsOnly - - - - - - - false - - - meas_id: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Activate - - - - - - - Send request - - - - - - - AddComment - - - - - - - false - - - meas_id: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - - - - - DeActivate - - - - - - - GetConfig - - - - - - - - Response - - - - - - - Courier New - - - - true - - - - - - - Clear - - - - - - - - - - - - - - - request_load_config_file_radiobutton - toggled(bool) - label - setEnabled(bool) - - - 126 - 101 - - - 202 - 102 - - - - - request_load_config_file_radiobutton - toggled(bool) - request_load_config_file_param_config_path_lineedit - setEnabled(bool) - - - 69 - 101 - - - 468 - 102 - - - - - request_upload_measurement_radiobutton - toggled(bool) - label_3 - setEnabled(bool) - - - 135 - 247 - - - 202 - 248 - - - - - request_upload_measurement_radiobutton - toggled(bool) - request_upload_measurement_param_meas_id_lineedit - setEnabled(bool) - - - 73 - 247 - - - 458 - 248 - - - - - request_add_comment_radiobutton - toggled(bool) - label_4 - setEnabled(bool) - - - 112 - 299 - - - 202 - 300 - - - - - request_add_comment_radiobutton - toggled(bool) - request_add_comment_param_meas_id_lineedit - setEnabled(bool) - - - 84 - 299 - - - 468 - 300 - - - - - request_add_comment_radiobutton - toggled(bool) - label_5 - setEnabled(bool) - - - 91 - 299 - - - 196 - 324 - - - - - request_add_comment_radiobutton - toggled(bool) - request_add_comment_param_comment_textedit - setEnabled(bool) - - - 100 - 299 - - - 275 - 324 - - - - - request_delete_measurement_radiobutton - toggled(bool) - label_6 - setEnabled(bool) - - - 135 - 273 - - - 202 - 274 - - - - - request_delete_measurement_radiobutton - toggled(bool) - request_delete_measurement_lineedit - setEnabled(bool) - - - 57 - 273 - - - 468 - 274 - - - - - diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 0000000..cd7f135 --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,742 @@ +# ========================= 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 ================================= + +project(core VERSION ${eCAL_VERSION_STRING}) + +find_package(asio REQUIRED) + +if (ECAL_CORE_CONFIG_INIFILE) + find_package(simpleini REQUIRED) +endif() + +if (ECAL_CORE_COMMAND_LINE) + find_package(tclap REQUIRED) +endif() + +if (ECAL_CORE_TRANSPORT_TCP) + find_package(tcp_pubsub REQUIRED) +endif() + +if (ECAL_CORE_NPCAP_SUPPORT) + find_package(udpcap REQUIRED) +endif() + +# If we're currently doing a build within a git repository, we will configure the header files. +# Else, (e.g. for source packages such as debian source packages) we will use a preconfigured file. +# If there is really no information available, it will generate a dummy version file 0.0.0 +if (IS_GIT_TREE OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/ecal/ecal_defs.h") + configure_file(src/ecal_defs.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/ecal/ecal_defs.h" @ONLY) +endif (IS_GIT_TREE OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/ecal/ecal_defs.h") + +if(UNIX) + include (CheckSymbolExists) + set(CMAKE_REQUIRED_DEFINITIONS "-D__USE_GNU" + "-D_GNU_SOURCE") + set(CMAKE_REQUIRED_LIBRARIES "pthread") + check_symbol_exists(pthread_mutex_clocklock "pthread.h" ECAL_HAS_CLOCKLOCK_MUTEX) + check_symbol_exists(pthread_mutexattr_setrobust "pthread.h" ECAL_HAS_ROBUST_MUTEX) + unset(CMAKE_REQUIRED_DEFINITIONS) + unset(CMAKE_REQUIRED_LIBRARIES) + if(NOT ECAL_HAS_ROBUST_MUTEX) + if(NOT ECAL_HAS_CLOCKLOCK_MUTEX) + message(WARNING "This OS does not support robust mutexes with monotonic clock which can cause dead locks under certain circumstances. (e.g. memfile monitoring).") + else() + message(WARNING "This OS does not support robust mutexes which can cause dead locks under certain circumstances (e.g. memfile monitoring).") + endif() + endif() +endif() + +###################################### +# config +###################################### +set(ecal_config_src + src/config/ecal_config.cpp + src/config/ecal_config_reader.cpp + src/config/ecal_config_reader.h + src/config/ecal_config_reader_hlp.h +) + +###################################### +# io/mtx +###################################### +if(ECAL_CORE_REGISTRATION_SHM OR ECAL_CORE_TRANSPORT_SHM) + set(ecal_io_mtx_src + src/io/mtx/ecal_named_mutex.cpp + src/io/mtx/ecal_named_mutex.h + src/io/mtx/ecal_named_mutex_base.h + ) + + # io/mtx/linux + if(UNIX) + set(ecal_io_mtx_linux_src + src/io/mtx/linux/ecal_named_mutex_impl.cpp + $<$,$>:src/io/mtx/linux/ecal_named_mutex_robust_clocklock_impl.cpp> + src/io/mtx/linux/ecal_named_mutex_impl.h + $<$,$>:src/io/mtx/linux/ecal_named_mutex_robust_clocklock_impl.h> + ) + endif() + + # io/mtx/win32 + if(WIN32) + set(ecal_io_mtx_win_src + src/io/mtx/win32/ecal_named_mutex_impl.cpp + src/io/mtx/win32/ecal_named_mutex_impl.h + ) + endif() +endif() + +###################################### +# io/shm +###################################### +if(ECAL_CORE_REGISTRATION_SHM OR ECAL_CORE_TRANSPORT_SHM) + set(ecal_io_shm_src + src/io/shm/ecal_memfile.cpp + src/io/shm/ecal_memfile_db.cpp + src/io/shm/ecal_memfile_naming.cpp + src/io/shm/ecal_memfile_pool.cpp + src/io/shm/ecal_memfile_sync.cpp + src/io/shm/ecal_memfile.h + src/io/shm/ecal_memfile_db.h + src/io/shm/ecal_memfile_header.h + src/io/shm/ecal_memfile_info.h + src/io/shm/ecal_memfile_naming.h + src/io/shm/ecal_memfile_os.h + src/io/shm/ecal_memfile_pool.h + src/io/shm/ecal_memfile_sync.h + ) + + # io/shm/linux + if(UNIX) + set(ecal_io_shm_linux_src + src/io/shm/linux/ecal_memfile_os.cpp + ) + endif() + + # io/shm/win32 + if(WIN32) + set(ecal_io_shm_win_src + src/io/shm/win32/ecal_memfile_os.cpp + ) + endif() +endif() + +###################################### +# io/udp +###################################### +# io/udp/fragmentation +set(ecal_io_udp_fragmentation_src + src/io/udp/fragmentation/msg_type.h + src/io/udp/fragmentation/rcv_fragments.cpp + src/io/udp/fragmentation/rcv_fragments.h + src/io/udp/fragmentation/snd_fragments.cpp + src/io/udp/fragmentation/snd_fragments.h +) + +# io/udp/sendreceive (npcap) +if(ECAL_CORE_NPCAP_SUPPORT) +set(ecal_io_udp_sendreceive_src_npcap + src/io/udp/sendreceive/udp_receiver_npcap.cpp + src/io/udp/sendreceive/udp_receiver_npcap.h +) +endif() + +# io/udp/sendreceive +set(ecal_io_udp_sendreceive_src + src/io/udp/sendreceive/udp_receiver.cpp + src/io/udp/sendreceive/udp_receiver.h + src/io/udp/sendreceive/udp_receiver_asio.cpp + src/io/udp/sendreceive/udp_receiver_asio.h + src/io/udp/sendreceive/udp_sender.cpp + src/io/udp/sendreceive/udp_sender.h + ${ecal_io_udp_sendreceive_src_npcap} +) + +# io/udp/sendreceive/linux +if(UNIX) + set(ecal_io_udp_sendreceive_linux_src + src/io/udp/sendreceive/linux/socket_os.h +) +endif() + +# io/udp/sendreceive/win32 +if (WIN32) + set(ecal_io_udp_sendreceive_win_src + src/io/udp/sendreceive/win32/socket_os.h +) +endif() + +# io/udp +set(ecal_io_udp_src + src/io/udp/ecal_udp_configurations.cpp + src/io/udp/ecal_udp_configurations.h + src/io/udp/ecal_udp_sample_receiver.cpp + src/io/udp/ecal_udp_sample_receiver.h + src/io/udp/ecal_udp_sample_sender.cpp + src/io/udp/ecal_udp_sample_sender.h + src/io/udp/ecal_udp_topic2mcast.h +) + +###################################### +# logging +###################################### +set(ecal_logging_src + src/logging/ecal_log.cpp + src/logging/ecal_log_impl.cpp + src/logging/ecal_log_impl.h +) + +###################################### +# monitoring +###################################### +if(ECAL_CORE_MONITORING) +set(ecal_monitoring_src + src/monitoring/ecal_monitoring_def.cpp + src/monitoring/ecal_monitoring_def.h + src/monitoring/ecal_monitoring_impl.cpp + src/monitoring/ecal_monitoring_impl.h +) +endif() + +###################################### +# pubsub +###################################### +if(ECAL_CORE_PUBLISHER) + set(ecal_pub_src + src/pubsub/ecal_publisher.cpp + src/pubsub/ecal_pubgate.cpp + src/pubsub/ecal_pubgate.h + ) +endif() + +if(ECAL_CORE_SUBSCRIBER) + set(ecal_sub_src + src/pubsub/ecal_subscriber.cpp + src/pubsub/ecal_subgate.cpp + src/pubsub/ecal_subgate.h + ) +endif() + +###################################### +# readwrite +###################################### +if(ECAL_CORE_PUBLISHER) + set(ecal_writer_src + src/readwrite/ecal_writer.cpp + src/readwrite/ecal_writer.h + src/readwrite/ecal_writer_base.h + src/readwrite/ecal_writer_buffer_payload.h + src/readwrite/ecal_writer_data.h + src/readwrite/ecal_writer_info.h + ) + if(ECAL_CORE_TRANSPORT_UDP) + list(APPEND ecal_writer_src + src/readwrite/udp/ecal_writer_udp_mc.cpp + src/readwrite/udp/ecal_writer_udp_mc.h + ) + endif() + if(ECAL_CORE_TRANSPORT_TCP) + list(APPEND ecal_writer_src + src/readwrite/tcp/ecal_tcp_pubsub_logger.h + src/readwrite/tcp/ecal_writer_tcp.cpp + src/readwrite/tcp/ecal_writer_tcp.h + ) + endif() + if(ECAL_CORE_TRANSPORT_SHM) + list(APPEND ecal_writer_src + src/readwrite/shm/ecal_writer_shm.cpp + src/readwrite/shm/ecal_writer_shm.h + ) + endif() +endif() + +if(ECAL_CORE_SUBSCRIBER) + set(ecal_reader_src + src/readwrite/ecal_reader.cpp + src/readwrite/ecal_reader.h + src/readwrite/ecal_reader_layer.h + ) + if(ECAL_CORE_TRANSPORT_UDP) + list(APPEND ecal_reader_src + src/readwrite/udp/ecal_reader_udp_mc.cpp + src/readwrite/udp/ecal_reader_udp_mc.h + ) + endif() + if(ECAL_CORE_TRANSPORT_TCP) + list(APPEND ecal_reader_src + src/readwrite/tcp/ecal_reader_tcp.cpp + src/readwrite/tcp/ecal_reader_tcp.h + ) + endif() + if(ECAL_CORE_TRANSPORT_SHM) + list(APPEND ecal_reader_src + src/readwrite/shm/ecal_reader_shm.cpp + src/readwrite/shm/ecal_reader_shm.h + ) + endif() +endif() + +###################################### +# registration +###################################### +if (ECAL_CORE_REGISTRATION) + set(ecal_registration_src + src/registration/ecal_registration_provider.cpp + src/registration/ecal_registration_provider.h + src/registration/ecal_registration_receiver.cpp + src/registration/ecal_registration_receiver.h + ) + if(ECAL_CORE_REGISTRATION_SHM) + list(APPEND ecal_registration_src + src/registration/ecal_registration_receiver_shm.cpp + src/registration/ecal_registration_receiver_shm.h + src/registration/shm/ecal_memfile_broadcast.cpp + src/registration/shm/ecal_memfile_broadcast.h + src/registration/shm/ecal_memfile_broadcast_reader.cpp + src/registration/shm/ecal_memfile_broadcast_reader.h + src/registration/shm/ecal_memfile_broadcast_writer.cpp + src/registration/shm/ecal_memfile_broadcast_writer.h + src/registration/shm/relocatable_circular_queue.h + ) + endif() +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/ecal_serialize_common.cpp + src/serialization/ecal_serialize_common.h + src/serialization/ecal_serialize_logging.cpp + src/serialization/ecal_serialize_logging.h + src/serialization/ecal_serialize_monitoring.cpp + src/serialization/ecal_serialize_monitoring.h + src/serialization/ecal_serialize_sample_payload.cpp + src/serialization/ecal_serialize_sample_payload.h + src/serialization/ecal_serialize_sample_registration.cpp + src/serialization/ecal_serialize_sample_registration.h + src/serialization/ecal_serialize_service.cpp + src/serialization/ecal_serialize_service.h + src/serialization/ecal_struct_logging.h + src/serialization/ecal_struct_sample_common.h + src/serialization/ecal_struct_sample_payload.h + src/serialization/ecal_struct_sample_registration.h + src/serialization/ecal_struct_service.h +) + +###################################### +# service +###################################### +if(ECAL_CORE_SERVICE) + set(ecal_service_src + src/service/ecal_clientgate.cpp + src/service/ecal_clientgate.h + src/service/ecal_servicegate.cpp + src/service/ecal_servicegate.h + src/service/ecal_service_client.cpp + src/service/ecal_service_client_impl.cpp + src/service/ecal_service_client_impl.h + src/service/ecal_service_server.cpp + src/service/ecal_service_server_impl.cpp + src/service/ecal_service_server_impl.h + src/service/ecal_service_singleton_manager.cpp + src/service/ecal_service_singleton_manager.h + ) +endif() + +###################################### +# time +###################################### +set(ecal_time_src + src/time/ecal_time.cpp + src/time/ecal_timer.cpp +) +if(ECAL_CORE_TIMEPLUGIN) + list(APPEND ecal_time_src + src/time/ecal_timegate.cpp + src/time/ecal_timegate.h + ) +endif() + +###################################### +# util +###################################### +set(ecal_util_src + src/util/ecal_expmap.h + src/util/ecal_thread.h + src/util/getenvvar.h +) +if (ECAL_CORE_COMMAND_LINE) + list(APPEND ecal_util_src + src/util/advanced_tclap_output.cpp + src/util/advanced_tclap_output.h + ) +endif() + + +###################################### +# common +###################################### +set(ecal_cmn_src + src/ecal.cpp + src/ecal_def.h + src/ecal_def_ini.h + src/ecal_descgate.cpp + src/ecal_descgate.h + src/ecal_event.cpp + src/ecal_event.h + src/ecal_eventhandle.h + src/ecal_global_accessors.cpp + src/ecal_global_accessors.h + src/ecal_globals.cpp + src/ecal_globals.h + src/ecal_process.cpp + src/ecal_sample_to_topicinfo.h + src/ecal_util.cpp + src/ecal_win_main.h + src/ecalc.cpp +) +if (WIN32) + list(APPEND ecal_cmn_src + src/ecal_win_main.h + ) +endif() + +###################################### +# c interface +###################################### +set(ecal_c_src + src/ecalc.cpp +) + +###################################### +# windows dll +###################################### +if(WIN32) + set(ecal_c_win_src + src/win32/dll/dllmain.cpp + src/win32/dll/ecal.rc + ) +endif() + +###################################### +# public header +###################################### +set(ecal_header_cmn + include/ecal/types/monitoring.h + include/ecal/ecal.h + include/ecal/ecal_callback.h + include/ecal/ecal_client.h + include/ecal/ecal_config.h + include/ecal/ecal_core.h + include/ecal/ecal_deprecate.h + include/ecal/ecal_init.h + include/ecal/ecal_log.h + include/ecal/ecal_log_level.h + include/ecal/ecal_os.h + include/ecal/ecal_payload_writer.h + include/ecal/ecal_monitoring.h + include/ecal/ecal_process.h + include/ecal/ecal_process_severity.h + include/ecal/ecal_publisher.h + include/ecal/ecal_server.h + include/ecal/ecal_service_info.h + include/ecal/ecal_subscriber.h + include/ecal/ecal_time.h + include/ecal/ecal_timer.h + include/ecal/ecal_tlayer.h + include/ecal/ecal_types.h + include/ecal/ecal_util.h + include/ecal/ecalc.h + include/ecal/ecalc_types.h +) + +set(ecal_header_cimpl + include/ecal/cimpl/ecal_callback_cimpl.h + include/ecal/cimpl/ecal_client_cimpl.h + include/ecal/cimpl/ecal_core_cimpl.h + include/ecal/cimpl/ecal_init_cimpl.h + include/ecal/cimpl/ecal_log_cimpl.h + 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_server_cimpl.h + include/ecal/cimpl/ecal_service_cimpl.h + include/ecal/cimpl/ecal_service_info_cimpl.h + include/ecal/cimpl/ecal_subscriber_cimpl.h + include/ecal/cimpl/ecal_time_cimpl.h + include/ecal/cimpl/ecal_timer_cimpl.h + include/ecal/cimpl/ecal_util_cimpl.h +) + +set(ecal_header_msg + include/ecal/msg/protobuf/client.h + include/ecal/msg/protobuf/dynamic_publisher.h + include/ecal/msg/protobuf/dynamic_subscriber.h + include/ecal/msg/protobuf/ecal_proto_dyn.h + include/ecal/msg/protobuf/ecal_proto_hlp.h + include/ecal/msg/protobuf/publisher.h + include/ecal/msg/protobuf/server.h + include/ecal/msg/protobuf/subscriber.h + include/ecal/msg/string/publisher.h + include/ecal/msg/string/subscriber.h + include/ecal/msg/dynamic.h + include/ecal/msg/publisher.h + include/ecal/msg/subscriber.h +) + +set(ecal_header_public + ${ecal_header_cmn} + ${ecal_header_cimpl} + ${ecal_header_msg} +) + +ecal_add_ecal_shared_library(${PROJECT_NAME} + ${ecal_config_src} + ${ecal_io_mtx_src} + ${ecal_io_mtx_linux_src} + ${ecal_io_mtx_win_src} + ${ecal_io_shm_src} + ${ecal_io_shm_linux_src} + ${ecal_io_shm_win_src} + ${ecal_io_udp_fragmentation_src} + ${ecal_io_udp_sendreceive_src} + ${ecal_io_udp_src} + ${ecal_io_udp_sendreceive_linux_src} + ${ecal_io_udp_sendreceive_win_src} + ${ecal_logging_src} + ${ecal_monitoring_src} + ${ecal_pub_src} + ${ecal_sub_src} + ${ecal_writer_src} + ${ecal_reader_src} + ${ecal_registration_src} + ${ecal_serialization_src} + ${ecal_service_src} + ${ecal_time_src} + ${ecal_util_src} + ${ecal_cmn_src} + ${ecal_header_public} + ${CMAKE_CURRENT_BINARY_DIR}/include/ecal/ecal_defs.h +) + +if(UNIX) + set_source_files_properties(src/util/convert_utf.cpp PROPERTIES COMPILE_FLAGS -Wno-implicit-fallthrough) +endif() + +ecal_add_ecal_shared_library(${PROJECT_NAME}_c ${ecal_c_src} ${ecal_c_win_src}) + +add_library(eCAL::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +add_library(eCAL::${PROJECT_NAME}_c ALIAS ${PROJECT_NAME}_c) + +target_link_libraries(${PROJECT_NAME}_c ${PROJECT_NAME}) + +target_compile_definitions(${PROJECT_NAME}_c + INTERFACE ECAL_C_DLL + PUBLIC + ASIO_STANDALONE + ASIO_DISABLE_VISIBILITY + PRIVATE + eCAL_EXPORTS + ECAL_NO_DEPRECATION_WARNINGS +) + +target_compile_definitions(${PROJECT_NAME} + PUBLIC + ASIO_STANDALONE + ASIO_DISABLE_VISIBILITY + PRIVATE + eCAL_EXPORTS + $<$:ECAL_HAS_CLOCKLOCK_MUTEX> + $<$:ECAL_HAS_ROBUST_MUTEX> + $<$:ECAL_USE_CLOCKLOCK_MUTEX> + ECAL_NO_DEPRECATION_WARNINGS +) + +set(ECAL_CORE_FEATURES + ECAL_CORE_CONFIG_INIFILE + ECAL_CORE_COMMAND_LINE + ECAL_CORE_REGISTRATION + ECAL_CORE_MONITORING + ECAL_CORE_PUBLISHER + ECAL_CORE_SUBSCRIBER + ECAL_CORE_SERVICE + ECAL_CORE_TIMEPLUGIN + ECAL_CORE_REGISTRATION_SHM + ECAL_CORE_TRANSPORT_UDP + ECAL_CORE_TRANSPORT_TCP + ECAL_CORE_TRANSPORT_SHM + ECAL_CORE_NPCAP_SUPPORT +) + +foreach(CORE_FEATURE ${ECAL_CORE_FEATURES}) + if(${CORE_FEATURE}) + target_compile_definitions(${PROJECT_NAME} + PRIVATE ${CORE_FEATURE}) + target_compile_definitions(${PROJECT_NAME}_c + PRIVATE ${CORE_FEATURE}) + endif() +endforeach() + +if(ECAL_CORE_CONFIG_INIFILE) + target_link_libraries(${PROJECT_NAME} + PRIVATE + simpleini::simpleini + ) +endif() + +if(ECAL_CORE_COMMAND_LINE) + target_link_libraries(${PROJECT_NAME} + PRIVATE + tclap::tclap + ) +endif() + +if(ECAL_CORE_SERVICE) + target_link_libraries(${PROJECT_NAME} + PRIVATE + ecal_service + ) +endif() + +if(ECAL_CORE_TRANSPORT_TCP) + target_link_libraries(${PROJECT_NAME} + PRIVATE + tcp_pubsub::tcp_pubsub + ) +endif() + +if(ECAL_CORE_NPCAP_SUPPORT) + target_link_libraries(${PROJECT_NAME} + PRIVATE + udpcap::udpcap + ) +endif() + +target_include_directories(${PROJECT_NAME} + PRIVATE + $ + $ + $ + PUBLIC + $ + $ + $ +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + $<$,$>>:dl> + $<$,$>,$>>:rt> + $<$:util> + $<$:iphlpapi> + $<$:psapi> + $<$:shlwapi.lib> + $<$:winmm> + $<$:ws2_32> + $<$:wsock32> + $<$:socket> + asio::asio + eCAL::ecal-utils +) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core) +set_property(TARGET ${PROJECT_NAME}_c PROPERTY FOLDER core) + +ecal_install_ecal_shared_library(${PROJECT_NAME}_c) +ecal_install_ecal_shared_library(${PROJECT_NAME}) + +install(DIRECTORY + "include/" DESTINATION "${INSTALL_INCLUDE_DIR}" COMPONENT sdk + FILES_MATCHING PATTERN "*.h") + +#Install generated ecal_defs.h file +install(DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}/include/" DESTINATION "${INSTALL_INCLUDE_DIR}" COMPONENT sdk + FILES_MATCHING PATTERN "*.h") + + +if(NOT ${CMAKE_VERSION} VERSION_LESS "3.8.0") + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${ecal_config_src} + ${ecal_io_mtx_src} + ${ecal_io_mtx_linux_src} + ${ecal_io_mtx_win_src} + ${ecal_io_shm_src} + ${ecal_io_shm_linux_src} + ${ecal_io_shm_win_src} + ${ecal_io_udp_fragmentation_src} + ${ecal_io_udp_sendreceive_src} + ${ecal_io_udp_src} + ${ecal_io_udp_sendreceive_linux_src} + ${ecal_io_udp_sendreceive_win_src} + ${ecal_logging_src} + ${ecal_monitoring_src} + ${ecal_pub_src} + ${ecal_sub_src} + ${ecal_writer_src} + ${ecal_writer_udp_src} + ${ecal_reader_src} + ${ecal_reader_udp_src} + ${ecal_registration_src} + ${ecal_serialization_src} + ${ecal_service_src} + ${ecal_time_src} + ${ecal_util_src} + ${ecal_cmn_src} + ${ecal_c_src} + ${ecal_c_win_src} + ${ecal_header_public} + ) +endif() + +# eCAL Process stub +if(UNIX) + set(PROJECT_NAME_PROCESS_STUB process_stub) + + ecal_add_app_console(${PROJECT_NAME_PROCESS_STUB} + src/ecal_process_stub.cpp + src/ecal_process_stub.h + ) + + ecal_install_app(${PROJECT_NAME_PROCESS_STUB}) + + set_property(TARGET ${PROJECT_NAME_PROCESS_STUB} PROPERTY FOLDER ecal/core) +endif(UNIX) diff --git a/ecal/core/cfg/CMakeLists.txt b/src/core/cfg/CMakeLists.txt similarity index 100% rename from ecal/core/cfg/CMakeLists.txt rename to src/core/cfg/CMakeLists.txt diff --git a/src/core/cfg/ecal.ini b/src/core/cfg/ecal.ini new file mode 100644 index 0000000..f5a9a94 --- /dev/null +++ b/src/core/cfg/ecal.ini @@ -0,0 +1,190 @@ +; -------------------------------------------------- +; NETWORK SETTINGS +; -------------------------------------------------- +; network_enabled = true / false true = all eCAL components communicate over network boundaries +; false = local host only communication +; +; multicast_config_version = v1 / v2 UDP configuration version (Since eCAL 5.12.) +; v1: default behavior +; v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups +; multicast_group = 239.0.0.1 UDP multicast group base +; All registration and logging is sent on this address +; multicast_mask = 0.0.0.1-0.0.0.255 v1: Mask maximum number of dynamic multicast group +; 255.0.0.0-255.255.255.255 v2: masks are now considered like routes masking +; +; multicast_port = 14000 + x UDP multicast port number (eCAL will use at least the 2 following port +; numbers too, so please modify in steps of 10 (e.g. 1010, 1020 ...) +; +; multicast_ttl = 0 + x UDP ttl value, also known as hop limit, is used in determining +; the intermediate routers being traversed towards the destination +; +; multicast_sndbuf = 1024 * x UDP send buffer in bytes +; +; multicast_rcvbuf = 1024 * x UDP receive buffer in bytes +; +; multicast_join_all_if = false Linux specific setting to enable joining multicast groups on all network interfacs +; independent of their link state. Enabling this makes sure that eCAL processes +; receive data if they are started before network devices are up and running. +; +; shm_rec_enabled = true Enable to receive on eCAL shared memory layer +; tcp_rec_enabled = true Enable to receive on eCAL tcp layer +; udp_mc_rec_enabled = true Enable to receive on eCAL udp multicast layer +; +; npcap_enabled = false Enable to receive UDP traffic with the Npcap based receiver +; +; tcp_pubsub_num_executor_reader = 4 Tcp_pubsub reader amount of threads that shall execute workload +; tcp_pubsub_num_executor_writer = 4 Tcp_pubsub writer amount of threads that shall execute workload +; tcp_pubsub_max_reconnections = 5 Tcp_pubsub reconnection attemps the session will try to reconnect in +; case of an issue (a negative value means infinite reconnection attemps) +; +; host_group_name = Common host group name that enables interprocess mechanisms across +; (virtual) host borders (e.g, Docker); by default equivalent to local host name +; -------------------------------------------------- + +[network] +network_enabled = false +multicast_config_version = v1 +multicast_group = 239.0.0.1 +multicast_mask = 0.0.0.15 +multicast_port = 14000 +multicast_ttl = 2 +multicast_sndbuf = 5242880 +multicast_rcvbuf = 5242880 + +multicast_join_all_if = false + +shm_rec_enabled = true +tcp_rec_enabled = true +udp_mc_rec_enabled = true + +npcap_enabled = false + +tcp_pubsub_num_executor_reader = 4 +tcp_pubsub_num_executor_writer = 4 +tcp_pubsub_max_reconnections = 5 + +host_group_name = + +; -------------------------------------------------- +; COMMON SETTINGS +; -------------------------------------------------- +; registration_timeout = 60000 Timeout for topic registration in ms (internal) +; registration_refresh = 1000 Topic registration refresh cylce (has to be smaller then registration timeout !) + +; -------------------------------------------------- +[common] +registration_timeout = 60000 +registration_refresh = 1000 + +; -------------------------------------------------- +; TIME SETTINGS +; -------------------------------------------------- +; timesync_module_rt = "ecaltime-localtime" Time synchronisation interface name (dynamic library) +; The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so) +; +; Available modules are: +; - ecaltime-localtime local system time without synchronization +; - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux +; (device configuration in ecaltime.ini) +; -------------------------------------------------- +[time] +timesync_module_rt = "" + +; --------------------------------------------- +; PROCESS SETTINGS +; --------------------------------------------- +; +; terminal_emulator = /usr/bin/x-terminal-emulator -e command for starting applications with an external terminal emulator. If empty, the command will be ignored. Ignored on Windows. +; e.g. /usr/bin/x-terminal-emulator -e +; /usr/bin/gnome-terminal -x +; /usr/bin/xterm -e +; +; --------------------------------------------- +[process] +terminal_emulator = + +; -------------------------------------------------- +; PUBLISHER SETTINGS +; -------------------------------------------------- +; use_shm = 0, 1, 2 Use shared memory transport layer (0 = off, 1 = on, 2 = auto, default = 2) +; use_tcp = 0, 1, 2 Use tcp transport layer (0 = off, 1 = on, 2 = auto, default = 0) +; use_udp_mc = 0, 1, 2 Use udp multicast transport layer (0 = off, 1 = on, 2 = auto, default = 2) +; +; memfile_minsize = x * 4096 kB Default memory file size for new publisher +; +; memfile_reserve = 50 .. x % Dynamic file size reserve before recreating memory file if topic size changes +; +; memfile_ack_timeout = 0 .. x ms Publisher timeout for ack event from subscriber that memory file content is processed +; +; memfile_buffer_count = 1 .. x Number of parallel used memory file buffers for 1:n publish/subscribe ipc connections (default = 1) +; memfile_zero_copy = 0, 1 Allow matching subscriber to access memory file without copying its content in advance (blocking mode) +; +; share_ttype = 0, 1 Share topic type via registration layer +; share_tdesc = 0, 1 Share topic description via registration layer (switch off to disable reflection) +; -------------------------------------------------- +[publisher] +use_shm = 2 +use_tcp = 0 +use_udp_mc = 2 + +memfile_minsize = 4096 +memfile_reserve = 50 +memfile_ack_timeout = 0 +memfile_buffer_count = 1 +memfile_zero_copy = 0 + +share_ttype = 1 +share_tdesc = 1 + +; -------------------------------------------------- +; SERVICE SETTINGS +; -------------------------------------------------- +; protocol_v0 = 0, 1 Support service protocol v0, eCAL 5.11 and older (0 = off, 1 = on) +; protocol_v1 = 0, 1 Support service protocol v1, eCAL 5.12 and newer (0 = off, 1 = on) +; -------------------------------------------------- +[service] +protocol_v0 = 1 +protocol_v1 = 1 + +; -------------------------------------------------- +; MONITORING SETTINGS +; -------------------------------------------------- +; timeout = 1000 + (x * 1000) Timeout for topic monitoring in ms +; filter_excl = __.* Topics blacklist as regular expression (will not be monitored) +; filter_incl = Topics whitelist as regular expression (will be monitored only) +; filter_log_con = info, warning, error, fatal Log messages logged to console (all, info, warning, error, fatal, debug1, debug2, debug3, debug4) +; filter_log_file = Log messages to logged into file system +; filter_log_udp = info, warning, error, fatal Log messages logged via udp network +; -------------------------------------------------- +[monitoring] +timeout = 5000 +filter_excl = __.* +filter_incl = +filter_log_con = info, warning, error, fatal +filter_log_file = +filter_log_udp = info, warning, error, fatal + +; -------------------------------------------------- +; SYS SETTINGS +; -------------------------------------------------- +; filter_excl = App1,App2 Apps blacklist to be excluded when importing tasks from cloud +; -------------------------------------------------- +[sys] +filter_excl = ^eCALSysClient$|^eCALSysGUI$|^eCALSys$ + +; -------------------------------------------------- +; EXPERIMENTAL SETTINGS +; -------------------------------------------------- +; shm_monitoring_enabled = false Enable distribution of monitoring/registration information via shared memory +; shm_monitoring_domain = ecal_monitoring Domain name for shared memory based monitoring/registration +; shm_monitoring_queue_size = 1024 Queue size of monitoring/registration events +; network_monitoring_disabled = false Disable distribution of monitoring/registration information via network +; +; drop_out_of_order_messages = false Enable dropping of payload messages that arrive out of order +; -------------------------------------------------- +[experimental] +shm_monitoring_enabled = false +shm_monitoring_domain = ecal_mon +shm_monitoring_queue_size = 1024 +network_monitoring_disabled = false +drop_out_of_order_messages = false diff --git a/ecal/core/include/ecal/cimpl/ecal_callback_cimpl.h b/src/core/include/ecal/cimpl/ecal_callback_cimpl.h similarity index 76% rename from ecal/core/include/ecal/cimpl/ecal_callback_cimpl.h rename to src/core/include/ecal/cimpl/ecal_callback_cimpl.h index fb0d76a..b2e7b2d 100644 --- a/ecal/core/include/ecal/cimpl/ecal_callback_cimpl.h +++ b/src/core/include/ecal/cimpl/ecal_callback_cimpl.h @@ -34,7 +34,6 @@ enum eCAL_Subscriber_Event sub_event_connected = 1, sub_event_disconnected = 2, sub_event_dropped = 3, - sub_event_timeout = 4, sub_event_corrupted = 5, sub_event_update_connection = 6, }; @@ -106,12 +105,13 @@ struct SReceiveCallbackDataC **/ struct SPubEventCallbackDataC { - enum eCAL_Publisher_Event type; //!< event type - long long time; //!< event time stamp - long long clock; //!< event clock - const char* tid; //!< topic id of the connected subscriber (for pub_event_update_connection only) - const char* ttype; //!< topic type information of the connected subscriber (for pub_event_update_connection only) - const char* tdesc; //!< topic descriptor information of the connected subscriber (for pub_event_update_connection only) + enum eCAL_Publisher_Event type; //!< event type + long long time; //!< event time stamp + long long clock; //!< event clock + const char* tid; //!< topic id of the connected subscriber (for pub_event_update_connection only) + const char* tname; //!< topic type encoding of the connected subscriber (for pub_event_update_connection only) + const char* tencoding; //!< topic type name of the connected subscriber (for pub_event_update_connection only) + const char* tdesc; //!< topic type descriptor information of the connected subscriber (for pub_event_update_connection only) }; /** @@ -119,12 +119,13 @@ struct SPubEventCallbackDataC **/ struct SSubEventCallbackDataC { - enum eCAL_Subscriber_Event type; //!< event type - long long time; //!< event time stamp - long long clock; //!< event clock - const char* tid; //!< topic id of the connected publisher (for sub_event_update_connection only) - const char* ttype; //!< topic type information of the connected publisher (for sub_event_update_connection only) - const char* tdesc; //!< topic descriptor information of the connected publisher (for sub_event_update_connection only) + enum eCAL_Subscriber_Event type; //!< event type + long long time; //!< event time stamp + long long clock; //!< event clock + const char* tid; //!< topic id of the connected publisher (for sub_event_update_connection only) + const char* tname; //!< topic type encoding of the connected publisher (for sub_event_update_connection only) + const char* tencoding; //!< topic type name of the connected publisher (for sub_event_update_connection only) + const char* tdesc; //!< topic type descriptor information of the connected publisher (for sub_event_update_connection only) }; /** diff --git a/ecal/core/include/ecal/cimpl/ecal_client_cimpl.h b/src/core/include/ecal/cimpl/ecal_client_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_client_cimpl.h rename to src/core/include/ecal/cimpl/ecal_client_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_core_cimpl.h b/src/core/include/ecal/cimpl/ecal_core_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_core_cimpl.h rename to src/core/include/ecal/cimpl/ecal_core_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_init_cimpl.h b/src/core/include/ecal/cimpl/ecal_init_cimpl.h similarity index 94% rename from ecal/core/include/ecal/cimpl/ecal_init_cimpl.h rename to src/core/include/ecal/cimpl/ecal_init_cimpl.h index 89dcace..490fccb 100644 --- a/ecal/core/include/ecal/cimpl/ecal_init_cimpl.h +++ b/src/core/include/ecal/cimpl/ecal_init_cimpl.h @@ -31,7 +31,6 @@ #define eCAL_Init_Monitoring 0x08 /*!< Initialize Monitoring API */ #define eCAL_Init_Logging 0x10 /*!< Initialize Logging API */ #define eCAL_Init_TimeSync 0x20 /*!< Initialize Time API */ -#define eCAL_Init_RPC 0x40 /*!< Initialize RPC API */ #define eCAL_Init_ProcessReg 0x80 /*!< Initialize Process Registration API */ #define eCAL_Init_All (eCAL_Init_Publisher \ @@ -40,7 +39,6 @@ | eCAL_Init_Monitoring \ | eCAL_Init_Logging \ | eCAL_Init_TimeSync \ - | eCAL_Init_RPC \ | eCAL_Init_ProcessReg) /*!< Initialize complete eCAL API */ #define eCAL_Init_Default (eCAL_Init_Publisher \ diff --git a/ecal/core/include/ecal/cimpl/ecal_log_cimpl.h b/src/core/include/ecal/cimpl/ecal_log_cimpl.h similarity index 74% rename from ecal/core/include/ecal/cimpl/ecal_log_cimpl.h rename to src/core/include/ecal/cimpl/ecal_log_cimpl.h index 03cccc9..4725a20 100644 --- a/ecal/core/include/ecal/cimpl/ecal_log_cimpl.h +++ b/src/core/include/ecal/cimpl/ecal_log_cimpl.h @@ -53,28 +53,6 @@ extern "C" * @param msg_ The log message string. **/ ECALC_API void eCAL_Logging_Log(const char* const msg_); - - /** - * @brief Mark the start of the user core process. - **/ - ECALC_API void eCAL_Logging_StartCoreTimer(); - - /** - * @brief Mark the stop of the user core process. - **/ - ECALC_API void eCAL_Logging_StopCoreTimer(); - - /** - * @brief Set the current measured core time in s (for user implemented measuring). - * - * @param time_ The core time. - **/ - ECALC_API void eCAL_Logging_SetCoreTime(double time_); - - /** - * @brief Returns the current measured core time in s. - **/ - ECALC_API double eCAL_Logging_GetCoreTime(); #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/ecal/core/include/ecal/cimpl/ecal_monitoring_cimpl.h b/src/core/include/ecal/cimpl/ecal_monitoring_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_monitoring_cimpl.h rename to src/core/include/ecal/cimpl/ecal_monitoring_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_process_cimpl.h b/src/core/include/ecal/cimpl/ecal_process_cimpl.h similarity index 69% rename from ecal/core/include/ecal/cimpl/ecal_process_cimpl.h rename to src/core/include/ecal/cimpl/ecal_process_cimpl.h index a271d36..07fca1e 100644 --- a/ecal/core/include/ecal/cimpl/ecal_process_cimpl.h +++ b/src/core/include/ecal/cimpl/ecal_process_cimpl.h @@ -50,13 +50,6 @@ extern "C" **/ ECALC_API int eCAL_Process_GetHostName(void* name_, int name_len_); - /** - * @brief Get unique host id. - * - * @return The host id. - **/ - ECALC_API int eCAL_Process_GetHostID(); - /** * @brief Get process unit name (defined with eCAL_Initialize). * @@ -116,58 +109,6 @@ extern "C" **/ ECALC_API int eCAL_Process_GetProcessParameter(void* par_, int par_len_); - /** - * @brief Get CPU usage of current process. - * - * @return The CPU usage in percent. - **/ - ECALC_API float eCAL_Process_GetProcessCpuUsage(); - - /** - * @brief Get memory usage of current process. - * - * @return The memory usage in bytes. - **/ - ECALC_API unsigned long eCAL_Process_GetProcessMemory(); - - /** - * @deprecated Use the function eCAL_Process_GetWClock() instead - **/ - ECALC_API long long eCAL_Process_GetSClock(); - - /** - * @deprecated Use the function eCAL_Process_GetWBytes() instead - **/ - ECALC_API long long eCAL_Process_GetSBytes(); - - /** - * @brief Get the write clock of the current process. - * - * @return The message write count per second. - **/ - ECALC_API long long eCAL_Process_GetWClock(); - - /** - * @brief Get the write bytes of the current process. - * - * @return The message write bytes per second. - **/ - ECALC_API long long eCAL_Process_GetWBytes(); - - /** - * @brief Get the read clock of the current process. - * - * @return The message read count per second. - **/ - ECALC_API long long eCAL_Process_GetRClock(); - - /** - * @brief Get the read bytes of the current process. - * - * @return The message read bytes per second. - **/ - ECALC_API long long eCAL_Process_GetRBytes(); - /** * @brief Set process state info. * @@ -179,34 +120,34 @@ extern "C" ECALC_API void eCAL_Process_SetState(enum eCAL_Process_eSeverity severity_, enum eCAL_Process_eSeverity_Level level_, const char* info_); /** - * @brief Start specified process (windows only). + * @brief Start specified process (windows only). * - * @param proc_name_ Process name. - * @param proc_args_ Process argument string. - * @param working_dir_ Working directory. - * @param create_console_ Start process in own console window (Windows only). - * @param process_mode_ Start normal, hidden, minimized, maximized (Windows only). - * @param block_ Block until process finished. + * @param proc_name_ Process name. + * @param proc_args_ Process argument string. + * @param working_dir_ Working directory. + * @param create_console_ Start process in own console window (Windows only). + * @param process_mode_ Start normal, hidden, minimized, maximized (Windows only). + * @param block_ Block until process finished. * - * @return Process id or zero if failed. + * @return Process id or zero if failed. **/ ECALC_API int eCAL_Process_StartProcess(const char* proc_name_, const char* proc_args_, const char* working_dir_, int create_console_, enum eCAL_Process_eStartMode process_mode_, int block_); /** - * @brief Stop specified process (windows only). + * @brief Stop specified process (windows only). * - * @param proc_name_ Process name. + * @param proc_name_ Process name. * - * @return None zero if successful. + * @return None zero if successful. **/ ECALC_API int eCAL_Process_StopProcessName(const char* proc_name_); /** - * @brief Stop specified process (windows only). + * @brief Stop specified process (windows only). * - * @param proc_id_ Process id. + * @param proc_id_ Process id. * - * @return None zero if successful. + * @return None zero if successful. **/ ECALC_API int eCAL_Process_StopProcessID(int proc_id_); #ifdef __cplusplus diff --git a/src/core/include/ecal/cimpl/ecal_publisher_cimpl.h b/src/core/include/ecal/cimpl/ecal_publisher_cimpl.h new file mode 100644 index 0000000..c3fa155 --- /dev/null +++ b/src/core/include/ecal/cimpl/ecal_publisher_cimpl.h @@ -0,0 +1,180 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_publisher_cimpl.h + * @brief eCAL publisher c interface +**/ + +#ifndef ecal_publisher_cimpl_h_included +#define ecal_publisher_cimpl_h_included + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /*__cplusplus*/ + /** + * @brief Instance a publisher. + * + * @return Handle to new publisher or NULL if failed. + **/ + ECALC_API ECAL_HANDLE eCAL_Pub_New(); + + /** + * @brief Create a publisher. + * + * @param handle_ Publisher handle. + * @param topic_name_ Unique topic name. + * @param topic_type_encoding_ Topic type encoding (like 'base', 'proto'). + * @param topic_type_name_ Topic type name (like 'string', 'person'). + * @param topic_desc_ Topic type description. + * @param topic_desc_len_ Topic type description length. + * + * @return None zero if succeeded. + **/ + ECALC_API int eCAL_Pub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_encoding_, const char* topic_type_name_, const char* topic_desc_, int topic_desc_len_); + + /** + * @brief Destroy a publisher. + * + * @param handle_ Publisher handle. + * + * @return None zero if succeeded. + **/ + ECALC_API int eCAL_Pub_Destroy(ECAL_HANDLE handle_); + + /** + * @brief Sets publisher attribute. + * + * @param handle_ Publisher handle. + * @param attr_name_ Attribute name. + * @param attr_name_len_ Attribute name length. + * @param attr_value_ Attribute value. + * @param attr_value_len_ Attribute value length. + * + * @return None zero if succeeded. + **/ + ECALC_API int eCAL_Pub_SetAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_, const char* attr_value_, int attr_value_len_); + + /** + * @brief Removes publisher attribute. + * + * @param handle_ Publisher handle. + * @param attr_name_ Attribute name. + * @param attr_name_len_ Attribute name length. + * + * @return None zero if succeeded. + * @experimental + **/ + ECALC_API int eCAL_Pub_ClearAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_); + + /** + * @brief Share topic type. + * + * @param handle_ Publisher handle. + * @param state_ Set type share mode (none zero == share type). + * + * @return None zero if succeeded. + **/ + ECALC_API int eCAL_Pub_ShareType(ECAL_HANDLE handle_, int state_); + + /** + * @brief Share topic description. + * + * @param handle_ Publisher handle. + * @param state_ Set description share mode (none zero == share description). + * + * @return None zero if succeeded. + **/ + ECALC_API int eCAL_Pub_ShareDescription(ECAL_HANDLE handle_, int state_); + + /** + * @brief Set the specific topic id. + * + * @param handle_ Publisher handle. + * @param id_ The topic id for subscriber side filtering (0 == no id). + * + * @return True if it succeeds, false if it fails. + **/ + ECALC_API int eCAL_Pub_SetID(ECAL_HANDLE handle_, long long id_); + + /** + * @brief Query if the publisher is subscribed. + * + * @param handle_ Publisher handle. + * + * @return None zero if subscribed. + **/ + ECALC_API int eCAL_Pub_IsSubscribed(ECAL_HANDLE handle_); + + /** + * @brief Send a message to all subscribers. + * + * @param handle_ Publisher handle. + * @param buf_ Buffer that contains content to send. + * @param buf_len_ Send buffer length. + * @param time_ Send time (-1 = use eCAL system time in us, default = -1). + * + * @return Number of bytes sent. + **/ + ECALC_API int eCAL_Pub_Send(ECAL_HANDLE handle_, const void* const buf_, int buf_len_, long long time_); + + /** + * @brief Add callback function for publisher events. + * + * @param handle_ Publisher handle. + * @param type_ The event type to react on. + * @param callback_ The callback function to add. + * @param par_ User defined context that will be forwarded to the callback function. + * + * @return None zero if succeeded. + **/ + ECALC_API int eCAL_Pub_AddEventCallback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_, PubEventCallbackCT callback_, void* par_); + + /** + * @brief Remove callback function for publisher events. + * + * @param handle_ Publisher handle. + * @param type_ The event type to remove. + * + * @return None zero if succeeded. + **/ + ECALC_API int eCAL_Pub_RemEventCallback(ECAL_HANDLE handle_, enum eCAL_Publisher_Event type_); + + /** + * @brief Dump the whole class state into a string buffer. + * + * @param handle_ Publisher handle. + * @param [out] buf_ Pointer to store the monitoring information. + * @param buf_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Dump buffer length or zero if failed. + **/ + ECALC_API int eCAL_Pub_Dump(ECAL_HANDLE handle_, void* buf_, int buf_len_); +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*ecal_publisher_cimpl_h_included*/ diff --git a/ecal/core/include/ecal/cimpl/ecal_server_cimpl.h b/src/core/include/ecal/cimpl/ecal_server_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_server_cimpl.h rename to src/core/include/ecal/cimpl/ecal_server_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_service_cimpl.h b/src/core/include/ecal/cimpl/ecal_service_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_service_cimpl.h rename to src/core/include/ecal/cimpl/ecal_service_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_service_info_cimpl.h b/src/core/include/ecal/cimpl/ecal_service_info_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_service_info_cimpl.h rename to src/core/include/ecal/cimpl/ecal_service_info_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_subscriber_cimpl.h b/src/core/include/ecal/cimpl/ecal_subscriber_cimpl.h similarity index 76% rename from ecal/core/include/ecal/cimpl/ecal_subscriber_cimpl.h rename to src/core/include/ecal/cimpl/ecal_subscriber_cimpl.h index 2e7efb9..d7060e3 100644 --- a/ecal/core/include/ecal/cimpl/ecal_subscriber_cimpl.h +++ b/src/core/include/ecal/cimpl/ecal_subscriber_cimpl.h @@ -30,9 +30,6 @@ #include -#include "ecal_qos_cimpl.h" -#include "ecal_tlayer_cimpl.h" - #ifdef __cplusplus extern "C" { @@ -45,17 +42,18 @@ extern "C" ECALC_API ECAL_HANDLE eCAL_Sub_New(); /** - * @brief Create a subscriber. + * @brief Create a subscriber. * - * @param handle_ Subscriber handle. - * @param topic_name_ Unique topic name. - * @param topic_type_ Topic type name. - * @param topic_desc_ Topic description. - * @param topic_desc_len_ Topic type description length. + * @param handle_ Publisher handle. + * @param topic_name_ Unique topic name. + * @param topic_type_encoding_ Topic type encoding (like 'base', 'proto'). + * @param topic_type_name_ Topic type name (like 'string', 'person'). + * @param topic_desc_ Topic type description. + * @param topic_desc_len_ Topic type description length. * * @return None zero if succeeded. **/ - ECALC_API int eCAL_Sub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_, const char* topic_desc_, int topic_desc_len_); + ECALC_API int eCAL_Sub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_encoding_, const char* topic_type_name_, const char* topic_desc_, int topic_desc_len_); /** * @brief Destroy a subscriber. @@ -66,26 +64,6 @@ extern "C" **/ ECALC_API int eCAL_Sub_Destroy(ECAL_HANDLE handle_); - /** - * @brief Set subscriber quality of service attributes. - * - * @param handle_ Subscriber handle. - * @param qos_ Quality of service policies. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Sub_SetQOS(ECAL_HANDLE handle_, struct SReaderQOSC qos_); - - /** - * @brief Get subscriber quality of service attributes. - * - * @param handle_ Subscriber handle. - * @param qos_ Quality of service policies. - * - * @return None zero if succeeded. - **/ - ECALC_API int eCAL_Sub_GetQOS(ECAL_HANDLE handle_, struct SReaderQOSC* qos_); - /** * @brief Set a set of id's to prefiltering topics (see eCAL_Pub_SetID). * @@ -191,18 +169,6 @@ extern "C" **/ ECALC_API int eCAL_Sub_AddReceiveCallback(ECAL_HANDLE handle_, ReceiveCallbackCT callback_, void* par_); - /** - * @brief Add callback function for incoming receives. - * @deprecated Please use eCAL_Sub_AddReceiveCallback instead - * - * @param handle_ Subscriber handle. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. - * - * @return None zero if succeeded. - **/ - ECALC_API_DEPRECATED int eCAL_Sub_AddReceiveCallbackC(ECAL_HANDLE handle_, ReceiveCallbackCT callback_, void* par_); - /** * @brief Remove callback function for incoming receives. * @@ -214,7 +180,6 @@ extern "C" /** * @brief Add callback function for subscriber events. - * @since eCAL 5.10.0 * * @param handle_ Subscriber handle. * @param type_ The event type to react on. @@ -226,50 +191,50 @@ extern "C" ECALC_API int eCAL_Sub_AddEventCallback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_, SubEventCallbackCT callback_, void* par_); /** - * @deprecated Use eCAL_Sub_AddEventCallback instead - * @brief Add callback function for subscriber events. + * @brief Remove callback function for subscriber events. * - * @param handle_ Subscriber handle. - * @param type_ The event type to react on. - * @param callback_ The callback function to add. - * @param par_ User defined context that will be forwarded to the callback function. + * @param handle_ Subscriber handle. + * @param type_ The event type to remove. * * @return None zero if succeeded. **/ - ECALC_API_DEPRECATED int eCAL_Sub_AddEventCallbackC(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_, SubEventCallbackCT callback_, void* par_); + ECALC_API int eCAL_Sub_RemEventCallback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_); /** - * @brief Remove callback function for subscriber events. + * @brief Gets type name of the connected topic. * - * @param handle_ Subscriber handle. - * @param type_ The event type to remove. + * @param handle_ Subscriber handle. + * @param [out] buf_ Pointer to store the subscriber type name string. + * @param buf_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). * - * @return None zero if succeeded. + * @return Type name buffer length or zero if failed. **/ - ECALC_API int eCAL_Sub_RemEventCallback(ECAL_HANDLE handle_, enum eCAL_Subscriber_Event type_); + ECALC_API int eCAL_Sub_GetTypeName(ECAL_HANDLE handle_, void* buf_, int buf_len_); /** - * @brief Gets description of the connected topic. + * @brief Gets encoding of the connected topic. * - * @param handle_ Subscriber handle. - * @param [out] buf_ Pointer to store the subscriber description string. + * @param handle_ Subscriber handle. + * @param [out] buf_ Pointer to store the subscriber encoding string. * @param buf_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * eCAL should allocate the buffer for you (see eCAL_FreeMem). * - * @return Description buffer length or zero if failed. + * @return Encoding buffer length or zero if failed. **/ - ECALC_API int eCAL_Sub_GetDescription(ECAL_HANDLE handle_, void* buf_, int buf_len_); + ECALC_API int eCAL_Sub_GetEncoding(ECAL_HANDLE handle_, void* buf_, int buf_len_); /** - * @brief Set the timeout parameter for triggering - * the timeout callback. + * @brief Gets description of the connected topic. * - * @param handle_ Subscriber handle. - * @param timeout_ The timeout in milliseconds. + * @param handle_ Subscriber handle. + * @param [out] buf_ Pointer to store the subscriber description string. + * @param buf_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). * - * @return True if succeeded, false if not. + * @return Description buffer length or zero if failed. **/ - ECALC_API int eCAL_Sub_SetTimeout(ECAL_HANDLE handle_, int timeout_); + ECALC_API int eCAL_Sub_GetDescription(ECAL_HANDLE handle_, void* buf_, int buf_len_); /** * @brief Dump the whole class state into a string buffer. diff --git a/ecal/core/include/ecal/cimpl/ecal_time_cimpl.h b/src/core/include/ecal/cimpl/ecal_time_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_time_cimpl.h rename to src/core/include/ecal/cimpl/ecal_time_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_timer_cimpl.h b/src/core/include/ecal/cimpl/ecal_timer_cimpl.h similarity index 100% rename from ecal/core/include/ecal/cimpl/ecal_timer_cimpl.h rename to src/core/include/ecal/cimpl/ecal_timer_cimpl.h diff --git a/ecal/core/include/ecal/cimpl/ecal_tlayer_cimpl.h b/src/core/include/ecal/cimpl/ecal_tlayer_cimpl.h similarity index 98% rename from ecal/core/include/ecal/cimpl/ecal_tlayer_cimpl.h rename to src/core/include/ecal/cimpl/ecal_tlayer_cimpl.h index 3a00bd8..7ebd523 100644 --- a/ecal/core/include/ecal/cimpl/ecal_tlayer_cimpl.h +++ b/src/core/include/ecal/cimpl/ecal_tlayer_cimpl.h @@ -34,7 +34,6 @@ enum eTransportLayerC tlayer_udp_mc = 1, tlayer_shm = 4, tlayer_tcp = 5, - tlayer_inproc = 42, tlayer_all = 255 }; diff --git a/ecal/core/include/ecal/cimpl/ecal_util_cimpl.h b/src/core/include/ecal/cimpl/ecal_util_cimpl.h similarity index 86% rename from ecal/core/include/ecal/cimpl/ecal_util_cimpl.h rename to src/core/include/ecal/cimpl/ecal_util_cimpl.h index 8e6de2a..aa279f9 100644 --- a/ecal/core/include/ecal/cimpl/ecal_util_cimpl.h +++ b/src/core/include/ecal/cimpl/ecal_util_cimpl.h @@ -77,6 +77,30 @@ extern "C" **/ 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. * @@ -127,32 +151,6 @@ extern "C" * @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 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. - **/ - // [[deprecated]] - ECALC_API int eCAL_Util_GetTypeName(const char* topic_name_, void* topic_type_, int topic_type_len_); - - /** - * @brief Gets type description of the specified topic. - * - * @param topic_name_ Topic name. - * @param [out] topic_desc_ Pointer to store the type description 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 description buffer length or zero if failed. - **/ - // [[deprecated]] - ECALC_API int eCAL_Util_GetDescription(const char* topic_name_, void* topic_desc_, int topic_desc_len_); #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/ecal/core/include/ecal/ecal.h b/src/core/include/ecal/ecal.h similarity index 85% rename from ecal/core/include/ecal/ecal.h rename to src/core/include/ecal/ecal.h index d4d5ef2..43669cb 100644 --- a/ecal/core/include/ecal/ecal.h +++ b/src/core/include/ecal/ecal.h @@ -31,12 +31,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include @@ -44,13 +42,3 @@ #include #include #include - -/* Legacy namespace to be compatible with eCAL < 4.9 code, will be removed in future eCAL versions*/ -namespace eCAL -{ - namespace pb - { - } -} -namespace eCALPB = eCAL::pb; - diff --git a/ecal/core/include/ecal/ecal_callback.h b/src/core/include/ecal/ecal_callback.h similarity index 52% rename from ecal/core/include/ecal/ecal_callback.h rename to src/core/include/ecal/ecal_callback.h index 76ad157..2fac20a 100644 --- a/ecal/core/include/ecal/ecal_callback.h +++ b/src/core/include/ecal/ecal_callback.h @@ -24,7 +24,9 @@ #pragma once +#include #include +#include #include #include @@ -36,19 +38,11 @@ namespace eCAL **/ struct SReceiveCallbackData { - SReceiveCallbackData() - { - buf = nullptr; - size = 0; - id = 0; - time = 0; - clock = 0; - }; - void* buf; //!< payload buffer - long size; //!< payload buffer size - long long id; //!< publisher id (SetId()) - long long time; //!< publisher send time in µs - long long clock; //!< publisher send clock + void* buf = nullptr; //!< payload buffer + long size = 0; //!< payload buffer size + long long id = 0; //!< publisher id (SetId()) + long long time = 0; //!< publisher send time in µs + long long clock = 0; //!< publisher send clock }; /** @@ -62,12 +56,11 @@ namespace eCAL time = 0; clock = 0; }; - eCAL_Publisher_Event type; //!< publisher event type - long long time; //!< publisher event time in µs - long long clock; //!< publisher event clock - std::string tid; //!< topic id of the of the connected subscriber (for pub_event_update_connection only) - std::string ttype; //!< topic type information of the connected subscriber (for pub_event_update_connection only) - std::string tdesc; //!< topic descriptor information of the connected subscriber (for pub_event_update_connection only) + eCAL_Publisher_Event type; //!< publisher event type + long long time; //!< publisher event time in µs + long long clock; //!< publisher event clock + std::string tid; //!< topic id of the of the connected subscriber (for pub_event_update_connection only) + SDataTypeInformation tdatatype; //!< datatype description of the connected subscriber (for pub_event_update_connection only) }; /** @@ -81,12 +74,11 @@ namespace eCAL time = 0; clock = 0; }; - eCAL_Subscriber_Event type; //!< subscriber event type - long long time; //!< subscriber event time in µs - long long clock; //!< subscriber event clock - std::string tid; //!< topic id of the of the connected publisher (for sub_event_update_connection only) - std::string ttype; //!< topic type information of the connected publisher (for sub_event_update_connection only) - std::string tdesc; //!< topic descriptor information of the connected publisher (for sub_event_update_connection only) + eCAL_Subscriber_Event type; //!< subscriber event type + long long time; //!< subscriber event time in µs + long long clock; //!< subscriber event clock + std::string tid; //!< topic id of the of the connected publisher (for sub_event_update_connection only) + SDataTypeInformation tdatatype; //!< topic information of the connected subscriber (for pub_event_update_connection only) }; /** @@ -94,14 +86,18 @@ namespace eCAL **/ struct SServiceAttr { - std::string key; //!< unique service key (internal) - std::string hname; //!< host name - std::string pname; //!< process name - std::string uname; //!< process unit name - std::string sname; //!< service name - std::string sid; //!< service id - int pid = 0; //!< process id - unsigned short tcp_port = 0; //!< service tcp port + std::string key; //!< unique service key (internal) + std::string hname; //!< host name + std::string pname; //!< process name + std::string uname; //!< process unit name + std::string sname; //!< service name + std::string sid; //!< service id + int pid = 0; //!< process id + + // internal protocol specifics + unsigned int version = 0; //!< service protocol version + unsigned short tcp_port_v0 = 0; //!< service tcp port protocol version 0 + unsigned short tcp_port_v1 = 0; //!< service tcp port protocol version 1 }; /** @@ -109,14 +105,9 @@ namespace eCAL **/ struct SClientEventCallbackData { - SClientEventCallbackData() - { - type = client_event_none; - time = 0; - }; - eCAL_Client_Event type; //!< event type - long long time; //!< event time in µs - SServiceAttr attr; //!< event related service attributes + eCAL_Client_Event type = client_event_none; //!< event type + long long time = 0; //!< event time in µs + SServiceAttr attr; //!< event related service attributes }; /** @@ -131,6 +122,8 @@ namespace eCAL std::string sname; //!< service name std::string sid; //!< service id int pid = 0; //!< process id + + unsigned int version = 0; //!< client version }; /** @@ -138,13 +131,8 @@ namespace eCAL **/ struct SServerEventCallbackData { - SServerEventCallbackData() - { - type = server_event_none; - time = 0; - }; - eCAL_Server_Event type; //!< event type - long long time; //!< event time in µs + eCAL_Server_Event type = server_event_none; //!< event type + long long time = 0; //!< event time in µs }; /** @@ -153,12 +141,12 @@ namespace eCAL * @param topic_name_ The topic name of the received message. * @param data_ Data struct containing payload, timestamp and publication clock. **/ - typedef std::function ReceiveCallbackT; + using ReceiveCallbackT = std::function; /** * @brief Timer callback function type. **/ - typedef std::function TimerCallbackT; + using TimerCallbackT = std::function; /** * @brief Registration callback type. @@ -166,37 +154,37 @@ namespace eCAL * @param sample_ The sample protocol buffer registration payload buffer. * @param sample_size_ The payload buffer size. **/ - typedef std::function RegistrationCallbackT; + using RegistrationCallbackT = std::function; /** * @brief Publisher event callback function type. * * @param topic_name_ The topic name of the publisher that triggered the event. - * @param data_ Event callback data structure with the event specific informations. + * @param data_ Event callback data structure with the event specific information. **/ - typedef std::function PubEventCallbackT; + using PubEventCallbackT = std::function; /** * @brief Subscriber event callback function type. * * @param topic_name_ The topic name of the subscriber that triggered the event. - * @param data_ Event callback data structure with the event specific informations. + * @param data_ Event callback data structure with the event specific information. **/ - typedef std::function SubEventCallbackT; + using SubEventCallbackT = std::function; /** * @brief Client event callback function type. * * @param name_ The name of the connection that triggered the event. - * @param data_ Event callback data structure with the event specific informations. + * @param data_ Event callback data structure with the event specific information. **/ - typedef std::function ClientEventCallbackT; + using ClientEventCallbackT = std::function; /** * @brief Server event callback function type. * * @param name_ The name of the connection that triggered the event. - * @param data_ Event callback data structure with the event specific informations. + * @param data_ Event callback data structure with the event specific information. **/ - typedef std::function ServerEventCallbackT; -}; + using ServerEventCallbackT = std::function; +} diff --git a/ecal/core/include/ecal/ecal_client.h b/src/core/include/ecal/ecal_client.h similarity index 71% rename from ecal/core/include/ecal/ecal_client.h rename to src/core/include/ecal/ecal_client.h index 906c88d..3666f39 100644 --- a/ecal/core/include/ecal/ecal_client.h +++ b/src/core/include/ecal/ecal_client.h @@ -24,6 +24,7 @@ #pragma once +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include namespace eCAL { @@ -39,35 +41,35 @@ namespace eCAL /** * @brief Service client wrapper class. **/ - class ECAL_API CServiceClient + class CServiceClient { public: /** * @brief Constructor. **/ - CServiceClient(); + ECAL_API CServiceClient(); /** * @brief Constructor. * * @param service_name_ Unique service name. **/ - CServiceClient(const std::string& service_name_); + ECAL_API CServiceClient(const std::string& service_name_); /** * @brief Destructor. **/ - virtual ~CServiceClient(); + ECAL_API virtual ~CServiceClient(); /** * @brief CServiceClients are non-copyable **/ - CServiceClient(const CServiceClient&) = delete; + ECAL_API CServiceClient(const CServiceClient&) = delete; /** * @brief CServiceClients are non-copyable **/ - CServiceClient& operator=(const CServiceClient&) = delete; + ECAL_API CServiceClient& operator=(const CServiceClient&) = delete; /** * @brief Creates this object. @@ -76,14 +78,14 @@ namespace eCAL * * @return True if successful. **/ - bool Create(const std::string& service_name_); + ECAL_API bool Create(const std::string& service_name_); /** * @brief Destroys this object. * * @return True if successful. **/ - bool Destroy(); + ECAL_API bool Destroy(); /** * @brief Change the host name filter for that client instance @@ -92,7 +94,7 @@ namespace eCAL * * @return True if successful. **/ - bool SetHostName(const std::string& host_name_); + ECAL_API bool SetHostName(const std::string& host_name_); /** * @brief Call a method of this service, responses will be returned by callback. @@ -103,7 +105,7 @@ namespace eCAL * * @return True if successful. **/ - bool Call(const std::string& method_name_, const std::string& request_, int timeout_ = -1); + ECAL_API bool Call(const std::string& method_name_, const std::string& request_, int timeout_ = -1); /** * @brief Call a method of this service, all responses will be returned in service_response_vec_. @@ -115,21 +117,7 @@ namespace eCAL * * @return True if successful. **/ - bool Call(const std::string& method_name_, const std::string& request_, int timeout_, ServiceResponseVecT* service_response_vec_); - - /** - * @brief Call method of this service, for specific host (deprecated). - * - * @param host_name_ Host name. - * @param method_name_ Method name. - * @param request_ Request string. - * @param [out] service_info_ Service response struct for detailed informations. - * @param [out] response_ Response string. - * - * @return True if successful. - **/ - [[deprecated]] - bool Call(const std::string& host_name_, const std::string& method_name_, const std::string& request_, struct SServiceResponse& service_info_, std::string& response_); + ECAL_API bool Call(const std::string& method_name_, const std::string& request_, int timeout_, ServiceResponseVecT* service_response_vec_); /** * @brief Call a method of this service asynchronously, responses will be returned by callback. @@ -140,7 +128,7 @@ namespace eCAL * * @return True if successful. **/ - bool CallAsync(const std::string& method_name_, const std::string& request_, int timeout_ = -1); + ECAL_API bool CallAsync(const std::string& method_name_, const std::string& request_, int timeout_ = -1); /** * @brief Add server response callback. @@ -149,14 +137,14 @@ namespace eCAL * * @return True if successful. **/ - bool AddResponseCallback(const ResponseCallbackT& callback_); + ECAL_API bool AddResponseCallback(const ResponseCallbackT& callback_); /** * @brief Remove server response callback. * * @return True if successful. **/ - bool RemResponseCallback(); + ECAL_API bool RemResponseCallback(); /** * @brief Add client event callback function. @@ -166,7 +154,7 @@ namespace eCAL * * @return True if succeeded, false if not. **/ - bool AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_); + ECAL_API bool AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_); /** * @brief Remove client event callback function. @@ -175,24 +163,24 @@ namespace eCAL * * @return True if succeeded, false if not. **/ - bool RemEventCallback(eCAL_Client_Event type_); + ECAL_API bool RemEventCallback(eCAL_Client_Event type_); /** * @brief Retrieve service name. * * @return The service name. **/ - std::string GetServiceName(); + ECAL_API std::string GetServiceName(); /** * @brief Check connection state. * * @return True if connected, false if not. **/ - bool IsConnected(); + ECAL_API bool IsConnected(); protected: - CServiceClientImpl* m_service_client_impl; + std::shared_ptr m_service_client_impl; bool m_created; }; } diff --git a/ecal/core/include/ecal/ecal_config.h b/src/core/include/ecal/ecal_config.h similarity index 94% rename from ecal/core/include/ecal/ecal_config.h rename to src/core/include/ecal/ecal_config.h index 8e82807..c817d79 100644 --- a/ecal/core/include/ecal/ecal_config.h +++ b/src/core/include/ecal/ecal_config.h @@ -25,6 +25,7 @@ #include +//@{ namespace eCAL { namespace Config @@ -59,12 +60,9 @@ namespace eCAL ECAL_API bool IsUdpMulticastJoinAllIfEnabled (); - ECAL_API int GetMaxUdpBandwidthBytesPerSecond (); - ECAL_API bool IsUdpMulticastRecEnabled (); ECAL_API bool IsShmRecEnabled (); ECAL_API bool IsTcpRecEnabled (); - ECAL_API bool IsInprocRecEnabled (); ECAL_API bool IsNpcapEnabled (); @@ -72,6 +70,8 @@ namespace eCAL ECAL_API int GetTcpPubsubWriterThreadpoolSize (); ECAL_API int GetTcpPubsubMaxReconnectionAttemps (); + ECAL_API std::string GetHostGroupName (); + ///////////////////////////////////// // time ///////////////////////////////////// @@ -104,7 +104,6 @@ namespace eCAL ///////////////////////////////////// // publisher ///////////////////////////////////// - ECAL_API TLayer::eSendMode GetPublisherInprocMode (); ECAL_API TLayer::eSendMode GetPublisherShmMode (); ECAL_API TLayer::eSendMode GetPublisherTcpMode (); ECAL_API TLayer::eSendMode GetPublisherUdpMulticastMode (); @@ -118,6 +117,12 @@ namespace eCAL ECAL_API bool IsTopicTypeSharingEnabled (); ECAL_API bool IsTopicDescriptionSharingEnabled (); + ///////////////////////////////////// + // service + ///////////////////////////////////// + ECAL_API bool IsServiceProtocolV0Enabled (); + ECAL_API bool IsServiceProtocolV1Enabled (); + ///////////////////////////////////// // experimental ///////////////////////////////////// @@ -131,3 +136,4 @@ namespace eCAL } } } +//@} \ No newline at end of file diff --git a/ecal/core/include/ecal/ecal_core.h b/src/core/include/ecal/ecal_core.h similarity index 100% rename from ecal/core/include/ecal/ecal_core.h rename to src/core/include/ecal/ecal_core.h diff --git a/src/core/include/ecal/ecal_deprecate.h b/src/core/include/ecal/ecal_deprecate.h new file mode 100644 index 0000000..c583fbb --- /dev/null +++ b/src/core/include/ecal/ecal_deprecate.h @@ -0,0 +1,61 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_deprecate.h + * @brief eCAL function / variable deprecation macros +**/ + +#pragma once + +#include + +//uncomment this line if you do want to get deprecation warnings inside eCAL core +//#undef ECAL_NO_DEPRECATION_WARNINGS + +#if !defined(ECAL_NO_DEPRECATION_WARNINGS) && ECAL_VERSION_INTEGER >= ECAL_VERSION_CALCULATE(5, 4, 0) +#define ECAL_DEPRECATE_SINCE_5_4(__message__) [[deprecated(__message__)]] //!< Deprecate the following function with eCAL Version 5.4.0 +#else +#define ECAL_DEPRECATE_SINCE_5_4(__message__) //!< Deprecate the following function with eCAL Version 5.4.0 +#endif + + +#if !defined(ECAL_NO_DEPRECATION_WARNINGS) && ECAL_VERSION_INTEGER >= ECAL_VERSION_CALCULATE(5, 10, 0) +#define ECAL_DEPRECATE_SINCE_5_10(__message__) [[deprecated(__message__)]] //!< Deprecate the following function with eCAL Version 5.10.0 +#else +#define ECAL_DEPRECATE_SINCE_5_10(__message__) //!< Deprecate the following function with eCAL Version 5.10.0 +#endif + +#if !defined(ECAL_NO_DEPRECATION_WARNINGS) && ECAL_VERSION_INTEGER >= ECAL_VERSION_CALCULATE(5, 11, 0) +#define ECAL_DEPRECATE_SINCE_5_11(__message__) [[deprecated(__message__)]] //!< Deprecate the following function with eCAL Version 5.11.0 +#else +#define ECAL_DEPRECATE_SINCE_5_11(__message__) //!< Deprecate the following function with eCAL Version 5.11.0 +#endif + +#if !defined(ECAL_NO_DEPRECATION_WARNINGS) && ECAL_VERSION_INTEGER >= ECAL_VERSION_CALCULATE(5, 12, 0) +#define ECAL_DEPRECATE_SINCE_5_12(__message__) [[deprecated(__message__)]] //!< Deprecate the following function with eCAL Version 5.12.0 +#else +#define ECAL_DEPRECATE_SINCE_5_12(__message__) //!< Deprecate the following function with eCAL Version 5.12.0 +#endif + +#if !defined(ECAL_NO_DEPRECATION_WARNINGS) && ECAL_VERSION_INTEGER >= ECAL_VERSION_CALCULATE(5, 13, 0) +#define ECAL_DEPRECATE_SINCE_5_13(__message__) [[deprecated(__message__)]] //!< Deprecate the following function with eCAL Version 5.13.0 +#else +#define ECAL_DEPRECATE_SINCE_5_13(__message__) //!< Deprecate the following function with eCAL Version 5.13.0 +#endif diff --git a/ecal/core/include/ecal/ecal_init.h b/src/core/include/ecal/ecal_init.h similarity index 100% rename from ecal/core/include/ecal/ecal_init.h rename to src/core/include/ecal/ecal_init.h diff --git a/ecal/core/include/ecal/ecal_log.h b/src/core/include/ecal/ecal_log.h similarity index 70% rename from ecal/core/include/ecal/ecal_log.h rename to src/core/include/ecal/ecal_log.h index bd49e7b..9cf9da5 100644 --- a/ecal/core/include/ecal/ecal_log.h +++ b/src/core/include/ecal/ecal_log.h @@ -26,6 +26,8 @@ #include #include + +#include #include namespace eCAL @@ -33,12 +35,12 @@ namespace eCAL namespace Logging { /** - * @brief Sets the log level. + * @brief Sets the log level. * - * @param level_ The level. + * @param level_ The level. **/ ECAL_API void SetLogLevel(eCAL_Logging_eLogLevel level_); - + /** * @brief Get the current log level. * @@ -47,16 +49,16 @@ namespace eCAL ECAL_API eCAL_Logging_eLogLevel GetLogLevel(); /** - * @brief Log a message (with current log level). + * @brief Log a message (with current log level). * * @param msg_ The log message string. **/ ECAL_API void Log(const std::string& msg_); /** - * @brief Log a message. + * @brief Log a message. * - * @param level_ The level. + * @param level_ The level. * @param msg_ The log message string. **/ inline void Log(eCAL_Logging_eLogLevel level_, const std::string& msg_) @@ -66,23 +68,12 @@ namespace eCAL } /** - * @brief Mark the start of the user core process. - **/ - ECAL_API void StartCoreTimer(); - - /** - * @brief Mark the stop of the user core process. - **/ - ECAL_API void StopCoreTimer(); - - /** - * @brief Set the current measured core time in s (for user implemented measuring). - **/ - ECAL_API void SetCoreTime(double time_); - - /** - * @brief Returns the current measured core time in s. + * @brief Get logging as serialized protobuf string. + * + * @param [out] log_ String to store the logging information. + * + * @return Monitoring buffer length or zero if failed. **/ - ECAL_API double GetCoreTime(); + ECAL_API int GetLogging(std::string& log_); } } diff --git a/ecal/core/include/ecal/ecal_log_level.h b/src/core/include/ecal/ecal_log_level.h similarity index 91% rename from ecal/core/include/ecal/ecal_log_level.h rename to src/core/include/ecal/ecal_log_level.h index 52faccc..532844e 100644 --- a/ecal/core/include/ecal/ecal_log_level.h +++ b/src/core/include/ecal/ecal_log_level.h @@ -41,7 +41,4 @@ enum eCAL_Logging_eLogLevel log_level_debug4 = 128, }; -/* -* @brief This type is to be used as a bitmask for the activated logging levels -*/ -typedef char eCAL_Logging_Filter; \ No newline at end of file +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 diff --git a/ecal/core/include/ecal/ecal_monitoring.h b/src/core/include/ecal/ecal_monitoring.h similarity index 65% rename from ecal/core/include/ecal/ecal_monitoring.h rename to src/core/include/ecal/ecal_monitoring.h index 6b84901..972f109 100644 --- a/ecal/core/include/ecal/ecal_monitoring.h +++ b/src/core/include/ecal/ecal_monitoring.h @@ -25,7 +25,7 @@ #pragma once #include -#include +#include namespace eCAL { @@ -59,42 +59,24 @@ namespace eCAL ECAL_API int SetFilterState(bool state_); /** - * @brief Get monitoring protobuf string. + * @brief Get monitoring subset as serialized protobuf string. * - * @param [out] mon_ String to store the monitoring information. - * - * @return Monitoring buffer length or zero if failed. - **/ - ECAL_API int GetMonitoring(std::string& mon_); - - /** - * @brief Get logging protobuf string. - * - * @param [out] log_ String to store the logging information. - * - * @return Monitoring buffer length or zero if failed. - **/ - ECAL_API int GetLogging(std::string& log_); - - /** - * @brief Publish monitoring protobuf message. - * - * @param state_ Switch publishing on/off. - * @param name_ Monitoring topic name. + * @param [out] mon_ Target string to store the monitoring information. + * @param entities_ Entities to get. * * @return Zero if succeeded. **/ - ECAL_API int PubMonitoring(bool state_, std::string name_ = "ecal.monitoring"); - + ECAL_API int GetMonitoring(std::string& mon_, unsigned int entities_ = Entity::All); + /** - * @brief Publish logging protobuf message. + * @brief Get monitoring as a struct. * - * @param state_ Switch publishing on/off. - * @param name_ Logging topic name. + * @param [out] mon_ Target struct to store the monitoring information. + * @param entities_ Entities definition. * - * @return Zero if succeeded. + * @return Number of struct elements if succeeded. **/ - ECAL_API int PubLogging(bool state_, std::string name_ = "ecal.logging"); + ECAL_API int GetMonitoring(SMonitoring& mon_, unsigned int entities_ = Entity::All); } /** @example monitoring_rec.cpp * This is an example how the eCAL Monitoring API may be utilized to print monitoring information. diff --git a/ecal/core/include/ecal/ecal_os.h b/src/core/include/ecal/ecal_os.h similarity index 100% rename from ecal/core/include/ecal/ecal_os.h rename to src/core/include/ecal/ecal_os.h diff --git a/src/core/include/ecal/ecal_payload_writer.h b/src/core/include/ecal/ecal_payload_writer.h new file mode 100644 index 0000000..6ea98a7 --- /dev/null +++ b/src/core/include/ecal/ecal_payload_writer.h @@ -0,0 +1,119 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_payload_writer.h + * @brief eCAL payload writer base class +**/ + +#pragma once + +#include + +namespace eCAL +{ + /** + * @brief Base payload writer class to allow zero copy memory operations. + * + * This class serves as the base class for payload writers, allowing zero-copy memory + * operations. The `WriteFull` and `WriteModified` calls may operate on the target + * memory file directly in zero-copy mode. + * + * A partial writing / modification of the memory file is only possible when zero-copy mode + * is activated. If zero-copy is not enabled, the `WriteModified` method is ignored and the + * `WriteFull` method is always executed (see CPublisher::ShmEnableZeroCopy) + * + **/ + class CPayloadWriter + { + public: + /** + * @brief Default constructor for CPayloadWriter. + **/ + CPayloadWriter() = default; + + /** + * @brief Virtual destructor for CPayloadWriter. + **/ + virtual ~CPayloadWriter() = default; + + /** + * @brief Copy constructor (deleted). + **/ + CPayloadWriter(const CPayloadWriter&) = default; + + /** + * @brief Move constructor (deleted). + **/ + CPayloadWriter(CPayloadWriter&&) = default; + + /** + * @brief Copy assignment operator (deleted). + **/ + CPayloadWriter& operator=(const CPayloadWriter&) = default; + + /** + * @brief Move assignment operator (deleted). + **/ + CPayloadWriter& operator=(CPayloadWriter&&) = default; + + /** + * @brief Perform a full write operation on uninitialized memory. + * + * This virtual function allows derived classes to perform a full write operation + * when the provisioned memory is uninitialized. Typically, this is the case when a + * memory file had to be recreated or its size had to be changed. + * + * @param buffer_ Pointer to the buffer containing the data to be written. + * @param size_ Size of the data to be written. + * + * @return True if the write operation is successful, false otherwise. + **/ + virtual bool WriteFull(void* buffer_, size_t size_) = 0; + + /** + * @brief Perform a partial write operation to modify existing data. + * + * This virtual function allows derived classes to modify existing data when the provisioned + * memory is already initialized by a WriteFull call (i.e. contains the data from that full write operation). + * + * The memory can be partially modified and does not have to be completely rewritten, which leads to significantly + * higher performance (lower latency). + * + * If not implemented (by default), this operation will just call the `WriteFull` function. + * + * @param buffer_ Pointer to the buffer containing the data to be modified. + * @param size_ Size of the data to be modified. + * + * @return True if the write/update operation is successful, false otherwise. + **/ + virtual bool WriteModified(void* buffer_, size_t size_) { return WriteFull(buffer_, size_); }; + + /** + * @brief Get the size of the required memory. + * + * This virtual function allows derived classes to provide the size of the memory + * that eCAL needs to allocate. + * + * @return The size of the required memory. + **/ + virtual size_t GetSize() = 0; + }; + +} // namespace eCAL diff --git a/ecal/core/include/ecal/ecal_process.h b/src/core/include/ecal/ecal_process.h similarity index 78% rename from ecal/core/include/ecal/ecal_process.h rename to src/core/include/ecal/ecal_process.h index 78958f5..f37d905 100644 --- a/ecal/core/include/ecal/ecal_process.h +++ b/src/core/include/ecal/ecal_process.h @@ -56,12 +56,11 @@ namespace eCAL ECAL_API std::string GetHostName(); /** - * @brief Get unique host id. + * @brief Get current host group name. * - * @return Host id or zero if failed. + * @return Host group name or empty string if failed. **/ - [[deprecated]] - ECAL_API int GetHostID(); + ECAL_API std::string GetHostGroupName(); /** * @brief Get current unit name (defined via eCAL::Initialize). @@ -145,61 +144,6 @@ namespace eCAL **/ ECAL_API std::string GetProcessParameter(); - /** - * @brief Get CPU usage of current process. - * - * @return The CPU usage in percent. - **/ - ECAL_API float GetProcessCpuUsage(); - - /** - * @brief Get memory usage of current process. - * - * @return The memory usage in bytes. - **/ - ECAL_API unsigned long GetProcessMemory(); - - /** - * @deprecated Use the function GetWClock() instead - **/ - [[deprecated("use GetWClock() instead")]] - ECAL_API long long GetSClock(); - - /** - * @deprecated Use the function GetWBytes() instead - **/ - - [[deprecated("use GetWBytes() instead")]] - ECAL_API long long GetSBytes(); - - /** - * @brief Get the write clock of the current process. - * - * @return The message write count per second. - **/ - ECAL_API long long GetWClock(); - - /** - * @brief Get the write bytes of the current process. - * - * @return The message write bytes per second. - **/ - ECAL_API long long GetWBytes(); - - /** - * @brief Get the read clock of the current process. - * - * @return The message read count per second. - **/ - ECAL_API long long GetRClock(); - - /** - * @brief Get the read bytes of the current process. - * - * @return The message read bytes per second. - **/ - ECAL_API long long GetRBytes(); - /** * @brief Set process state info. * @@ -218,14 +162,14 @@ namespace eCAL * * @return Zero if succeeded. **/ - ECAL_API int AddRegistrationCallback(enum eCAL_Registration_Event event_, RegistrationCallbackT callback_); + ECAL_API int AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_); /** - * @brief Remove registration callback. - * - * @param event_ The type of registration. - * - * @return Zero if succeeded. + * @brief Remove registration callback. + * + * @param event_ The type of registration. + * + * @return Zero if succeeded. **/ ECAL_API int RemRegistrationCallback(enum eCAL_Registration_Event event_); diff --git a/ecal/core/include/ecal/ecal_process_mode.h b/src/core/include/ecal/ecal_process_mode.h similarity index 100% rename from ecal/core/include/ecal/ecal_process_mode.h rename to src/core/include/ecal/ecal_process_mode.h diff --git a/ecal/core/include/ecal/ecal_process_severity.h b/src/core/include/ecal/ecal_process_severity.h similarity index 100% rename from ecal/core/include/ecal/ecal_process_severity.h rename to src/core/include/ecal/ecal_process_severity.h diff --git a/src/core/include/ecal/ecal_publisher.h b/src/core/include/ecal/ecal_publisher.h new file mode 100644 index 0000000..aeaf8c9 --- /dev/null +++ b/src/core/include/ecal/ecal_publisher.h @@ -0,0 +1,302 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_publisher.h + * @brief eCAL publisher interface +**/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace eCAL +{ + class CDataWriter; + + /** + * @brief eCAL publisher class. + * + * The CPublisher class is used to send topics to matching eCAL subscribers. The topic is created automatically by the constructor + * or by the Create member function. + *
+ *
+ * For sending the topic payload the publisher class provides an overloaded Send method. The first one is sending the payload as + * a std::string. The second needs a preallocated buffer described by a buffer address and a buffer length. The publisher is not + * taking the ownership for the allocated memory buffer. + *
+ *
+ * An optional time stamp can be attached to the topic payload. + * + **/ + /** + * @code + * // create publisher, topic name "A" + * eCAL::CPublisher pub("A"); + * + * // send string + * std::string send_s = "Hello World "; + * + * // send content + * size_t snd_len = pub.Send(send_s); + * @endcode + **/ + class CPublisher + { + public: + + ECAL_API static constexpr long long DEFAULT_TIME_ARGUMENT = -1; /*!< Use DEFAULT_TIME_ARGUMENT in the `Send()` function to let eCAL determine the send timestamp */ + + /** + * @brief Constructor. + **/ + ECAL_API CPublisher(); + + /** + * @brief Constructor. + * + * @param topic_name_ Unique topic name. + * @param topic_info_ Topic information (encoding, type, descriptor) + **/ + ECAL_API CPublisher(const std::string& topic_name_, const SDataTypeInformation& topic_info_); + + /** + * @brief Constructor. + * + * @param topic_name_ Unique topic name. + **/ + ECAL_API CPublisher(const std::string& topic_name_); + + /** + * @brief Destructor. + **/ + ECAL_API virtual ~CPublisher(); + + /** + * @brief CPublishers are non-copyable + **/ + ECAL_API CPublisher(const CPublisher&) = delete; + + /** + * @brief CPublishers are non-copyable + **/ + ECAL_API CPublisher& operator=(const CPublisher&) = delete; + + /** + * @brief CPublishers are move-enabled + **/ + ECAL_API CPublisher(CPublisher&& rhs) noexcept; + + /** + * @brief CPublishers are move-enabled + **/ + ECAL_API CPublisher& operator=(CPublisher&& rhs) noexcept; + + /** + * @brief Creates this object. + * + * @param topic_name_ Unique topic name. + * @param topic_info_ Topic information (encoding, type, descriptor) + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_); + + /** + * @brief Creates this object. + * + * @param topic_name_ Unique topic name. + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool Create(const std::string& topic_name_); + + /** + * @brief Destroys this object. + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool Destroy(); + + /** + * @brief Setup topic information. + * + * @param topic_info_ Topic information attributes. + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool SetDataTypeInformation(const SDataTypeInformation& topic_info_); + + /** + * @brief Sets publisher attribute. + * + * @param attr_name_ Attribute name. + * @param attr_value_ Attribute value. + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); + + /** + * @brief Removes publisher attribute. + * + * @param attr_name_ Attribute name. + * + * @return True if it succeeds, false if it fails. + * @experimental + **/ + ECAL_API bool ClearAttribute(const std::string& attr_name_); + + /** + * @brief Share topic type. + * + * @param state_ Set type share mode (true == share type). + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool ShareType(bool state_ = true); + + /** + * @brief Share topic description. + * + * @param state_ Set description share mode (true == share description). + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool ShareDescription(bool state_ = true); + + /** + * @brief Set the specific topic id. + * + * @param id_ The topic id for subscriber side filtering (0 == no id). + * + * @return True if it succeeds, false if it fails. + **/ + ECAL_API bool SetID(long long id_); + + /** + * @brief Send a message to all subscribers. + * + * @param buf_ Pointer to content buffer. + * @param len_ Length of buffer. + * @param time_ Send time (-1 = use eCAL system time in us, default = -1). + * + * @return Number of bytes sent. + **/ + ECAL_API size_t Send(const void* buf_, size_t len_, long long time_ = DEFAULT_TIME_ARGUMENT) const; + + /** + * @brief Send a message to all subscribers. + * + * @param payload_ Payload. + * @param time_ Send time (-1 = use eCAL system time in us, default = -1). + * + * @return Number of bytes sent. + **/ + ECAL_API size_t Send(CPayloadWriter& payload_, long long time_ = DEFAULT_TIME_ARGUMENT) const; + + /** + * @brief Send a message to all subscribers. + * + * @param s_ String that contains content to send. + * @param time_ Send time (-1 = use eCAL system time in us, default = -1). + * + * @return Number of bytes sent. + **/ + ECAL_API size_t Send(const std::string& s_, long long time_ = DEFAULT_TIME_ARGUMENT) const; + + /** + * @brief Add callback function for publisher events. + * + * @param type_ The event type to react on. + * @param callback_ The callback function to add. + * + * @return True if succeeded, false if not. + **/ + ECAL_API bool AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_); + + /** + * @brief Remove callback function for publisher events. + * + * @param type_ The event type to remove. + * + * @return True if succeeded, false if not. + **/ + ECAL_API bool RemEventCallback(eCAL_Publisher_Event type_); + + /** + * @brief Query if the publisher is created. + * + * @return True if created, false if not. + **/ + ECAL_API bool IsCreated() const {return(m_created);} + + /** + * @brief Query if the publisher is subscribed. + * + * @return true if subscribed, false if not. + **/ + ECAL_API bool IsSubscribed() const; + + /** + * @brief Query the number of subscribers. + * + * @return Number of subscribers. + **/ + ECAL_API size_t GetSubscriberCount() const; + + /** + * @brief Gets name of the connected topic. + * + * @return The topic name. + **/ + ECAL_API std::string GetTopicName() const; + + /** + * @brief Gets description of the connected topic. + * + * @return The topic information. + **/ + ECAL_API SDataTypeInformation GetDataTypeInformation() const; + + /** + * @brief Dump the whole class state into a string. + * + * @param indent_ Indentation used for dump. + * + * @return The dump string. + **/ + ECAL_API std::string Dump(const std::string& indent_ = "") const; + + protected: + // class members + std::shared_ptr m_datawriter; + long long m_id; + bool m_created; + bool m_initialized; + }; +} diff --git a/ecal/core/include/ecal/ecal_server.h b/src/core/include/ecal/ecal_server.h similarity index 72% rename from ecal/core/include/ecal/ecal_server.h rename to src/core/include/ecal/ecal_server.h index 3a798f5..3491cce 100644 --- a/ecal/core/include/ecal/ecal_server.h +++ b/src/core/include/ecal/ecal_server.h @@ -24,12 +24,14 @@ #pragma once +#include #include #include #include #include #include +#include namespace eCAL { @@ -38,35 +40,35 @@ namespace eCAL /** * @brief Service Server wrapper class. **/ - class ECAL_API CServiceServer + class CServiceServer { public: /** * @brief Constructor. **/ - CServiceServer(); + ECAL_API CServiceServer(); /** * @brief Constructor. * * @param service_name_ Unique service name. **/ - CServiceServer(const std::string& service_name_); + ECAL_API CServiceServer(const std::string& service_name_); /** * @brief Destructor. **/ - virtual ~CServiceServer(); + ECAL_API virtual ~CServiceServer(); /** * @brief CServiceServers are non-copyable **/ - CServiceServer(const CServiceServer&) = delete; + ECAL_API CServiceServer(const CServiceServer&) = delete; /** * @brief CServiceServers are non-copyable **/ - CServiceServer& operator=(const CServiceServer&) = delete; + ECAL_API CServiceServer& operator=(const CServiceServer&) = delete; /** * @brief Creates this object. @@ -75,14 +77,14 @@ namespace eCAL * * @return True if successful. **/ - bool Create(const std::string& service_name_); + ECAL_API bool Create(const std::string& service_name_); /** * @brief Destroys this object. * * @return True if successful. **/ - bool Destroy(); + ECAL_API bool Destroy(); /** * @brief Add method type descriptions. @@ -95,7 +97,7 @@ namespace eCAL * * @return True if successful. **/ - bool AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_); + ECAL_API bool AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_); /** * @brief Add method callback. @@ -107,7 +109,7 @@ namespace eCAL * * @return True if successful. **/ - bool AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_); + ECAL_API bool AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_); /** * @brief Remove method callback. @@ -116,7 +118,7 @@ namespace eCAL * * @return True if successful. **/ - bool RemMethodCallback(const std::string& method_); + ECAL_API bool RemMethodCallback(const std::string& method_); /** * @brief Add server event callback function. @@ -126,7 +128,7 @@ namespace eCAL * * @return True if succeeded, false if not. **/ - bool AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_); + ECAL_API bool AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_); /** * @brief Remove server event callback function. @@ -135,24 +137,24 @@ namespace eCAL * * @return True if succeeded, false if not. **/ - bool RemEventCallback(eCAL_Server_Event type_); + ECAL_API bool RemEventCallback(eCAL_Server_Event type_); /** * @brief Retrieve service name. * * @return The service name. **/ - std::string GetServiceName(); + ECAL_API std::string GetServiceName(); /** * @brief Check connection state. * * @return True if connected, false if not. **/ - bool IsConnected(); + ECAL_API bool IsConnected(); - protected: - CServiceServerImpl* m_service_server_impl; - bool m_created; + private: + std::shared_ptr m_service_server_impl; + bool m_created; }; } diff --git a/ecal/core/include/ecal/ecal_service_info.h b/src/core/include/ecal/ecal_service_info.h similarity index 99% rename from ecal/core/include/ecal/ecal_service_info.h rename to src/core/include/ecal/ecal_service_info.h index bf37ffd..266a963 100644 --- a/ecal/core/include/ecal/ecal_service_info.h +++ b/src/core/include/ecal/ecal_service_info.h @@ -70,4 +70,4 @@ namespace eCAL * @param service_response_ Service response struct containing the (responding) server informations and the response itself. **/ typedef std::function ResponseCallbackT; -}; +} diff --git a/ecal/core/include/ecal/ecal_subscriber.h b/src/core/include/ecal/ecal_subscriber.h similarity index 63% rename from ecal/core/include/ecal/ecal_subscriber.h rename to src/core/include/ecal/ecal_subscriber.h index ddcefb3..0d3a690 100644 --- a/ecal/core/include/ecal/ecal_subscriber.h +++ b/src/core/include/ecal/ecal_subscriber.h @@ -25,9 +25,11 @@ #pragma once #include +#include #include -#include +#include +#include #include #include @@ -78,81 +80,79 @@ namespace eCAL * @endcode **/ - class ECAL_API CSubscriber + class CSubscriber { public: /** * @brief Constructor. **/ - CSubscriber(); + ECAL_API CSubscriber(); /** - * @brief Constructor. + * @brief Constructor. * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional for type checking). - * @param topic_desc_ Type description (optional for description checking). - **/ - CSubscriber(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = ""); + * @param topic_name_ Unique topic name. + * @param topic_info_ Topic information (encoding, type, descriptor) + **/ + ECAL_API CSubscriber(const std::string& topic_name_, const SDataTypeInformation& topic_info_); + + /** + * @brief Constructor. + * + * @param topic_name_ Unique topic name. + **/ + ECAL_API CSubscriber(const std::string& topic_name_); /** * @brief Destructor. **/ - virtual ~CSubscriber(); + ECAL_API virtual ~CSubscriber(); /** * @brief CSubscribers are non-copyable **/ - CSubscriber(const CSubscriber&) = delete; + ECAL_API CSubscriber(const CSubscriber&) = delete; /** * @brief CSubscribers are non-copyable **/ - CSubscriber& operator=(const CSubscriber&) = delete; + ECAL_API CSubscriber& operator=(const CSubscriber&) = delete; /** * @brief CSubscribers are move-enabled **/ - CSubscriber(CSubscriber&& rhs) noexcept; + ECAL_API CSubscriber(CSubscriber&& rhs) noexcept; /** * @brief CSubscribers are move-enabled **/ - CSubscriber& operator=(CSubscriber&& rhs) noexcept; + ECAL_API CSubscriber& operator=(CSubscriber&& rhs) noexcept; /** - * @brief Creates this object. + * @brief Creates this object. * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional for type checking). - * @param topic_desc_ Type description (optional for description checking). - * - * @return true if it succeeds, false if it fails. - **/ - bool Create(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = ""); - - /** - * @brief Destroys this object. + * @param topic_name_ Unique topic name. * - * @return true if it succeeds, false if it fails. + * @return True if it succeeds, false if it fails. **/ - bool Destroy(); + ECAL_API bool Create(const std::string& topic_name_); /** - * @brief Set subscriber quality of service attributes. + * @brief Creates this object. * - * @param qos_ Quality of service policies. + * @param topic_name_ Unique topic name. + * @param topic_info_ Topic information (encoding, type, descriptor) * * @return True if it succeeds, false if it fails. **/ - bool SetQOS(const QOS::SReaderQOS& qos_); + ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_); /** - * @brief Get current subscriber quality of service attributes. + * @brief Destroys this object. * - * @return Quality of service attributes. + * @return true if it succeeds, false if it fails. **/ - QOS::SReaderQOS GetQOS(); + ECAL_API bool Destroy(); /** * @brief Set a set of id's to prefiltering topics (see CPublisher::SetID). @@ -161,7 +161,7 @@ namespace eCAL * * @return True if it succeeds, false if it fails. **/ - bool SetID(const std::set& id_set_); + ECAL_API bool SetID(const std::set& id_set_); /** * @brief Sets subscriber attribute. @@ -172,7 +172,7 @@ namespace eCAL * @return True if it succeeds, false if it fails. * @experimental **/ - bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); + ECAL_API bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); /** * @brief Removes subscriber attribute. @@ -180,21 +180,8 @@ namespace eCAL * @param attr_name_ Attribute name. * * @return True if it succeeds, false if it fails. - * @experimental **/ - bool ClearAttribute(const std::string& attr_name_); - - /** - * @brief Receive a message from the publisher. - * - * @param [out] buf_ Standard string for copying message content. - * @param [out] time_ Time from publisher in us (default = nullptr). - * @param rcv_timeout_ Maximum time before receive operation returns (in milliseconds, -1 means infinite). - * - * @return Length of received buffer. - **/ - [[deprecated]] - size_t Receive(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ = 0) const; + ECAL_API bool ClearAttribute(const std::string& attr_name_); /** * @brief Receive a message from the publisher (able to process zero length buffer). @@ -205,7 +192,7 @@ namespace eCAL * * @return True if it succeeds, false if it fails. **/ - bool ReceiveBuffer(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ = 0) const; + ECAL_API bool ReceiveBuffer(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ = 0) const; /** * @brief Add callback function for incoming receives. @@ -214,14 +201,14 @@ namespace eCAL * * @return True if succeeded, false if not. **/ - bool AddReceiveCallback(ReceiveCallbackT callback_); + ECAL_API bool AddReceiveCallback(ReceiveCallbackT callback_); /** * @brief Remove callback function for incoming receives. * * @return True if succeeded, false if not. **/ - bool RemReceiveCallback(); + ECAL_API bool RemReceiveCallback(); /** * @brief Add callback function for subscriber events. @@ -231,7 +218,7 @@ namespace eCAL * * @return True if succeeded, false if not. **/ - bool AddEventCallback(eCAL_Subscriber_Event type_, SubEventCallbackT callback_); + ECAL_API bool AddEventCallback(eCAL_Subscriber_Event type_, SubEventCallbackT callback_); /** * @brief Remove callback function for subscriber events. @@ -240,52 +227,35 @@ namespace eCAL * * @return True if succeeded, false if not. **/ - bool RemEventCallback(eCAL_Subscriber_Event type_); + ECAL_API bool RemEventCallback(eCAL_Subscriber_Event type_); /** * @brief Query if this object is created. * * @return true if created, false if not. **/ - bool IsCreated() const {return(m_created);} + ECAL_API bool IsCreated() const {return(m_created);} /** - * @brief Query the number of publishers. + * @brief Query the number of publishers. * - * @return Number of publishers. + * @return Number of publishers. **/ - size_t GetPublisherCount() const; + ECAL_API size_t GetPublisherCount() const; /** * @brief Gets name of the connected topic. * * @return The topic name. **/ - std::string GetTopicName() const; + ECAL_API std::string GetTopicName() const; /** - * @brief Gets type of the connected topic. + * @brief Gets description of the connected topic. * - * @return The type name. + * @return The topic information. **/ - std::string GetTypeName() const; - - /** - * @brief Gets description of the connected topic. - * - * @return The description. - **/ - std::string GetDescription() const; - - /** - * @brief Set the timeout parameter for triggering - * the timeout callback. - * - * @param timeout_ The timeout in milliseconds. - * - * @return True if succeeded, false if not. - **/ - bool SetTimeout(int timeout_); + ECAL_API SDataTypeInformation GetDataTypeInformation() const; /** * @brief Dump the whole class state into a string. @@ -294,15 +264,12 @@ namespace eCAL * * @return The dump sting. **/ - std::string Dump(const std::string& indent_ = "") const; + ECAL_API std::string Dump(const std::string& indent_ = "") const; protected: - void InitializeQOS(); - // class members - CDataReader* m_datareader; - struct ECAL_API QOS::SReaderQOS m_qos; + std::shared_ptr m_datareader; bool m_created; bool m_initialized; }; -}; +} diff --git a/ecal/core/include/ecal/ecal_time.h b/src/core/include/ecal/ecal_time.h similarity index 100% rename from ecal/core/include/ecal/ecal_time.h rename to src/core/include/ecal/ecal_time.h diff --git a/ecal/core/include/ecal/ecal_timer.h b/src/core/include/ecal/ecal_timer.h similarity index 78% rename from ecal/core/include/ecal/ecal_timer.h rename to src/core/include/ecal/ecal_timer.h index b35865c..add1c7c 100644 --- a/ecal/core/include/ecal/ecal_timer.h +++ b/src/core/include/ecal/ecal_timer.h @@ -26,6 +26,7 @@ #include #include +#include namespace eCAL { @@ -37,13 +38,13 @@ namespace eCAL * * The CTimer class is used to realize simple time triggered callbacks. **/ - class ECAL_API CTimer + class CTimer { public: /** * @brief Constructor. **/ - CTimer(); + ECAL_API CTimer(); /** * @brief Constructor. @@ -52,12 +53,18 @@ namespace eCAL * @param callback_ The callback function. * @param delay_ Timer callback delay for first call in ms. **/ - CTimer(int timeout_, TimerCallbackT callback_, int delay_ = 0); + ECAL_API CTimer(int timeout_, TimerCallbackT callback_, int delay_ = 0); /** * @brief Destructor. **/ - virtual ~CTimer(); + ECAL_API virtual ~CTimer(); + + // Object not copyable / moveable + CTimer(const CTimer&) = delete; + CTimer& operator=(const CTimer&) = delete; + CTimer(CTimer&& rhs) = delete; + CTimer& operator=(CTimer&& rhs) = delete; /** * @brief Start the timer. @@ -68,22 +75,17 @@ namespace eCAL * * @return True if timer could be started. **/ - bool Start(int timeout_, TimerCallbackT callback_, int delay_ = 0); + ECAL_API bool Start(int timeout_, TimerCallbackT callback_, int delay_ = 0); /** * @brief Stop the timer. * * @return True if timer could be stopped. **/ - bool Stop(); + ECAL_API bool Stop(); protected: // class members - CTimerImpl* m_timer; - - private: - // this object must not be copied. - CTimer(const CTimer&); - CTimer& operator=(const CTimer&); + std::unique_ptr m_timer; }; } diff --git a/ecal/core/include/ecal/ecal_tlayer.h b/src/core/include/ecal/ecal_tlayer.h similarity index 91% rename from ecal/core/include/ecal/ecal_tlayer.h rename to src/core/include/ecal/ecal_tlayer.h index f746561..45e5ad4 100644 --- a/ecal/core/include/ecal/ecal_tlayer.h +++ b/src/core/include/ecal/ecal_tlayer.h @@ -39,7 +39,6 @@ namespace eCAL tlayer_udp_mc = 1, tlayer_shm = 4, tlayer_tcp = 5, - tlayer_inproc = 42, tlayer_all = 255 }; @@ -63,12 +62,10 @@ namespace eCAL { sm_udp_mc = smode_none; sm_shm = smode_none; - sm_inproc = smode_none; sm_tcp = smode_none; } eSendMode sm_udp_mc; //!< udp multicast eSendMode sm_shm; //!< shared memory - eSendMode sm_inproc; //!< inner process (inner process memory forwarding only) eSendMode sm_tcp; //!< tcp }; } diff --git a/src/core/include/ecal/ecal_types.h b/src/core/include/ecal/ecal_types.h new file mode 100644 index 0000000..3c946cc --- /dev/null +++ b/src/core/include/ecal/ecal_types.h @@ -0,0 +1,95 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_types.h + * @brief This file contains type definitions for information associated with a given topic +**/ + +#pragma once +#include + +namespace eCAL +{ + /** + * @brief Optional compile time information associated with a given topic + * (necessary for reflection / runtime type checking) + **/ + struct SDataTypeInformation + { + std::string name; //!< name of the datatype + std::string encoding; //!< encoding of the datatype (e.g. protobuf, flatbuffers, capnproto) + std::string descriptor; //!< descriptor information of the datatype (necessary for reflection) + + //!< @cond + bool operator==(const SDataTypeInformation& other) const + { + return name == other.name && encoding == other.encoding && descriptor == other.descriptor; + } + + bool operator!=(const SDataTypeInformation& other) const + { + return !(*this == other); + } + //!< @endcond + }; + + /** + * @brief Optional compile time information associated with a given topic + * (necessary for reflection / runtime type checking) + **/ + struct STopicInformation + { + SDataTypeInformation topic_type; //!< Data type description of the topic + + //!< @cond + bool operator==(const STopicInformation& other) const + { + return topic_type == other.topic_type; + } + + bool operator!=(const STopicInformation& other) const + { + return !(*this == other); + } + //!< @endcond + }; + + /** + * @brief Optional compile time information associated with a given service method + * (necessary for reflection / runtime type checking) + **/ + struct SServiceMethodInformation + { + SDataTypeInformation request_type; //!< Data type description of the request + SDataTypeInformation response_type; //!< Data type description of the response + + //!< @cond + bool operator==(const SServiceMethodInformation& other) const + { + return request_type == other.request_type && response_type == other.response_type; + } + + bool operator!=(const SServiceMethodInformation& other) const + { + return !(*this == other); + } + //!< @endcond + }; +} diff --git a/ecal/core/include/ecal/ecal_util.h b/src/core/include/ecal/ecal_util.h similarity index 58% rename from ecal/core/include/ecal/ecal_util.h rename to src/core/include/ecal/ecal_util.h index c580dd7..39bb0a8 100644 --- a/ecal/core/include/ecal/ecal_util.h +++ b/src/core/include/ecal/ecal_util.h @@ -24,27 +24,22 @@ #pragma once +#include #include +#include +#include #include #include #include +#include #include + namespace eCAL { namespace Util { - /** - * @brief Retrieve eCAL home path (for starting eCAL applications). - * Windows: $ECAL_HOME/eCAL - * Linux: $HOME/.ecal - * - * @return eCAL home path. - **/ - [[deprecated]] - ECAL_API std::string GeteCALHomePath(); - /** * @brief Retrieve eCAL configuration path. * This is path is for the global eCAL configuration files @@ -80,27 +75,17 @@ namespace eCAL **/ ECAL_API std::string GeteCALActiveIniFile(); - /** - * @brief Retrieve full eCAL default ini file name. - * - * @deprecated Use GeteCALActiveIniFile() instead - * - * @return eCAL default ini file name. - **/ - [[deprecated]] - ECAL_API std::string GeteCALDefaultIniFile(); - /** * @brief Send shutdown event to specified local user process using it's unit name. * - * @param unit_name_ Process unit name. + * @param unit_name_ Process unit name. **/ ECAL_API void ShutdownProcess(const std::string& unit_name_); /** * @brief Send shutdown event to specified local user process using it's process id. * - * @param process_id_ Process id. + * @param process_id_ Process id. **/ ECAL_API void ShutdownProcess(int process_id_); @@ -139,18 +124,13 @@ namespace eCAL **/ ECAL_API void PubShareDescription(bool state_); - struct STopicInfo - { - std::string type_name; //!< Type name of the current topic - std::string type_description; //!< Descriptor string of the current topic. - }; /** * @brief Get complete topic map (including types and descriptions). * - * @param topic_info_map_ Map to store the topic informations. - * Map containing { TopicName -> (Type, Description) } mapping of all topics that are currently known. + * @param topic_info_map_ Map to store the datatype descriptions. + * Map containing { TopicName -> (Encoding, Type, Description) } mapping of all topics that are currently known. **/ - ECAL_API void GetTopics(std::unordered_map& topic_info_map_); + ECAL_API void GetTopics(std::unordered_map& topic_info_map_); /** * @brief Get all topic names. @@ -159,58 +139,23 @@ namespace eCAL **/ ECAL_API void GetTopicNames(std::vector& topic_names_); - /** - * @brief Gets type name of the specified topic. - * - * @param topic_name_ Topic name. - * @param topic_type_ String to store type name. - * - * @return True if succeeded. - **/ - ECAL_API bool GetTopicTypeName(const std::string& topic_name_, std::string& topic_type_); - - /** - * @brief Gets type name of the specified topic. - * - * @param topic_name_ Topic name. - * - * @return Topic type name. - **/ - ECAL_API std::string GetTopicTypeName(const std::string& topic_name_); - - /** - * @brief Gets description of the specified topic. - * - * @param topic_name_ Topic name. - * @param topic_desc_ String to store description. - * - * @return True if succeeded. - **/ - ECAL_API bool GetTopicDescription(const std::string& topic_name_, std::string& topic_desc_); - /** * @brief Gets description of the specified topic. * * @param topic_name_ Topic name. + * @param topic_info_ SDataTypeInformation to be filled by this function. * - * @return Topic description. + * @return True if TopicInformation for specified topic could be retrieved, false otherwise. **/ - ECAL_API std::string GetTopicDescription(const std::string& topic_name_); + ECAL_API bool GetTopicDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& topic_info_); - struct SServiceMethodInfo - { - std::string request_type_name; //!< Type name of the request message - std::string request_type_description; //!< Descriptor string of the request description - std::string response_type_name; //!< Type name of the response message - std::string response_type_description; //!< Descriptor string of the response message - }; /** * @brief Get complete service map (including request and response types and descriptions). * - * @param service_info_map_ Map to store the topic informations. + * @param service_info_map_ Map to store the datatype descriptions. * Map { (ServiceName, MethodName) -> ( (ReqType, ReqDescription), (RespType, RespDescription) ) } mapping of all currently known services. **/ - ECAL_API void GetServices(std::map, Util::SServiceMethodInfo>& service_info_map_); + ECAL_API void GetServices(std::map, SServiceMethodInformation>& service_info_map_); /** * @brief Get all service/method names. @@ -244,53 +189,22 @@ namespace eCAL ECAL_API bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_); /** - * @brief Gets type name of the specified topic. - * - * This function is deprecated with eCAL 5.10. Please use GetTopicTypeName. - * - * @param topic_name_ Topic name. - * @param topic_type_ String to store type name. - * - * @return True if succeeded. - **/ - [[deprecated]] - ECAL_API bool GetTypeName(const std::string& topic_name_, std::string& topic_type_); - - /** - * @brief Gets type name of the specified topic. - * - * This function is deprecated with eCAL 5.10. Please use GetTopicTypeName. - * - * @param topic_name_ Topic name. - * - * @return Topic type name. - **/ - [[deprecated]] - ECAL_API std::string GetTypeName(const std::string& topic_name_); - - /** - * @brief Gets description of the specified topic. + * @brief Splits the topic type (eCAL < 5.12) into encoding and types (>= eCAL 5.12) * - * This function is deprecated with eCAL 5.10. Please use GetTopicDescription. + * @param combined_topic_type_ "Old" typename. * - * @param topic_name_ Topic name. - * @param topic_desc_ String to store description. - * - * @return True if succeeded. + * @return std::pair(encoding, typename). **/ - [[deprecated]] - ECAL_API bool GetDescription(const std::string& topic_name_, std::string& topic_desc_); + ECAL_API std::pair SplitCombinedTopicType(const std::string& combined_topic_type_); /** - * @brief Gets description of the specified topic. - * - * This function is deprecated with eCAL 5.10. Please use GetTopicDescription. + * @brief Combine separate encoding and type iinformation (>= eCAL 5.12) into a combined typename (eCAL < 5.12) * - * @param topic_name_ Topic name. + * @param topic_encoding_ Topic Encoding + * @param topic_type_ Topic Type * - * @return Topic description. + * @return "Old" typename. ( encoding:typename ). **/ - [[deprecated]] - ECAL_API std::string GetDescription(const std::string& topic_name_); + ECAL_API std::string CombinedTopicEncodingAndType(const std::string& topic_encoding_, const std::string& topic_type_); } } diff --git a/ecal/core/include/ecal/ecalc.h b/src/core/include/ecal/ecalc.h similarity index 90% rename from ecal/core/include/ecal/ecalc.h rename to src/core/include/ecal/ecalc.h index 24cec92..fb74dd8 100644 --- a/ecal/core/include/ecal/ecalc.h +++ b/src/core/include/ecal/ecalc.h @@ -35,18 +35,15 @@ #include #include #include -#include #include #include #include #include -#include #include #include -#include #include #include #include #include -#endif /*ecalc_h_included*/ +#endif /* ecalc_h_included */ diff --git a/ecal/core/src/sys_usage.h b/src/core/include/ecal/ecalc_types.h similarity index 67% rename from ecal/core/src/sys_usage.h rename to src/core/include/ecal/ecalc_types.h index 0c93f9f..61d8870 100644 --- a/ecal/core/src/sys_usage.h +++ b/src/core/include/ecal/ecalc_types.h @@ -5,9 +5,9 @@ * 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. @@ -18,9 +18,21 @@ */ /** - * @brief System usage monitoring + * @file ecalc_types.h + * @brief File including shared types for eCAL C API +**/ + +#ifndef ecalc_types_h_included +#define ecalc_types_h_included + +/** + * @brief Flag to indicate eCAL to allocate/deallocate memory. **/ +#define ECAL_ALLOCATE_4ME 0 -#pragma once +/** + * @brief Common handle for eCAL C API function calls. +**/ +typedef void* ECAL_HANDLE; -float GetCPULoad(); +#endif /* ecalc_types_h_included */ diff --git a/ecal/core/include/ecal/msg/dynamic.h b/src/core/include/ecal/msg/dynamic.h similarity index 100% rename from ecal/core/include/ecal/msg/dynamic.h rename to src/core/include/ecal/msg/dynamic.h diff --git a/ecal/core/include/ecal/msg/protobuf/client.h b/src/core/include/ecal/msg/protobuf/client.h similarity index 95% rename from ecal/core/include/ecal/msg/protobuf/client.h rename to src/core/include/ecal/msg/protobuf/client.h index 362f029..c96f1d5 100644 --- a/ecal/core/include/ecal/msg/protobuf/client.h +++ b/src/core/include/ecal/msg/protobuf/client.h @@ -24,8 +24,9 @@ #pragma once +#include #include -#include +#include // protobuf includes #ifdef _MSC_VER @@ -168,7 +169,7 @@ namespace eCAL * * @return True if successful. **/ - [[deprecated]] + ECAL_DEPRECATE_SINCE_5_10("Please use the method bool Call(const std::string& method_name_, const google::protobuf::Message& request_, const int timeout_, ServiceResponseVecT* service_response_vec_) instead. This function will be removed in eCAL6.") bool Call(const std::string& host_name_, const std::string& method_name_, const google::protobuf::Message& request_, struct SServiceResponse& service_response_, google::protobuf::Message& response_) { ServiceResponseVecT service_response_vec; diff --git a/src/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h b/src/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h new file mode 100644 index 0000000..c8bd90f --- /dev/null +++ b/src/core/include/ecal/msg/protobuf/dynamic_json_subscriber.h @@ -0,0 +1,266 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_proto_dyn_json_sub.h + * @brief dynamic protobuf message to json decoder +**/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 0) // disable proto warnings +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif +#include +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace eCAL +{ + namespace protobuf + { + /** + * @brief eCAL dynamic protobuf to json subscriber. + **/ + class CDynamicJSONSubscriber + { + public: + /** + * @brief Constructor. + **/ + CDynamicJSONSubscriber(); + + /** + * @brief Constructor. + * + * @param topic_name_ Unique topic name. + **/ + explicit CDynamicJSONSubscriber(const std::string& topic_name_); + + /** + * @brief Destructor. + **/ + ~CDynamicJSONSubscriber(); + + CDynamicJSONSubscriber(const CDynamicJSONSubscriber&) = delete; + CDynamicJSONSubscriber& operator=(const CDynamicJSONSubscriber&) = delete; + CDynamicJSONSubscriber(CDynamicJSONSubscriber&& rhs) = delete; + CDynamicJSONSubscriber& operator=(CDynamicJSONSubscriber&& rhs) = delete; + + /** + * @brief Creates this object. + * + * @param topic_name_ Unique topic name. + * + * @return true if it succeeds, false if it fails. + **/ + void Create(const std::string& topic_name_); + + /** + * @brief Destroys this object. + * + * @return true if it succeeds, false if it fails. + **/ + void Destroy(); + + /** + * @brief Query if this object is created. + * + * @return true if created, false if not. + **/ + bool IsCreated() const { return(m_created); } + + /** + * @brief Add callback function for incoming receives. + * + * @param callback_ The callback function to add. + * + * @return True if succeeded, false if not. + **/ + bool AddReceiveCallback(ReceiveCallbackT callback_); + + /** + * @brief Remove callback function for incoming receives. + * + * @return True if succeeded, false if not. + **/ + bool RemReceiveCallback(); + + protected: + void OnReceive(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_); + + bool m_created; + std::unique_ptr m_msg_decoder; + std::string m_msg_string; + eCAL::CSubscriber m_msg_sub; + ReceiveCallbackT m_msg_callback; + + std::string m_topic_type; + std::string m_topic_type_full; + + std::string m_topic_desc; + std::shared_ptr m_resolver; + google::protobuf::DescriptorPool m_descriptor_pool; + }; + /** @example proto_dyn_json.cpp + * This is an example how to use CDynamicJSONSubscriber to receive dynamic google::protobuf data as a JSON string with eCAL. + */ + + inline CDynamicJSONSubscriber::CDynamicJSONSubscriber() : + m_created(false), + m_msg_decoder(nullptr), + m_msg_string() + {} + + inline CDynamicJSONSubscriber::CDynamicJSONSubscriber(const std::string& topic_name_) : + m_created(false), + m_msg_decoder(nullptr), + m_msg_string() + { + Create(topic_name_); + } + + inline CDynamicJSONSubscriber::~CDynamicJSONSubscriber() + { + Destroy(); + } + + inline void CDynamicJSONSubscriber::Create(const std::string& topic_name_) + { + if (m_created) return; + + // create message decoder + m_msg_decoder = std::make_unique(); + + // create subscriber + m_msg_sub.Create(topic_name_); + + // add callback + m_msg_sub.AddReceiveCallback(std::bind(&CDynamicJSONSubscriber::OnReceive, this, std::placeholders::_1, std::placeholders::_2)); + + m_created = true; + } + + inline void CDynamicJSONSubscriber::Destroy() + { + if (!m_created) return; + + // remove callback + m_msg_sub.RemReceiveCallback(); + + // destroy subscriber + m_msg_sub.Destroy(); + + // delete message decoder + m_msg_decoder.reset(); + + m_created = false; + } + + inline bool CDynamicJSONSubscriber::AddReceiveCallback(ReceiveCallbackT callback_) + { + if (!m_created) return false; + m_msg_callback = callback_; + return true; + } + + inline bool CDynamicJSONSubscriber::RemReceiveCallback() + { + if (!m_created) return false; + m_msg_callback = nullptr; + return true; + } + + inline void CDynamicJSONSubscriber::OnReceive(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) + { + if (m_msg_string.empty()) + { + // get topic type + SDataTypeInformation topic_info; + //nodiscard??? + eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info); + m_topic_type_full = topic_info.name; + m_topic_type = m_topic_type_full.substr(m_topic_type_full.find_last_of('.') + 1, m_topic_type_full.size()); + m_topic_type_full = "/" + m_topic_type_full; + + if (m_topic_type.empty()) + { + std::cout << "could not get type for topic " << topic_name_ << std::endl; + return; + } + + // get topic description + m_topic_desc = topic_info.descriptor; + if (m_topic_desc.empty()) + { + std::cout << "could not get description for topic " << topic_name_ << std::endl; + return; + } + + std::string error_s; + google::protobuf::FileDescriptorSet proto_desc; + proto_desc.ParseFromString(m_topic_desc); + const std::shared_ptr msg(m_msg_decoder->GetProtoMessageFromDescriptorSet(proto_desc, m_topic_type, error_s)); + m_resolver.reset(google::protobuf::util::NewTypeResolverForDescriptorPool("", m_msg_decoder->GetDescriptorPool())); + } + + // decode message and execute callback + //if(msg_callback && msg_ptr && msg_ptr->ParseFromArray(data_->buf, data_->size)) + if (m_msg_callback) + { + + google::protobuf::util::JsonOptions options; + options.always_print_primitive_fields = true; + + std::string binary_input; + binary_input.assign((char*)data_->buf, static_cast(data_->size)); + m_msg_string.clear(); + auto status = google::protobuf::util::BinaryToJsonString(m_resolver.get(), m_topic_type_full, binary_input, &m_msg_string, options); + if (status.ok()) + { + SReceiveCallbackData cb_data; + cb_data.buf = (void*)m_msg_string.c_str(); + cb_data.size = (long)m_msg_string.size(); + cb_data.time = data_->time; + m_msg_callback(topic_name_, &cb_data); + } + } + } + } +} diff --git a/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h b/src/core/include/ecal/msg/protobuf/dynamic_publisher.h similarity index 85% rename from ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h rename to src/core/include/ecal/msg/protobuf/dynamic_publisher.h index e1e7d2f..19ac4ff 100644 --- a/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h +++ b/src/core/include/ecal/msg/protobuf/dynamic_publisher.h @@ -28,7 +28,7 @@ #include #include -#include +#include #ifdef _MSC_VER #pragma warning(push, 0) // disable proto warnings @@ -56,8 +56,8 @@ namespace eCAL * @param msg_ Protobuf message object. **/ CDynamicPublisher(const std::string& topic_name_, std::shared_ptr msg_) - : CMsgPublisher(topic_name_, GetTypeNameFromMessage(msg_.get()), GetDescriptionFromMessage(msg_.get())), - m_msg{ msg_ } {} + : CMsgPublisher(topic_name_, GetTopicInformationFromMessage(msg_.get())) + , m_msg{ msg_ } {} /** * @brief Constructor. @@ -66,9 +66,8 @@ namespace eCAL * @param proto_type_name_ Protobuf message type name. **/ CDynamicPublisher(const std::string& topic_name_, const std::string& proto_type_name_) - : CMsgPublisher(topic_name_, GetTypeNameFromMessage(CreateMessageByName(proto_type_name_).get()), - GetDescriptionFromMessage(CreateMessageByName(proto_type_name_).get())), - m_msg{ CreateMessageByName(proto_type_name_) } {} + : CMsgPublisher(topic_name_, GetTopicInformationFromMessage(CreateMessageByName(proto_type_name_).get())) + , m_msg{ CreateMessageByName(proto_type_name_) } {} /** * @brief Send message. @@ -106,14 +105,9 @@ namespace eCAL private: size_t Send(const google::protobuf::Message& msg_, long long time_ = -1) = delete; - std::string GetTypeName() const override + SDataTypeInformation GetDataTypeInformation() const override { - return GetTypeNameFromMessage(m_msg.get()); - } - - std::string GetDescription() const override - { - return GetDescriptionFromMessage(m_msg.get()); + return GetTopicInformationFromMessage(m_msg.get()); } size_t GetSize(const google::protobuf::Message& msg_) const override @@ -130,15 +124,18 @@ namespace eCAL return (msg_.SerializeToArray((void*)buffer_, (int)size_)); } - - static std::string GetTypeNameFromMessage(const google::protobuf::Message* msg_ptr_) + static SDataTypeInformation GetTopicInformationFromMessage(const google::protobuf::Message* msg_ptr_) { assert(msg_ptr_); - return ("proto:" + msg_ptr_->GetTypeName()); + SDataTypeInformation topic_info; + topic_info.encoding = "proto"; + topic_info.name = msg_ptr_->GetTypeName(); + topic_info.descriptor = GetDescriptorFromMessage(msg_ptr_); + return topic_info; } - static std::string GetDescriptionFromMessage(const google::protobuf::Message* msg_ptr_) + static std::string GetDescriptorFromMessage(const google::protobuf::Message* msg_ptr_) { assert(msg_ptr_); diff --git a/ecal/core/include/ecal/msg/protobuf/dynamic_subscriber.h b/src/core/include/ecal/msg/protobuf/dynamic_subscriber.h similarity index 87% rename from ecal/core/include/ecal/msg/protobuf/dynamic_subscriber.h rename to src/core/include/ecal/msg/protobuf/dynamic_subscriber.h index 57b860f..4d78d36 100644 --- a/ecal/core/include/ecal/msg/protobuf/dynamic_subscriber.h +++ b/src/core/include/ecal/msg/protobuf/dynamic_subscriber.h @@ -24,11 +24,14 @@ #pragma once -#include -#include #include -#include +#include #include +#include + +#include +#include +#include #ifdef _MSC_VER #pragma warning(push, 0) // disable proto warnings @@ -108,11 +111,8 @@ namespace eCAL /** * @brief get a Pointer to a temporary message that can be passed to receive - * - * This function is deprecated and will return a nullptr now. * **/ - [[deprecated]] google::protobuf::Message* getMessagePointer(); /** @@ -155,13 +155,13 @@ namespace eCAL std::shared_ptr CreateMessagePointer(const std::string& topic_name_); - bool created; - std::string topic_name; - eCAL::protobuf::CProtoDynDecoder* msg_decoder; - std::shared_ptr msg_ptr; - eCAL::CSubscriber msg_sub; - ProtoMsgCallbackT msg_callback; - ProtoErrorCallbackT err_callback; + bool created; + std::string topic_name; + std::unique_ptr msg_decoder; + std::shared_ptr msg_ptr; + eCAL::CSubscriber msg_sub; + ProtoMsgCallbackT msg_callback; + ProtoErrorCallbackT err_callback; private: // this object must not be copied. @@ -188,7 +188,7 @@ namespace eCAL inline CDynamicSubscriber::~CDynamicSubscriber() { Destroy(); - }; + } inline void CDynamicSubscriber::Create(const std::string& topic_name_) { @@ -198,7 +198,7 @@ namespace eCAL topic_name = topic_name_; // create message decoder - msg_decoder = new eCAL::protobuf::CProtoDynDecoder(); + msg_decoder = std::make_unique(); // create subscriber msg_sub.Create(topic_name_); @@ -217,14 +217,26 @@ namespace eCAL msg_ptr = nullptr; // delete message decoder - delete msg_decoder; + msg_decoder.reset(); created = false; } inline google::protobuf::Message* CDynamicSubscriber::getMessagePointer() { - return nullptr; + try + { + // Create Message Pointer for our topic name. + if (msg_ptr == nullptr) + { + msg_ptr = CreateMessagePointer(topic_name); + } + } + catch (DynamicReflectionException& /*e*/) + { + return nullptr; + } + return msg_ptr.get(); } inline bool CDynamicSubscriber::Receive(google::protobuf::Message& msg_, long long* time_, int rcv_timeout_) @@ -297,21 +309,22 @@ namespace eCAL } } - /* - * Might throw DynamicReflectionException! - */ + /** + * Might throw DynamicReflectionException! + **/ inline std::shared_ptr CDynamicSubscriber::CreateMessagePointer(const std::string& topic_name_) { // get topic type - std::string topic_type = eCAL::Util::GetTopicTypeName(topic_name_); - topic_type = topic_type.substr(topic_type.find_first_of(':') + 1, topic_type.size()); + SDataTypeInformation topic_info; + eCAL::Util::GetTopicDataTypeInformation(topic_name, topic_info); + std::string topic_type{ topic_info.name }; topic_type = topic_type.substr(topic_type.find_last_of('.') + 1, topic_type.size()); if (StrEmptyOrNull(topic_type)) { throw DynamicReflectionException("CDynamicSubscriber: Could not get type for topic " + std::string(topic_name_)); } - std::string topic_desc = eCAL::Util::GetTopicDescription(topic_name_); + std::string topic_desc = topic_info.descriptor; if (StrEmptyOrNull(topic_desc)) { throw DynamicReflectionException("CDynamicSubscriber: Could not get description for topic " + std::string(topic_name_)); diff --git a/src/core/include/ecal/msg/protobuf/ecal_proto_dyn.h b/src/core/include/ecal/msg/protobuf/ecal_proto_dyn.h new file mode 100644 index 0000000..897c9c4 --- /dev/null +++ b/src/core/include/ecal/msg/protobuf/ecal_proto_dyn.h @@ -0,0 +1,419 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_proto_dyn.h + * @brief dynamic protobuf message decoder +**/ + +#pragma once + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 0) // disable proto warnings +#endif +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif +#include +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#include + +namespace eCAL +{ + namespace protobuf + { + /** + * @brief eCAL dynamic protobuf decoder. + * + * The CProtoDynDecoder class is used to decode a protobuf message using protobuf reflection interface. The returned + * google message objects are owned by CProtoDynDecoder and freed on destruction. + * + * @code + * // create dynamic decoder + * std::string error_s; + * eCAL::CProtoDynDecoder decoder; + * std::shared_ptr msg_obj(decoder.GetProtoMessageFromFile("foo.proto", "FooMessage", error_s)); + * + * // receive a message + * std::string msg_s = ReceiveMessageFromAnyWhere("foo"); + * + * // decode message + * msg_obj->ParseFromString(msg_s); + * + * // print message + * std::cout << msg_obj->DebugString() << std::endl; + * @endcode + **/ + class CProtoDynDecoder + { + public: + /** + * @brief Create message from proto file. + * + * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. + * + * @param proto_filename_ Proto file name. + * @param msg_type_ Type name. + * @param [out] error_s_ Error string. + * + * @return google message object or nullptr if generation failed (details see error_s_) + **/ + google::protobuf::Message* GetProtoMessageFromFile(const std::string& proto_filename_, const std::string& msg_type_, std::string& error_s_); + + /** + * @brief Create message from proto string. + * + * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. + * + * @param proto_string_ Proto string. + * @param msg_type_ Type name. + * @param [out] error_s_ Error string. + * + * @return google message object or nullptr if generation failed (details see error_s_) + **/ + google::protobuf::Message* GetProtoMessageFromString(const std::string& proto_string_, const std::string& msg_type_, std::string& error_s_); + + /** + * @brief Create message from proto descriptor. + * + * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. + * + * @param proto_desc_ Proto descriptor. + * @param msg_type_ Type name. + * @param [out] error_s_ Error string. + * + * @return google message object or nullptr if generation failed (details see error_s_) + **/ + google::protobuf::Message* GetProtoMessageFromDescriptor(const google::protobuf::FileDescriptorProto& proto_desc_, const std::string& msg_type_, std::string& error_s_); + + /** + * @brief Create message from serialized proto descriptor string. + * + * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. + * + * @param msg_desc_ Serialized message descriptor. + * @param msg_type_ Type name. + * @param [out] error_s_ Error string. + * + * @return google message object or nullptr if generation failed (details see error_s_) + **/ + google::protobuf::Message* GetProtoMessageFromDescriptor(const std::string& msg_desc_, const std::string& msg_type_, std::string& error_s_); + + /** + * @brief Create message from proto descriptor set. + * + * Note: Ownership of the google::protobuf::Message pointer is passed to the caller. + * + * @param proto_desc_set_ Proto descriptor set. + * @param msg_type_ Type name. + * @param [out] error_s_ Error string. + * + * @return google message object or nullptr if generation failed (details see error_s_) + **/ + google::protobuf::Message* GetProtoMessageFromDescriptorSet(const google::protobuf::FileDescriptorSet& proto_desc_set_, const std::string& msg_type_, std::string& error_s_); + + /** + * @brief Returns the DescriptorPool member variable + **/ + google::protobuf::DescriptorPool* GetDescriptorPool(); + + /** + * @brief Create proto descriptor from proto file. + * + * @param proto_filename_ Proto file name. + * @param [out] file_desc_proto_ Type name. + * @param [out] error_s_ Error string. + * + * @return true if succeeded otherwise false (details see error_s_) + **/ + static bool GetFileDescriptorFromFile(const std::string& proto_filename_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_); + + /** + * @brief Create proto descriptor from proto string. + * + * @param proto_string_ Proto string. + * @param [out] file_desc_proto_ Type name. + * @param [out] error_s_ Error string. + * + * @return true if succeeded otherwise false (details see error_s_) + **/ + static bool GetFileDescriptorFromString(const std::string& proto_string_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_); + + protected: + google::protobuf::DescriptorPool m_descriptor_pool; + google::protobuf::DynamicMessageFactory m_message_factory; + }; + + class ParserErrorCollector : public google::protobuf::io::ErrorCollector + { + public: + ParserErrorCollector() {} + ~ParserErrorCollector() {} + + std::string Get() { return (m_ss.str()); } + + // Indicates that there was an error in the input at the given line and + // column numbers. The numbers are zero-based, so you may want to add + // 1 to each before printing them. + void AddError(int line_, int column_, const std::string& msg_) + { + Add(line_, column_, "ERROR: " + msg_); + } + + // Indicates that there was a warning in the input at the given line and + // column numbers. The numbers are zero-based, so you may want to add + // 1 to each before printing them. + void AddWarning(int line_, int column_, const std::string& msg_) + { + Add(line_, column_, "WARNING: " + msg_); + } + + private: + void Add(int line_, int column_, const std::string& msg_) + { + m_ss << line_ << ":" << column_ << " " << msg_ << std::endl; + } + + std::stringstream m_ss; + }; + + class DescriptorErrorCollector : public google::protobuf::DescriptorPool::ErrorCollector + { + public: + DescriptorErrorCollector() {} + ~DescriptorErrorCollector() {} + + std::string Get() { return (m_ss.str()); } + + void AddError( + const std::string& filename, // File name in which the error occurred. + const std::string& element_name, // Full name of the erroneous element. + const google::protobuf::Message* descriptor, // Descriptor of the erroneous element. + ErrorLocation location, // One of the location constants, above. + const std::string& message // Human-readable error message. + ) + { + Add(filename, element_name, descriptor, location, "ERROR: " + message); + } + + void AddWarning( + const std::string& filename, // File name in which the error occurred. + const std::string& element_name, // Full name of the erroneous element. + const google::protobuf::Message* descriptor, // Descriptor of the erroneous element. + ErrorLocation location, // One of the location constants, above. + const std::string& message // Human-readable error message. + ) + { + Add(filename, element_name, descriptor, location, "WARNING: " + message); + } + + private: + void Add( + const std::string& filename, + const std::string& element_name, + const google::protobuf::Message* /*descriptor*/, + ErrorLocation location, + const std::string& message) + { + m_ss << filename << " " << element_name << " " << location << " " << message << std::endl; + } + + std::stringstream m_ss; + }; + + google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromFile(const std::string& proto_filename_, const std::string& msg_type_, std::string& error_s_) + { + google::protobuf::FileDescriptorProto proto; + if (!GetFileDescriptorFromFile(proto_filename_, &proto, error_s_)) return (nullptr); + google::protobuf::FileDescriptorSet pset; + google::protobuf::FileDescriptorProto* pdesc = pset.add_file(); + pdesc->CopyFrom(proto); + return (GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_)); + } + + google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromString(const std::string& proto_string_, const std::string& msg_type_, std::string& error_s_) + { + google::protobuf::FileDescriptorProto proto; + if (!GetFileDescriptorFromString(proto_string_, &proto, error_s_)) return (nullptr); + google::protobuf::FileDescriptorSet pset; + google::protobuf::FileDescriptorProto* pdesc = pset.add_file(); + pdesc->CopyFrom(proto); + return (GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_)); + } + + google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromDescriptor(const google::protobuf::FileDescriptorProto& proto_desc_, const std::string& msg_type_, std::string& error_s_) + { + google::protobuf::FileDescriptorSet pset; + google::protobuf::FileDescriptorProto* pdesc = pset.add_file(); + pdesc->CopyFrom(proto_desc_); + return (GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_)); + } + + google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromDescriptor(const std::string& msg_desc_, const std::string& msg_type_, std::string& error_s_) + { + // create file descriptor set + google::protobuf::FileDescriptorSet pset; + if (!pset.ParseFromString(msg_desc_)) + { + error_s_ = "Cannot get file descriptor of message: " + msg_type_; + return (nullptr); + } + + // create message object + google::protobuf::Message* proto_msg = GetProtoMessageFromDescriptorSet(pset, msg_type_, error_s_); + if (!proto_msg) + { + return (nullptr); + } + + return (proto_msg); + } + + google::protobuf::Message* CProtoDynDecoder::GetProtoMessageFromDescriptorSet(const google::protobuf::FileDescriptorSet& proto_desc_set_, const std::string& msg_type_, std::string& error_s_) + { + // check if msg_type_ is available in descriptor pool + const google::protobuf::Descriptor* desc = m_descriptor_pool.FindMessageTypeByName(msg_type_); + if (desc != nullptr) + { + const google::protobuf::Message* prototype_msg = m_message_factory.GetPrototype(desc); + if (prototype_msg != nullptr) + { + // ownership passed to caller here ! + google::protobuf::Message* proto_msg = prototype_msg->New(); + if (proto_msg == nullptr) + { + error_s_ = "Failed in prototype_msg->New(); to create mutable message"; + return (nullptr); + } + return (proto_msg); + } + } + + // suppose you want to parse a message type with a specific type name. + DescriptorErrorCollector error_collector; + const google::protobuf::FileDescriptor* file_desc = nullptr; + for (auto it = 0; it < proto_desc_set_.file_size(); ++it) + { + file_desc = m_descriptor_pool.BuildFileCollectingErrors(proto_desc_set_.file(it), &error_collector); + if (file_desc == nullptr) + { + error_s_ = error_collector.Get(); + return (nullptr); + } + if ((file_desc->message_type_count() > 0) && + (file_desc->message_type(0)->name() == msg_type_)) + { + break; + } + } + if (file_desc == nullptr) + { + error_s_ = "Cannot get file descriptor of message: " + msg_type_; + return (nullptr); + } + const google::protobuf::Descriptor* message_desc = file_desc->FindMessageTypeByName(msg_type_); + if (message_desc == nullptr) + { + error_s_ = "Cannot get message descriptor of message: " + msg_type_; + return (nullptr); + } + + const google::protobuf::Message* prototype_msg = m_message_factory.GetPrototype(message_desc); + if (prototype_msg == nullptr) + { + error_s_ = "Cannot create prototype message from message descriptor"; + return (nullptr); + } + + // ownership passed to caller here ! + google::protobuf::Message* proto_msg = prototype_msg->New(); + if (proto_msg == nullptr) + { + error_s_ = "Failed in prototype_msg->New(); to create mutable message"; + return (nullptr); + } + return (proto_msg); + } + + google::protobuf::DescriptorPool* CProtoDynDecoder::GetDescriptorPool() + { + return &m_descriptor_pool; + } + + bool CProtoDynDecoder::GetFileDescriptorFromFile(const std::string& proto_filename_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_) + { + std::ifstream fs(proto_filename_); + if (!fs.is_open()) + { + std::cout << "Cannot open .proto file: " << proto_filename_; + return (false); + } + std::stringstream ss; + ss << fs.rdbuf(); + + std::string proto_str = ss.str(); + + return (GetFileDescriptorFromString(proto_str, file_desc_proto_, error_s_)); + } + + bool CProtoDynDecoder::GetFileDescriptorFromString(const std::string& proto_string_, google::protobuf::FileDescriptorProto* file_desc_proto_, std::string& error_s_) + { + std::stringstream ss; + ss << proto_string_; + google::protobuf::io::IstreamInputStream proto_string_input_stream(&ss); + + google::protobuf::io::Tokenizer tokenizer(&proto_string_input_stream, nullptr); + google::protobuf::compiler::Parser parser; + ParserErrorCollector error_collector; + parser.RecordErrorsTo(&error_collector); + if (!parser.Parse(&tokenizer, file_desc_proto_)) + { + error_s_ = error_collector.Get(); + return (false); + } + + // here we walk around a bug in protocol buffers that + // |Parser::Parse| does not set name (.proto filename) in + // file_desc_proto. + if (!file_desc_proto_->has_name()) + { + file_desc_proto_->set_name("foo.proto"); + } + + return (true); + } + } +} diff --git a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_hlp.h b/src/core/include/ecal/msg/protobuf/ecal_proto_hlp.h similarity index 97% rename from lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_hlp.h rename to src/core/include/ecal/msg/protobuf/ecal_proto_hlp.h index 7a2bc2a..b60b8af 100644 --- a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_hlp.h +++ b/src/core/include/ecal/msg/protobuf/ecal_proto_hlp.h @@ -18,6 +18,7 @@ */ /** + * @file ecal_proto_hlp.h * @brief protobuf message description handling **/ @@ -29,8 +30,7 @@ // protobuf includes #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4100 4127 4146 4505 4800 4189 4592) // disable proto warnings +#pragma warning(push, 0) // disable proto warnings #endif #include #ifdef _MSC_VER diff --git a/src/core/include/ecal/msg/protobuf/publisher.h b/src/core/include/ecal/msg/protobuf/publisher.h new file mode 100644 index 0000000..e60f580 --- /dev/null +++ b/src/core/include/ecal/msg/protobuf/publisher.h @@ -0,0 +1,186 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file publisher.h + * @brief eCAL publisher interface for google::protobuf message definitions +**/ + +#pragma once + +#include +#include +#include + +// protobuf includes +#ifdef _MSC_VER +#pragma warning(push, 0) // disable proto warnings +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// stl includes +#include +#include +#include + +namespace eCAL +{ + namespace protobuf + { + /** + * @brief eCAL google::protobuf publisher class. + * + * Publisher template class for google::protobuf messages. For details see documentation of CPublisher class. + * + **/ + template + class CPublisher : public eCAL::CPublisher + { + class CPayload : public eCAL::CPayloadWriter + { + public: + explicit CPayload(const google::protobuf::Message& message_) : + message(message_) {}; + + ~CPayload() override = default; + + CPayload(const CPayload&) = default; + CPayload(CPayload&&) noexcept = default; + + CPayload& operator=(const CPayload&) = delete; + CPayload& operator=(CPayload&&) noexcept = delete; + + bool WriteFull(void* buf_, size_t len_) override + { + return message.SerializeToArray(buf_, static_cast(len_)); + } + + size_t GetSize() override { + size_t size(0); +#if GOOGLE_PROTOBUF_VERSION >= 3001000 + size = static_cast(message.ByteSizeLong()); +#else + size = static_cast(message.ByteSize()); +#endif + return(size); + }; + + private: + const google::protobuf::Message& message; + }; + + public: + /** + * @brief Constructor. + **/ + CPublisher() : eCAL::CPublisher() + { + } + + /** + * @brief Constructor. + * + * @param topic_name_ Unique topic name. + **/ + + // 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. + // 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_) : eCAL::CPublisher(topic_name_, CPublisher::GetDataTypeInformation()) + { + } + + /** + * @brief Copy Constructor is not available. + **/ + CPublisher(const CPublisher&) = delete; + + /** + * @brief Move Constructor + **/ + CPublisher(CPublisher&&) = default; + + /** + * @brief Destructor. + **/ + ~CPublisher() override = default; + + /** + * @brief Copy assignment is not available. + **/ + CPublisher& operator=(const CPublisher&) = delete; + + /** + * @brief Move assignment + **/ + CPublisher& operator=(CPublisher&&) = default; + + /** + * @brief Creates this object. + * + * @param topic_name_ Unique topic name. + * + * @return True if it succeeds, false if it fails. + **/ + bool Create(const std::string& topic_name_) + { + return(eCAL::CPublisher::Create(topic_name_, GetDataTypeInformation())); + } + + /** + * @brief Send a serialized message to all subscribers. + * + * @param msg_ The message object. + * @param time_ Time stamp. + * + * @return Number of bytes sent. + **/ + size_t Send(const T& msg_, long long time_ = DEFAULT_TIME_ARGUMENT) + { + CPayload payload{ msg_ }; + eCAL::CPublisher::Send(payload, time_); + return(0); + } + + private: + /** + * @brief Get datatype description of the protobuf message. + * + * @return Topic information. + **/ + struct SDataTypeInformation GetDataTypeInformation() const + { + struct SDataTypeInformation topic_info; + static T msg{}; + topic_info.encoding = "proto"; + topic_info.name = msg.GetTypeName(); + topic_info.descriptor = protobuf::GetProtoMessageDescription(msg); + return topic_info; + } + + }; + /** @example person_snd.cpp + * This is an example how to use eCAL::CPublisher to send google::protobuf data with eCAL. To receive the data, see @ref person_rec.cpp . + */ + } +} diff --git a/ecal/core/include/ecal/msg/protobuf/server.h b/src/core/include/ecal/msg/protobuf/server.h similarity index 97% rename from ecal/core/include/ecal/msg/protobuf/server.h rename to src/core/include/ecal/msg/protobuf/server.h index 812f21c..5fa84c1 100644 --- a/ecal/core/include/ecal/msg/protobuf/server.h +++ b/src/core/include/ecal/msg/protobuf/server.h @@ -25,7 +25,7 @@ #pragma once #include -#include +#include #include // protobuf includes @@ -38,6 +38,7 @@ #endif // stl includes +#include #include #include #include @@ -192,6 +193,7 @@ namespace eCAL google::protobuf::Message* request(m_service->GetRequestPrototype(method_descriptor).New()); if (!request->ParseFromString(request_)) { + std::cerr << "Protobuf Service " << GetServiceName() << " failed to parse request message!" << std::endl; delete request; return -1; } diff --git a/ecal/core/include/ecal/msg/protobuf/subscriber.h b/src/core/include/ecal/msg/protobuf/subscriber.h similarity index 70% rename from ecal/core/include/ecal/msg/protobuf/subscriber.h rename to src/core/include/ecal/msg/protobuf/subscriber.h index 3893145..4b30683 100644 --- a/ecal/core/include/ecal/msg/protobuf/subscriber.h +++ b/src/core/include/ecal/msg/protobuf/subscriber.h @@ -25,7 +25,7 @@ #pragma once #include -#include +#include // protobuf includes #ifdef _MSC_VER @@ -68,33 +68,40 @@ namespace eCAL * @param topic_name_ Unique topic name. **/ - // call the function via its class becase it's a virtual function that is called in constructor/destructor,- - // where the vtable is not created yet or it's destructed. - CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, CSubscriber::GetTypeName(), CSubscriber::GetDescription()) + // 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 CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, CSubscriber::GetDataTypeInformation()) { } /** - * @brief Copy Constructor is not available. + * @brief Destructor + **/ + ~CSubscriber() override + { + this->Destroy(); + } + + /** + * @brief Copy Constructor is not available. **/ CSubscriber(const CSubscriber&) = delete; /** - * @brief Copy Assignment is not available. + * @brief Copy Assignment is not available. **/ CSubscriber& operator=(const CSubscriber&) = delete; /** - * @brief Move Constructor + * @brief Move Constructor **/ CSubscriber(CSubscriber&&) = default; /** - * @brief Move assignment + * @brief Move assignment **/ CSubscriber& operator=(CSubscriber&&) = default; - /** * @brief Creates this object. * @@ -104,32 +111,26 @@ namespace eCAL **/ bool Create(const std::string& topic_name_) { - return(CMsgSubscriber::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name of the protobuf message. - * - * @return Type name. - **/ - std::string GetTypeName() const override - { - static T msg; - return("proto:" + msg.GetTypeName()); + return(CMsgSubscriber::Create(topic_name_, GetDataTypeInformation())); } private: /** - * @brief Get file descriptor string of the protobuf message. + * @brief Get topic information of the protobuf message. * - * @return Description string. + * @return Topic information. **/ - std::string GetDescription() const override + SDataTypeInformation GetDataTypeInformation() const override { - static T msg; - return(protobuf::GetProtoMessageDescription(msg)); + SDataTypeInformation topic_info; + static T msg{}; + topic_info.encoding = "proto"; + topic_info.name = msg.GetTypeName(); + topic_info.descriptor = protobuf::GetProtoMessageDescription(msg); + return topic_info; } + /** * @brief Deserialize the message object from a message buffer. * @@ -151,7 +152,7 @@ namespace eCAL }; /** @example person_rec.cpp - * This is an example how to use eCAL::CSubscriber to receive google::protobuf data with eCAL. To send the data, see @ref person_snd.cpp . - */ + * This is an example how to use eCAL::CSubscriber to receive google::protobuf data with eCAL. To send the data, see @ref person_snd.cpp . + **/ } } diff --git a/ecal/core/include/ecal/msg/publisher.h b/src/core/include/ecal/msg/publisher.h similarity index 52% rename from ecal/core/include/ecal/msg/publisher.h rename to src/core/include/ecal/msg/publisher.h index 1132132..ae1556b 100644 --- a/ecal/core/include/ecal/msg/publisher.h +++ b/src/core/include/ecal/msg/publisher.h @@ -24,13 +24,15 @@ #pragma once +#include #include +#include #include #include #include -#include -#include +#include +#include namespace eCAL { @@ -45,57 +47,68 @@ namespace eCAL { public: /** - * @brief Constructor. + * @brief Default Constructor. + * Using this constructor, the object is not actually in a usable state. + * Before being able to send data, one has to call the `Create()` function, first. **/ CMsgPublisher() : CPublisher() { } /** - * @brief Constructor. + * @brief Constructor, that automatically intializes the Publisher. + * This should be the preferred constructor. * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional for type checking). - * @param topic_desc_ Type description (optional for description checking). + * @param topic_name_ Unique topic name. + * @param topic_info_ Struct that contains information of the datatype (name, encoding, description) of the topic. **/ - CMsgPublisher(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = "") : CPublisher(topic_name_, topic_type_, topic_desc_) + CMsgPublisher(const std::string& topic_name_, const struct SDataTypeInformation& topic_info_) : CPublisher(topic_name_, topic_info_) { } /** - * @brief Copy Constructor is not available. + * @brief Constructor, that automatically intializes the Publisher. + * If no datatype information about the topic is available, this constructor can be used. + * + * @param topic_name_ Unique topic name. + **/ + explicit CMsgPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, GetDataTypeInformation()) + { + } + + /** + * @brief Copy Constructor is not available. **/ CMsgPublisher(const CMsgPublisher&) = delete; /** - * @brief Copy Constructor is not available. + * @brief Copy Constructor is not available. **/ CMsgPublisher& operator=(const CMsgPublisher&) = delete; /** - * @brief Move Constructor + * @brief Move Constructor **/ CMsgPublisher(CMsgPublisher&&) = default; /** - * @brief Move assignment + * @brief Move assignment **/ CMsgPublisher& operator=(CMsgPublisher&&) = default; - virtual ~CMsgPublisher() = default; + ~CMsgPublisher() override = default; /** - * @brief Creates this object. + * @brief Creates this object. * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional for type checking). - * @param topic_desc_ Type description (optional for description checking). + * @param topic_name_ Unique topic name. + * @param topic_info_ Associated datatype description. * - * @return True if it succeeds, false if it fails. + * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = "") + bool Create(const std::string& topic_name_, const struct SDataTypeInformation& topic_info_) { - return(CPublisher::Create(topic_name_, topic_type_, topic_desc_)); + return(CPublisher::Create(topic_name_, topic_info_)); } /** @@ -116,81 +129,45 @@ namespace eCAL * * @return Number of bytes sent. **/ - size_t Send(const T& msg_, long long time_ = -1) + size_t Send(const T& msg_, long long time_ = DEFAULT_TIME_ARGUMENT) { // this is an optimization ... // if there is no subscription we do not waste time for - // serialization but we send an empty payload + // serialization, but we send an empty payload // to still do some statistics like message clock - // counting and frequency calculation for the monitoring layer + // counting and frequency calculation for the monitoring layer if (!IsSubscribed()) { - return(CPublisher::Send(nullptr, 0)); + return(CPublisher::Send(nullptr, 0, time_)); } // if we have a subscription allocate memory for the // binary stream, serialize the message into the // buffer and finally send it with a binary publisher size_t size = GetSize(msg_); - if(size > 0) - { - m_buffer.resize(size); - if(Serialize(msg_, &m_buffer[0], m_buffer.size())) - { - return(CPublisher::Send(&m_buffer[0], size, time_)); - } - } - else - { - // send a zero payload length message to trigger the subscriber side - return(CPublisher::Send(nullptr, 0, time_)); - } - return(0); - } - - /** - * @brief Send a serialized message to all subscribers synchronized with acknowledge timeout (see also ShmSetAcknowledgeTimeout). - * - * This synchronized mode is currently implemented for local interprocess communication (shm-ecal layer) only. - * - * @param msg_ The message object. - * @param time_ Time stamp. - * @param acknowledge_timeout_ms_ Maximum time to wait for all subscribers acknowledge feedback in ms (buffer received and processed). - * - * @return Number of bytes sent. - **/ - size_t Send(const T& msg_, long long time_, long long acknowledge_timeout_ms_) - { - // see CMsgPublisher::Send - if (!IsSubscribed()) - { - return(CPublisher::Send(nullptr, 0)); - } - - // see CMsgPublisher::Send - size_t size = GetSize(msg_); if (size > 0) { m_buffer.resize(size); if (Serialize(msg_, &m_buffer[0], m_buffer.size())) { - return(CPublisher::Send(&m_buffer[0], size, time_, acknowledge_timeout_ms_)); + return(CPublisher::Send(&m_buffer[0], size, time_)); } } else { // send a zero payload length message to trigger the subscriber side - return(CPublisher::Send(nullptr, 0, time_, acknowledge_timeout_ms_)); + return(CPublisher::Send(nullptr, 0, time_)); } return(0); } + protected: + // We cannot make it pure virtual, as it would break a bunch of implementations, who are not (yet) implementing this function + virtual struct SDataTypeInformation GetDataTypeInformation() const { return SDataTypeInformation{}; } private: - virtual std::string GetTypeName() const = 0; - virtual std::string GetDescription() const = 0; virtual size_t GetSize(const T& msg_) const = 0; virtual bool Serialize(const T& msg_, char* buffer_, size_t size_) const = 0; std::vector m_buffer; }; -} \ No newline at end of file +} diff --git a/ecal/core/include/ecal/msg/string/publisher.h b/src/core/include/ecal/msg/string/publisher.h similarity index 65% rename from ecal/core/include/ecal/msg/string/publisher.h rename to src/core/include/ecal/msg/string/publisher.h index ab4c9c2..4465b68 100644 --- a/ecal/core/include/ecal/msg/string/publisher.h +++ b/src/core/include/ecal/msg/string/publisher.h @@ -24,6 +24,7 @@ #pragma once +#include #include #include @@ -56,40 +57,29 @@ namespace eCAL * @param topic_name_ Unique topic name. **/ - // call the function via its class becase it's a virtual function that is called in constructor/destructor,- - // where the vtable is not created yet or it's destructed. - CPublisher(const std::string& topic_name_) : CMsgPublisher(topic_name_, CPublisher::GetTypeName(), CPublisher::GetDescription()) + // 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_) : CMsgPublisher(topic_name_, GetDataTypeInformation()) { } /** - * @brief Constructor. - * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional). - * @param topic_desc_ Type description (optional). - **/ - CPublisher(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_) : CMsgPublisher(topic_name_, topic_type_, topic_desc_) - { - } - - /** - * @brief Copy Constructor is not available. + * @brief Copy Constructor is not available. **/ CPublisher(const CPublisher&) = delete; /** - * @brief Copy Constructor is not available. + * @brief Copy Constructor is not available. **/ CPublisher& operator=(const CPublisher&) = delete; /** - * @brief Move Constructor + * @brief Move Constructor **/ CPublisher(CPublisher&&) = default; /** - * @brief Move assignment + * @brief Move assignment **/ CPublisher& operator=(CPublisher&&) = default; @@ -102,30 +92,24 @@ namespace eCAL **/ bool Create(const std::string& topic_name_) { - return(CMsgPublisher::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name. - * - * @return Always returns "base:std::string". - **/ - std::string GetTypeName() const override - { - return("base:std::string"); + return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation())); } private: /** - * @brief Get description. + * @brief Get topic information of the message. * - * @return Empty string. + * @return Topic information. **/ - std::string GetDescription() const override + SDataTypeInformation GetDataTypeInformation() const override { - return(""); + SDataTypeInformation topic_info; + topic_info.encoding = "base"; + topic_info.name = "std::string"; + // empty descriptor + return topic_info; } - + /** * @brief Get size of the string object. * @@ -139,7 +123,7 @@ namespace eCAL } /** - * @brief Copy the string object into a preallocated char buffer. + * @brief Copy the string object into a pre-allocated char buffer. * * @param msg_ The message object. * @param [out] buffer_ Target buffer. @@ -158,7 +142,7 @@ namespace eCAL } }; /** @example minimal_snd.cpp - * This is an example how to use eCAL::CPublisher to send a std::string with eCAL. To receive the strings, see @ref minimal_rec.cpp . - */ + * This is an example how to use eCAL::CPublisher to send a std::string with eCAL. To receive the strings, see @ref minimal_rec.cpp . + **/ } } diff --git a/ecal/core/include/ecal/msg/string/subscriber.h b/src/core/include/ecal/msg/string/subscriber.h similarity index 69% rename from ecal/core/include/ecal/msg/string/subscriber.h rename to src/core/include/ecal/msg/string/subscriber.h index 838a231..2ccb576 100644 --- a/ecal/core/include/ecal/msg/string/subscriber.h +++ b/src/core/include/ecal/msg/string/subscriber.h @@ -56,29 +56,37 @@ namespace eCAL * @param topic_name_ Unique topic name. **/ - // call the function via its class becase it's a virtual function that is called in constructor/destructor,- - // where the vtable is not created yet or it's destructed. - CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, CSubscriber::GetTypeName(), CSubscriber::GetDescription()) + // 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 CSubscriber(const std::string& topic_name_) : CMsgSubscriber(topic_name_, CSubscriber::GetDataTypeInformation()) { } /** - * @brief Copy Constructor is not available. + * @brief Destructor + **/ + ~CSubscriber() override + { + this->Destroy(); + } + + /** + * @brief Copy Constructor is not available. **/ CSubscriber(const CSubscriber&) = delete; /** - * @brief Copy Constructor is not available. + * @brief Copy Assignment is not available. **/ CSubscriber& operator=(const CSubscriber&) = delete; /** - * @brief Move Constructor + * @brief Move Constructor **/ CSubscriber(CSubscriber&&) = default; /** - * @brief Move assignment + * @brief Move assignment **/ CSubscriber& operator=(CSubscriber&&) = default; @@ -91,28 +99,22 @@ namespace eCAL **/ bool Create(const std::string& topic_name_) { - return(CMsgSubscriber::Create(topic_name_, GetTypeName(), GetDescription())); - } - - /** - * @brief Get type name. - * - * @return Always returns "base:std::string". - **/ - std::string GetTypeName() const override - { - return("base:std::string"); + return(CMsgSubscriber::Create(topic_name_, GetDataTypeInformation())); } private: /** - * @brief Get description. + * @brief Get topic information of the protobuf message. * - * @return Always returns empty string. + * @return Topic information. ("base", "std::string", "") **/ - std::string GetDescription() const override + SDataTypeInformation GetDataTypeInformation() const override { - return(""); + SDataTypeInformation topic_info; + topic_info.encoding = "base"; + topic_info.name = "std::string"; + // empty descriptor + return topic_info; } /** @@ -132,7 +134,7 @@ namespace eCAL }; /** @example minimal_rec.cpp - * This is an example how to use eCAL::CSubscriber to receive a std::string with eCAL. To send the strings, see @ref minimal_snd.cpp . - */ + * This is an example how to use eCAL::CSubscriber to receive a std::string with eCAL. To send the strings, see @ref minimal_snd.cpp . + **/ } } diff --git a/ecal/core/include/ecal/msg/subscriber.h b/src/core/include/ecal/msg/subscriber.h similarity index 64% rename from ecal/core/include/ecal/msg/subscriber.h rename to src/core/include/ecal/msg/subscriber.h index 6746b61..f7c7c9c 100644 --- a/ecal/core/include/ecal/msg/subscriber.h +++ b/src/core/include/ecal/msg/subscriber.h @@ -24,8 +24,9 @@ #pragma once -#include +#include #include +#include #include #include @@ -47,30 +48,31 @@ namespace eCAL { public: /** - * @brief Constructor. + * @brief Constructor. **/ CMsgSubscriber() : CSubscriber() { } /** - * @brief Constructor. + * @brief Constructor. * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional for type checking). - * @param topic_desc_ Type description (optional for description checking). + * @param topic_name_ Unique topic name. + * @param topic_info_ Topic type information (encoding, type, descriptor). **/ - CMsgSubscriber(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = "") : CSubscriber(topic_name_, topic_type_, topic_desc_ ) + CMsgSubscriber(const std::string& topic_name_, const struct SDataTypeInformation& topic_info_) : CSubscriber(topic_name_, topic_info_) { } + virtual ~CMsgSubscriber() = default; + /** - * @brief Copy Constructor is not available. + * @brief Copy Constructor is not available. **/ CMsgSubscriber(const CMsgSubscriber&) = delete; /** - * @brief Copy Constructor is not available. + * @brief Copy Constructor is not available. **/ CMsgSubscriber& operator=(const CMsgSubscriber&) = delete; @@ -93,7 +95,7 @@ namespace eCAL } /** - * @brief Move assignment + * @brief Move assignment **/ CMsgSubscriber& operator=(CMsgSubscriber&& rhs) { @@ -113,26 +115,23 @@ namespace eCAL return *this; } - virtual ~CMsgSubscriber() {} - /** - * @brief Creates this object. + * @brief Creates this object. * - * @param topic_name_ Unique topic name. - * @param topic_type_ Type name (optional for type checking). - * @param topic_desc_ Type description (optional for description checking). + * @param topic_name_ Unique topic name. + * @param topic_info_ Topic type information (encoding, type, descriptor). * - * @return true if it succeeds, false if it fails. + * @return true if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_, const std::string& topic_type_ = "", const std::string& topic_desc_ = "") + bool Create(const std::string& topic_name_, const struct SDataTypeInformation& topic_info_) { - return(CSubscriber::Create(topic_name_, topic_type_, topic_desc_)); + return(CSubscriber::Create(topic_name_, topic_info_)); } /** - * @brief Destroys this object. + * @brief Destroys this object. * - * @return true if it succeeds, false if it fails. + * @return true if it succeeds, false if it fails. **/ bool Destroy() { @@ -141,20 +140,20 @@ namespace eCAL } /** - * @brief Receive deserialized message. + * @brief Receive deserialized message. * - * @param [out] msg_ The message object. - * @param [out] time_ Optional receive time stamp. - * @param rcv_timeout_ Receive timeout in ms. + * @param [out] msg_ The message object. + * @param [out] time_ Optional receive time stamp. + * @param rcv_timeout_ Receive timeout in ms. * - * @return True if a message could received, false otherwise. + * @return True if a message could received, false otherwise. **/ bool Receive(T& msg_, long long* time_ = nullptr, int rcv_timeout_ = 0) const { assert(IsCreated()); std::string rec_buf; bool success = CSubscriber::ReceiveBuffer(rec_buf, time_, rcv_timeout_); - if(!success) return(false); + if (!success) return(false); return(Deserialize(msg_, rec_buf.c_str(), rec_buf.size())); } @@ -166,46 +165,57 @@ namespace eCAL * @param time_ Message time stamp. * @param clock_ Message writer clock. * @param id_ Message id. - **/ + **/ typedef std::function MsgReceiveCallbackT; /** - * @brief Add receive callback for incoming messages. + * @brief Add receive callback for incoming messages. * - * @param callback_ The callback function. + * @param callback_ The callback function. * - * @return True if it succeeds, false if it fails. + * @return True if it succeeds, false if it fails. **/ bool AddReceiveCallback(MsgReceiveCallbackT callback_) { assert(IsCreated()); RemReceiveCallback(); - m_cb_callback = callback_; - auto callback = std::bind(&CMsgSubscriber::ReceiveCallback, this, std::placeholders::_1, std::placeholders::_2); + { + std::lock_guard callback_lock(m_cb_callback_mutex); + m_cb_callback = callback_; + } + auto callback = std::bind(&CMsgSubscriber::ReceiveCallback, this, std::placeholders::_1, std::placeholders::_2); return(CSubscriber::AddReceiveCallback(callback)); } /** - * @brief Remove receive callback for incoming messages. + * @brief Remove receive callback for incoming messages. * - * @return True if it succeeds, false if it fails. + * @return True if it succeeds, false if it fails. **/ bool RemReceiveCallback() { - if(m_cb_callback == nullptr) return(false); + bool ret = CSubscriber::RemReceiveCallback(); + + std::lock_guard callback_lock(m_cb_callback_mutex); + if (m_cb_callback == nullptr) return(false); m_cb_callback = nullptr; - return(CSubscriber::RemReceiveCallback()); + return(ret); } - private: - virtual std::string GetTypeName() const = 0; - virtual std::string GetDescription() const = 0; +protected: + // We cannot make it pure virtual, as it would break a bunch of implementations, who are not (yet) implementing this function + virtual struct SDataTypeInformation GetDataTypeInformation() const { return SDataTypeInformation{}; } virtual bool Deserialize(T& msg_, const void* buffer_, size_t size_) const = 0; + private: void ReceiveCallback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_) { - MsgReceiveCallbackT fn_callback(m_cb_callback); + MsgReceiveCallbackT fn_callback = nullptr; + { + std::lock_guard callback_lock(m_cb_callback_mutex); + fn_callback = m_cb_callback; + } if(fn_callback == nullptr) return; @@ -216,6 +226,7 @@ namespace eCAL } } + std::mutex m_cb_callback_mutex; MsgReceiveCallbackT m_cb_callback; }; } diff --git a/src/core/include/ecal/types/monitoring.h b/src/core/include/ecal/types/monitoring.h new file mode 100644 index 0000000..6b206d6 --- /dev/null +++ b/src/core/include/ecal/types/monitoring.h @@ -0,0 +1,231 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_monitoring_pb.h + * @brief eCAL monitoring interface using structs +**/ + +#pragma once + +#include + +#include +#include +#include + +namespace eCAL +{ + namespace Monitoring + { + namespace Entity + { + constexpr unsigned int Publisher = 0x001; + constexpr unsigned int Subscriber = 0x002; + constexpr unsigned int Server = 0x004; + constexpr unsigned int Client = 0x008; + constexpr unsigned int Process = 0x010; + constexpr unsigned int Host = 0x020; + + constexpr unsigned int All = Publisher + | Subscriber + | Server + | Client + | Process + | Host; + + constexpr unsigned int None = 0x000; + } + + enum eTLayerType + { + tl_none = 0, + tl_ecal_udp_mc = 1, + tl_ecal_shm = 4, + tl_ecal_tcp = 5, + }; + + struct TLayer + { + eTLayerType type = tl_none; // tlayer; //!< transport layer details + int32_t tsize; //!< topic size + + int32_t connections_loc; //!< number of local connected entities + int32_t connections_ext; //!< number of external connected entities + int32_t message_drops; //!< dropped messages + + int64_t did; //!< data send id (publisher setid) + int64_t dclock; //!< data clock (send / receive action) + int32_t dfreq; //!< data frequency (send / receive samples per second) [mHz] + + std::map attr; //!< generic topic description + }; + + struct SProcessMon // methods; // processes; // publisher; // subscriber; // server; // clients; //& tokens, @@ -102,7 +101,7 @@ namespace eCAL ECAL_API UdpConfigVersion GetUdpMulticastConfigVersion() { - std::string udp_config_version_string = eCALPAR(NET, UDP_MULTICAST_CONFIG_VERSION); + const std::string udp_config_version_string = eCALPAR(NET, UDP_MULTICAST_CONFIG_VERSION); if (udp_config_version_string == "v1") return UdpConfigVersion::V1; if (udp_config_version_string == "v2") @@ -120,13 +119,9 @@ namespace eCAL ECAL_API int GetUdpMulticastRcvBufSizeBytes () { return eCALPAR(NET, UDP_MULTICAST_RCVBUF); } ECAL_API bool IsUdpMulticastJoinAllIfEnabled () { return eCALPAR(NET, UDP_MULTICAST_JOIN_ALL_IF_ENABLED); } - - ECAL_API int GetMaxUdpBandwidthBytesPerSecond () { return eCALPAR(NET, BANDWIDTH_MAX_UDP); } - ECAL_API bool IsUdpMulticastRecEnabled () { return eCALPAR(NET, UDP_MC_REC_ENABLED); } ECAL_API bool IsShmRecEnabled () { return eCALPAR(NET, SHM_REC_ENABLED); } ECAL_API bool IsTcpRecEnabled () { return eCALPAR(NET, TCP_REC_ENABLED); } - ECAL_API bool IsInprocRecEnabled () { return eCALPAR(NET, INPROC_REC_ENABLED); } ECAL_API bool IsNpcapEnabled () { return eCALPAR(NET, NPCAP_ENABLED); } @@ -134,6 +129,8 @@ namespace eCAL ECAL_API int GetTcpPubsubWriterThreadpoolSize () { return eCALPAR(NET, TCP_PUBSUB_NUM_EXECUTOR_WRITER); } ECAL_API int GetTcpPubsubMaxReconnectionAttemps () { return eCALPAR(NET, TCP_PUBSUB_MAX_RECONNECTIONS); } + ECAL_API std::string GetHostGroupName () { return eCALPAR(NET, HOST_GROUP_NAME); } + ///////////////////////////////////// // time ///////////////////////////////////// @@ -150,18 +147,18 @@ namespace eCAL // monitoring ///////////////////////////////////// - ECAL_API int GetMonitoringTimeoutMs () { return eCALPAR(MON, TIMEOUT); } - ECAL_API std::string GetMonitoringFilterExcludeList () { return eCALPAR(MON, FILTER_EXCL); } - ECAL_API std::string GetMonitoringFilterIncludeList () { return eCALPAR(MON, FILTER_INCL); } - ECAL_API eCAL_Logging_Filter GetConsoleLogFilter () { return ParseLogLevel(eCALPAR(MON, LOG_FILTER_CON)); } - ECAL_API eCAL_Logging_Filter GetFileLogFilter () { return ParseLogLevel(eCALPAR(MON, LOG_FILTER_FILE)); } - ECAL_API eCAL_Logging_Filter GetUdpLogFilter () { return ParseLogLevel(eCALPAR(MON, LOG_FILTER_UDP)); } + ECAL_API int GetMonitoringTimeoutMs () { return eCALPAR(MON, TIMEOUT); } + ECAL_API std::string GetMonitoringFilterExcludeList () { return eCALPAR(MON, FILTER_EXCL); } + ECAL_API std::string GetMonitoringFilterIncludeList () { return eCALPAR(MON, FILTER_INCL); } + ECAL_API eCAL_Logging_Filter GetConsoleLogFilter () { return ParseLogLevel(eCALPAR(MON, LOG_FILTER_CON)); } + ECAL_API eCAL_Logging_Filter GetFileLogFilter () { return ParseLogLevel(eCALPAR(MON, LOG_FILTER_FILE)); } + ECAL_API eCAL_Logging_Filter GetUdpLogFilter () { return ParseLogLevel(eCALPAR(MON, LOG_FILTER_UDP)); } ///////////////////////////////////// // sys ///////////////////////////////////// - ECAL_API std::string GetEcalSysFilterExcludeList () { return eCALPAR(SYS, FILTER_EXCL); } + ECAL_API std::string GetEcalSysFilterExcludeList () { return eCALPAR(SYS, FILTER_EXCL); } ///////////////////////////////////// // publisher @@ -170,7 +167,6 @@ namespace eCAL ECAL_API TLayer::eSendMode GetPublisherUdpMulticastMode () { return TLayer::eSendMode(eCALPAR(PUB, USE_UDP_MC)); } ECAL_API TLayer::eSendMode GetPublisherShmMode () { return TLayer::eSendMode(eCALPAR(PUB, USE_SHM)); } ECAL_API TLayer::eSendMode GetPublisherTcpMode () { return TLayer::eSendMode(eCALPAR(PUB, USE_TCP)); } - ECAL_API TLayer::eSendMode GetPublisherInprocMode () { return TLayer::eSendMode(eCALPAR(PUB, USE_INPROC)); } ECAL_API size_t GetMemfileMinsizeBytes () { return static_cast(eCALPAR(PUB, MEMFILE_MINSIZE)); } ECAL_API size_t GetMemfileOverprovisioningPercentage () { return static_cast(eCALPAR(PUB, MEMFILE_RESERVE)); } @@ -181,6 +177,16 @@ namespace eCAL ECAL_API bool IsTopicTypeSharingEnabled () { return (eCALPAR(PUB, SHARE_TTYPE) != 0); } ECAL_API bool IsTopicDescriptionSharingEnabled () { return (eCALPAR(PUB, SHARE_TDESC) != 0); } + ///////////////////////////////////// + // service + ///////////////////////////////////// + ECAL_API bool IsServiceProtocolV0Enabled () { return (eCALPAR(SERVICE, PROTOCOL_V0) != 0); } + ECAL_API bool IsServiceProtocolV1Enabled () { return (eCALPAR(SERVICE, PROTOCOL_V1) != 0); } + + ///////////////////////////////////// + // experimemtal + ///////////////////////////////////// + namespace Experimental { ECAL_API bool IsShmMonitoringEnabled () { return eCALPAR(EXP, SHM_MONITORING_ENABLED); } diff --git a/ecal/core/src/ecal_config_reader.cpp b/src/core/src/config/ecal_config_reader.cpp similarity index 85% rename from ecal/core/src/ecal_config_reader.cpp rename to src/core/src/config/ecal_config_reader.cpp index 4a09c86..d960ee4 100644 --- a/ecal/core/src/ecal_config_reader.cpp +++ b/src/core/src/config/ecal_config_reader.cpp @@ -27,18 +27,15 @@ #include "ecal_def.h" #include "ecal_config_reader.h" #include "ecal_global_accessors.h" -#include "getenvvar.h" +#include "util/getenvvar.h" +#include +#include #include #include -#include #include -#ifdef ECAL_OS_WINDOWS -#include "ecal_win_main.h" -#endif - #ifdef ECAL_OS_LINUX #include #include @@ -46,7 +43,9 @@ #include #endif +#if ECAL_CORE_CONFIG_INIFILE #include +#endif namespace { @@ -59,13 +58,13 @@ namespace bool fileexists(const std::string& fname_) { - std::ifstream infile(fname_); + const std::ifstream infile(fname_); return infile.good(); } bool direxists(const std::string& path_) { - EcalUtils::Filesystem::FileStatus status(path_, EcalUtils::Filesystem::Current); + const EcalUtils::Filesystem::FileStatus status(path_, EcalUtils::Filesystem::Current); return (status.IsOk() && (status.GetType() == EcalUtils::Filesystem::Type::Dir)); } @@ -132,10 +131,10 @@ namespace // 2. The ecal.ini exists in that directory bool IsValidConfigFilePath(const std::string& path) { - if (path.empty()) { return false; }; + if (path.empty()) { return false; } // check existence of ecal.ini file - EcalUtils::Filesystem::FileStatus ecal_ini_status(path + std::string(ECAL_DEFAULT_CFG), EcalUtils::Filesystem::Current); + const EcalUtils::Filesystem::FileStatus ecal_ini_status(path + std::string(ECAL_DEFAULT_CFG), EcalUtils::Filesystem::Current); if (ecal_ini_status.IsOk() && (ecal_ini_status.GetType() == EcalUtils::Filesystem::Type::RegularFile)) { return true; @@ -196,17 +195,17 @@ namespace eCAL // ----------------------------------------------------------- // precedence 1: ECAL_DATA variable (windows and linux) // ----------------------------------------------------------- - std::string ecal_data_path{ eCALDataEnvPath() }; + const std::string ecal_data_path{ eCALDataEnvPath() }; // ----------------------------------------------------------- // precedence 2: cmake configured data paths (linux only) // ----------------------------------------------------------- - std::string cmake_data_path{ eCALDataCMakePath() }; + const std::string cmake_data_path{ eCALDataCMakePath() }; // ----------------------------------------------------------- // precedence 3: system data path // ----------------------------------------------------------- - std::string system_data_path(eCALDataSystemPath()); + const std::string system_data_path(eCALDataSystemPath()); // Check for first directory which contains the ini file. std::vector search_directories{ ecal_data_path, cmake_data_path, system_data_path }; @@ -279,16 +278,13 @@ namespace eCAL //////////////////////////////////////////////////////// // CConfigImpl //////////////////////////////////////////////////////// +#if ECAL_CORE_CONFIG_INIFILE + class CConfigImpl : public CSimpleIni { public: - CConfigImpl() - { - } - - virtual ~CConfigImpl() - { - } + CConfigImpl() = default; + virtual ~CConfigImpl() = default; void OverwriteKeys(const std::vector& key_vec_) { @@ -323,19 +319,19 @@ namespace eCAL } // update command line keys - for (auto full_key : m_overwrite_keys) + for (const auto& full_key : m_overwrite_keys) { auto sec_pos = full_key.find_last_of('/'); if (sec_pos == std::string::npos) continue; - std::string section = full_key.substr(0, sec_pos); + const std::string section = full_key.substr(0, sec_pos); std::string key = full_key.substr(sec_pos+1); auto val_pos = key.find_first_of(':'); if (val_pos == std::string::npos) continue; - std::string value = key.substr(val_pos+1); + const std::string value = key.substr(val_pos+1); key = key.substr(0, val_pos); - SI_Error err = SetValue(section.c_str(), key.c_str(), value.c_str()); + const SI_Error err = SetValue(section.c_str(), key.c_str(), value.c_str()); if (err == SI_FAIL) { std::cout << "Error: Could not overwrite key " << key << " in section " << section << "."; @@ -346,20 +342,34 @@ namespace eCAL std::vector m_overwrite_keys; }; +#else // ECAL_CORE_CONFIG_INIFILE + + class CConfigImpl + { + public: + CConfigImpl() = default; + virtual ~CConfigImpl() = default; + + void OverwriteKeys(const std::vector& /*key_vec_*/) {} + void AddFile(std::string& /*file_name_*/) {} + + std::string GetValue(const std::string& /*section_*/, const std::string& /*key_*/, const std::string& default_) { return default_;} + long GetLongValue(const std::string& /*section_*/, const std::string& /*key_*/, long default_) { return default_; } + double GetDoubleValue(const std::string& /*section_*/, const std::string& /*key_*/, double default_) { return default_; } + }; + +#endif // ECAL_CORE_CONFIG_INIFILE + //////////////////////////////////////////////////////// // CConfigBase //////////////////////////////////////////////////////// CConfig::CConfig() : m_impl(nullptr) { - m_impl = new CConfigImpl(); + m_impl = std::make_unique(); } - CConfig::~CConfig() - { - delete m_impl; - m_impl = nullptr; - } + CConfig::~CConfig() = default; void CConfig::OverwriteKeys(const std::vector& key_vec_) { @@ -381,8 +391,8 @@ namespace eCAL // use_udp_mc = 2 // ------------------------------------------------------------------ { - int use_tcp = get("publisher", "use_tcp", 0); - int use_udp_mc = get("publisher", "use_udp_mc", 0); + const int use_tcp = get("publisher", "use_tcp", 0); + const int use_udp_mc = get("publisher", "use_udp_mc", 0); if ((use_tcp == 2) && (use_udp_mc == 2)) { std::cerr << "eCAL config error: to set [publisher/use_tcp] and [publisher/use_udp_mc] both on auto mode (2) is not allowed" << std::endl; diff --git a/ecal/core/src/ecal_config_reader.h b/src/core/src/config/ecal_config_reader.h similarity index 97% rename from ecal/core/src/ecal_config_reader.h rename to src/core/src/config/ecal_config_reader.h index 757e12e..ab5d13a 100644 --- a/ecal/core/src/ecal_config_reader.h +++ b/src/core/src/config/ecal_config_reader.h @@ -27,6 +27,7 @@ #include #include +#include namespace eCAL { @@ -49,7 +50,7 @@ namespace eCAL std::string get(const std::string& section_, const std::string& key_, const char* default_); private: - CConfigImpl* m_impl; + std::unique_ptr m_impl; }; ECAL_API bool CfgGetBool (const std::string& section_, const std::string& key_, bool default_ = false); diff --git a/ecal/core/src/ecal_config_reader_hlp.h b/src/core/src/config/ecal_config_reader_hlp.h similarity index 100% rename from ecal/core/src/ecal_config_reader_hlp.h rename to src/core/src/config/ecal_config_reader_hlp.h diff --git a/ecal/core/src/ecal.cpp b/src/core/src/ecal.cpp similarity index 84% rename from ecal/core/src/ecal.cpp rename to src/core/src/ecal.cpp index 804b66e..29ab150 100644 --- a/ecal/core/src/ecal.cpp +++ b/src/core/src/ecal.cpp @@ -21,10 +21,29 @@ * @brief eCAL core functions **/ +#include "ecal_def.h" #include "ecal_globals.h" +#include "ecal_event.h" -#include -#include +#if ECAL_CORE_COMMAND_LINE +#include "util/advanced_tclap_output.h" +#endif + +#include + +namespace +{ + const eCAL::EventHandleT& ShutdownProcEvent() + { + static eCAL::EventHandleT evt; + static const std::string event_name(EVENT_SHUTDOWN_PROC + std::string("_") + std::to_string(eCAL::Process::GetProcessID())); + if (!gEventIsValid(evt)) + { + gOpenNamedEvent(&evt, event_name, true); + } + return(evt); + } +} namespace eCAL { @@ -59,10 +78,10 @@ namespace eCAL **/ int GetVersion(int* major_, int* minor_, int* patch_) { - if((major_ == nullptr) && (minor_ == nullptr) && (patch_ == nullptr)) return(-1); - if(major_) *major_ = ECAL_VERSION_MAJOR; - if(minor_) *minor_ = ECAL_VERSION_MINOR; - if(patch_) *patch_ = ECAL_VERSION_PATCH; + if((major_ == nullptr) || (minor_ == nullptr) || (patch_ == nullptr)) return(-1); + *major_ = ECAL_VERSION_MAJOR; + *minor_ = ECAL_VERSION_MINOR; + *patch_ = ECAL_VERSION_PATCH; return(0); } @@ -80,6 +99,8 @@ namespace eCAL { bool dump_config(false); std::vector config_keys; + +#if ECAL_CORE_COMMAND_LINE if ((argc_ > 0) && (argv_ != nullptr)) { // define command line object @@ -118,13 +139,14 @@ namespace eCAL config_keys = set_config_key_arg.getValue(); } } +#endif // first call if (g_globals_ctx == nullptr) { g_globals_ctx = new CGlobals; - if(unit_name_) g_unit_name = unit_name_; + if(unit_name_ != nullptr) g_unit_name = unit_name_; if (g_unit_name.empty()) { g_unit_name = Process::GetProcessName(); @@ -149,9 +171,9 @@ namespace eCAL #endif } - if (argv_) + if (argv_ != nullptr) { - for (size_t i = 0; i < static_cast(argc_); ++i) if (argv_[i]) g_task_parameter.push_back(argv_[i]); + for (size_t i = 0; i < static_cast(argc_); ++i) if (argv_[i] != nullptr) g_task_parameter.emplace_back(argv_[i]); } g_process_wclock = 0; @@ -165,7 +187,7 @@ namespace eCAL g_globals_ctx_ref_cnt++; // (post)initialize single components - int success = g_globals()->Initialize(components_, &config_keys); + const int success = g_globals()->Initialize(components_, &config_keys); // print out configuration if (dump_config) @@ -231,9 +253,20 @@ namespace eCAL if (g_globals_ctx == nullptr) return 1; g_globals_ctx_ref_cnt--; if (g_globals_ctx_ref_cnt > 0) return 0; - int ret = g_globals()->Finalize(components_); + int const ret = g_globals()->Finalize(components_); delete g_globals_ctx; g_globals_ctx = nullptr; return(ret); } + + /** + * @brief Return the eCAL process state. + * + * @return True if eCAL is in proper state. + **/ + bool Ok() + { + const bool ecal_is_ok = (g_globals_ctx != nullptr) && !gWaitForEvent(ShutdownProcEvent(), 0); + return(ecal_is_ok); + } } diff --git a/ecal/core/src/ecal_def.h b/src/core/src/ecal_def.h similarity index 71% rename from ecal/core/src/ecal_def.h rename to src/core/src/ecal_def.h index 841e27d..679947e 100644 --- a/ecal/core/src/ecal_def.h +++ b/src/core/src/ecal_def.h @@ -41,7 +41,7 @@ /* monitor settings */ /**********************************************************************************************/ /* timeout for automatic removing monitoring topics in ms */ -#define MON_TIMEOUT 5000 +#define MON_TIMEOUT 5000 /* topics blacklist as regular expression (will not be monitored) */ #define MON_FILTER_EXCL "_.*" /* topics whitelist as regular expression (will be monitored only) */ @@ -63,73 +63,63 @@ /* network settings */ /**********************************************************************************************/ /* network switch */ -#define NET_ENABLED false +#define NET_ENABLED false /* eCAL udp multicast defines */ #define NET_UDP_MULTICAST_CONFIG_VERSION "v1" #define NET_UDP_MULTICAST_GROUP "239.0.0.1" #define NET_UDP_MULTICAST_MASK "0.0.0.15" #define NET_UDP_MULTICAST_PORT 14000 -#define NET_UDP_MULTICAST_TTL 3 -#define NET_UDP_MULTICAST_PORT_REG_OFF 0 -#define NET_UDP_MULTICAST_PORT_LOG_OFF 1 -#define NET_UDP_MULTICAST_PORT_SAMPLE_OFF 2 -#define NET_UDP_MULTICAST_SNDBUF (5*1024*1024) /* 5 MByte */ -#define NET_UDP_MULTICAST_RCVBUF (5*1024*1024) /* 5 MByte */ +#define NET_UDP_MULTICAST_TTL 3 +#define NET_UDP_MULTICAST_PORT_REG_OFF 0 +#define NET_UDP_MULTICAST_PORT_SAMPLE_OFF 2 +#define NET_UDP_MULTICAST_PORT_LOG_OFF 4 +#define NET_UDP_MULTICAST_SNDBUF (5*1024*1024) /* 5 MByte */ +#define NET_UDP_MULTICAST_RCVBUF (5*1024*1024) /* 5 MByte */ #define NET_UDP_MULTICAST_JOIN_ALL_IF_ENABLED false -#define NET_UDP_RECBUFFER_TIMEOUT 1000 /* ms */ -#define NET_UDP_RECBUFFER_CLEANUP 10 /* ms */ +#define NET_UDP_RECBUFFER_TIMEOUT 1000 /* ms */ +#define NET_UDP_RECBUFFER_CLEANUP 10 /* ms */ -/* overall udp multicast bandwidth limitation in bytes/s, -1 == no limitation*/ -#define NET_BANDWIDTH_MAX_UDP -1 +#define NET_TCP_REC_ENABLED true +#define NET_SHM_REC_ENABLED true +#define NET_UDP_MC_REC_ENABLED true -#define NET_INPROC_REC_ENABLED true -#define NET_TCP_REC_ENABLED true -#define NET_SHM_REC_ENABLED true +#define NET_NPCAP_ENABLED false -#define NET_UDP_MC_REC_ENABLED true +#define NET_TCP_PUBSUB_NUM_EXECUTOR_READER 4 +#define NET_TCP_PUBSUB_NUM_EXECUTOR_WRITER 4 +#define NET_TCP_PUBSUB_MAX_RECONNECTIONS 5 -#define NET_NPCAP_ENABLED false - -#define NET_TCP_PUBSUB_NUM_EXECUTOR_READER 4 -#define NET_TCP_PUBSUB_NUM_EXECUTOR_WRITER 4 -#define NET_TCP_PUBSUB_MAX_RECONNECTIONS 5 - -/**********************************************************************************************/ -/* iceoryx settings */ -/**********************************************************************************************/ -#define ICEORYX_SERVICE "eCAL" -#define ICEORYX_INSTANCE "" +/* common host group name that enables interprocess mechanisms across (virtual) host borders (e.g, Docker); by default equivalent to local host name */ +#define NET_HOST_GROUP_NAME "" /**********************************************************************************************/ /* publisher settings */ /**********************************************************************************************/ -/* use inproc transport layer [auto = 2, on = 1, off = 0] */ -#define PUB_USE_INPROC 0 /* use shared memory transport layer [auto = 2, on = 1, off = 0] */ -#define PUB_USE_SHM 2 +#define PUB_USE_SHM 2 /* use tcp transport layer [auto = 2, on = 1, off = 0] */ -#define PUB_USE_TCP 0 +#define PUB_USE_TCP 0 /* use udp multicast transport layer [auto = 2, on = 1, off = 0] */ -#define PUB_USE_UDP_MC 2 +#define PUB_USE_UDP_MC 2 /* share topic type [ on = 1, off = 0] */ -#define PUB_SHARE_TTYPE 1 +#define PUB_SHARE_TTYPE 1 /* share topic description [ on = 1, off = 0] */ -#define PUB_SHARE_TDESC 1 +#define PUB_SHARE_TDESC 1 /* minimum size for created shared memory files */ -#define PUB_MEMFILE_MINSIZE (4*1024) +#define PUB_MEMFILE_MINSIZE (4*1024) /* reserve buffer size before reallocation in % */ -#define PUB_MEMFILE_RESERVE 50 +#define PUB_MEMFILE_RESERVE 50 /* timeout for create / open a memory file using mutex lock in ms */ -#define PUB_MEMFILE_CREATE_TO 200 -#define PUB_MEMFILE_OPEN_TO 50 +#define PUB_MEMFILE_CREATE_TO 200 +#define PUB_MEMFILE_OPEN_TO 200 /* timeout for memory read acknowledge signal from data reader in ms */ -#define PUB_MEMFILE_ACK_TO 0 /* ms */ +#define PUB_MEMFILE_ACK_TO 0 /* ms */ /* defines number of memory files handle by the publisher for a 1:n connection a higher number will increase data throughput, but will also increase the size of used memory, number of semaphores @@ -137,42 +127,54 @@ higher values than 3 are not recommended values > 1 will break local IPC compatibility to eCAL 5.9 and older */ -#define PUB_MEMFILE_BUF_COUNT 1 +#define PUB_MEMFILE_BUF_COUNT 1 /* allow subscriber to access memory file without copying content in advance (zero copy) this memory file is blocked for other readers wihle processed by the user callback function this option is fully IPC compatible to all eCAL 5.x versions */ -#define PUB_MEMFILE_ZERO_COPY 0 +#define PUB_MEMFILE_ZERO_COPY 0 + +/**********************************************************************************************/ +/* service settings */ +/**********************************************************************************************/ +/* support service protocol v0, eCAL 5.11 and older (0 = off, 1 = on) */ +#define SERVICE_PROTOCOL_V0 1 + +/* support service protocol v1, eCAL 5.12 and newer (0 = off, 1 = on) */ +#define SERVICE_PROTOCOL_V1 1 /**********************************************************************************************/ /* time settings */ /**********************************************************************************************/ -#define TIME_SYNC_MOD_RT "" -#define TIME_SYNC_MOD_REPLAY "" +#define TIME_SYNC_MOD_RT "" +#define TIME_SYNC_MOD_REPLAY "" /**********************************************************************************************/ /* process settings */ /**********************************************************************************************/ -#define PROCESS_TERMINAL_EMULATOR "" +#define PROCESS_TERMINAL_EMULATOR "" /**********************************************************************************************/ /* ecal internal timings */ /**********************************************************************************************/ /* timeout for automatic removing registered topics and memory files in global database in ms */ -#define CMN_REGISTRATION_TO (60*1000) +#define CMN_REGISTRATION_TO (60*1000) /* time for resend registration info from publisher/subscriber in ms */ -#define CMN_REGISTRATION_REFRESH 1000 +#define CMN_REGISTRATION_REFRESH 1000 /* delta time to check timeout for data readers in ms */ -#define CMN_DATAREADER_TIMEOUT_DTIME 10 +#define CMN_DATAREADER_TIMEOUT_RESOLUTION_MS 100 + +/* cylce time udp receive threads in ms */ +#define CMN_UDP_RECEIVE_THREAD_CYCLE_TIME_MS 1000 /**********************************************************************************************/ /* events */ /**********************************************************************************************/ /* common stop event prefix to shut down a local user process */ -#define EVENT_SHUTDOWN_PROC "ecal_shutdown_process" +#define EVENT_SHUTDOWN_PROC "ecal_shutdown_process" /**********************************************************************************************/ /* experimental */ @@ -182,11 +184,11 @@ /* disable distribution of monitoring/registration information via network (default) */ #define EXP_NETWORK_MONITORING_DISABLED false /* queue size of monitoring/registration events */ -#define EXP_SHM_MONITORING_QUEUE_SIZE 1024 +#define EXP_SHM_MONITORING_QUEUE_SIZE 1024 /* domain name for shared memory based monitoring/registration */ -#define EXP_SHM_MONITORING_DOMAIN "ecal_monitoring" +#define EXP_SHM_MONITORING_DOMAIN "ecal_monitoring" /* memory file access timeout */ -#define EXP_MEMFILE_ACCESS_TIMEOUT 100 +#define EXP_MEMFILE_ACCESS_TIMEOUT 100 /* enable dropping of payload messages that arrive out of order */ -#define EXP_DROP_OUT_OF_ORDER_MESSAGES false +#define EXP_DROP_OUT_OF_ORDER_MESSAGES false diff --git a/src/core/src/ecal_def_ini.h b/src/core/src/ecal_def_ini.h new file mode 100644 index 0000000..ee4718f --- /dev/null +++ b/src/core/src/ecal_def_ini.h @@ -0,0 +1,130 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL ini file keys +**/ + +#pragma once + +///////////////////////////////////// +// common +///////////////////////////////////// +#define CMN_SECTION_S "common" +#define CMN_REGISTRATION_TO_S "registration_timeout" +#define CMN_REGISTRATION_REFRESH_S "registration_refresh" + +///////////////////////////////////// +// network +///////////////////////////////////// +#define NET_SECTION_S "network" + +#define NET_ENABLED_S "network_enabled" + +#define NET_UDP_MULTICAST_CONFIG_VERSION_S "multicast_config_version" +#define NET_UDP_MULTICAST_GROUP_S "multicast_group" +#define NET_UDP_MULTICAST_MASK_S "multicast_mask" +#define NET_UDP_MULTICAST_PORT_S "multicast_port" +#define NET_UDP_MULTICAST_TTL_S "multicast_ttl" + +#define NET_UDP_MULTICAST_SNDBUF_S "multicast_sndbuf" +#define NET_UDP_MULTICAST_RCVBUF_S "multicast_rcvbuf" + +#define NET_UDP_MULTICAST_JOIN_ALL_IF_ENABLED_S "multicast_join_all_if" + +#define NET_UDP_MC_REC_ENABLED_S "udp_mc_rec_enabled" +#define NET_SHM_REC_ENABLED_S "shm_rec_enabled" +#define NET_TCP_REC_ENABLED_S "tcp_rec_enabled" + +#define NET_NPCAP_ENABLED_S "npcap_enabled" + +#define NET_TCP_PUBSUB_NUM_EXECUTOR_READER_S "tcp_pubsub_num_executor_reader" +#define NET_TCP_PUBSUB_NUM_EXECUTOR_WRITER_S "tcp_pubsub_num_executor_writer" +#define NET_TCP_PUBSUB_MAX_RECONNECTIONS_S "tcp_pubsub_max_reconnections" + +#define NET_HOST_GROUP_NAME_S "host_group_name" + +///////////////////////////////////// +// time +///////////////////////////////////// +#define TIME_SECTION_S "time" +#define TIME_SYNC_MOD_RT_S "timesync_module_rt" +#define TIME_SYNC_MOD_REPLAY_S "timesync_module_replay" + +///////////////////////////////////// +// process +///////////////////////////////////// +#define PROCESS_SECTION_S "process" +#define PROCESS_TERMINAL_EMULATOR_S "terminal_emulator" + +///////////////////////////////////// +// monitoring +///////////////////////////////////// +#define MON_SECTION_S "monitoring" + +#define MON_TIMEOUT_S "timeout" +#define MON_FILTER_EXCL_S "filter_excl" +#define MON_FILTER_INCL_S "filter_incl" + +#define MON_LOG_FILTER_CON_S "filter_log_con" +#define MON_LOG_FILTER_FILE_S "filter_log_file" +#define MON_LOG_FILTER_UDP_S "filter_log_udp" + +///////////////////////////////////// +// sys +///////////////////////////////////// +#define SYS_SECTION_S "sys" +#define SYS_FILTER_EXCL_S "filter_excl" + +///////////////////////////////////// +// publisher +///////////////////////////////////// +#define PUB_SECTION_S "publisher" + +#define PUB_USE_UDP_MC_S "use_udp_mc" +#define PUB_USE_SHM_S "use_shm" +#define PUB_USE_TCP_S "use_tcp" + +#define PUB_MEMFILE_MINSIZE_S "memfile_minsize" +#define PUB_MEMFILE_RESERVE_S "memfile_reserve" +#define PUB_MEMFILE_ACK_TO_S "memfile_ack_timeout" +#define PUB_MEMFILE_ZERO_COPY_S "memfile_zero_copy" +#define PUB_MEMFILE_BUF_COUNT_S "memfile_buffer_count" + +#define PUB_SHARE_TTYPE_S "share_ttype" +#define PUB_SHARE_TDESC_S "share_tdesc" + +///////////////////////////////////// +// service +///////////////////////////////////// +#define SERVICE_SECTION_S "service" + +#define SERVICE_PROTOCOL_V0_S "protocol_v0" +#define SERVICE_PROTOCOL_V1_S "protocol_v1" + +///////////////////////////////////// +// experimental +///////////////////////////////////// +#define EXP_SECTION_S "experimental" + +#define EXP_SHM_MONITORING_ENABLED_S "shm_monitoring_enabled" +#define EXP_NETWORK_MONITORING_DISABLED_S "network_monitoring_disabled" +#define EXP_SHM_MONITORING_QUEUE_SIZE_S "shm_monitoring_queue_size" +#define EXP_SHM_MONITORING_DOMAIN_S "shm_monitoring_domain" +#define EXP_DROP_OUT_OF_ORDER_MESSAGES_S "drop_out_of_order_messages" diff --git a/src/core/src/ecal_defs.h.in b/src/core/src/ecal_defs.h.in new file mode 100644 index 0000000..c792a26 --- /dev/null +++ b/src/core/src/ecal_defs.h.in @@ -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 ================================= +*/ + +/** + * @file ecal_defs.h + * @brief eCAL core defines (version numbers) +**/ + +#ifndef ecal_defs_h_included +#define ecal_defs_h_included +#define ECAL_VERSION_MAJOR (@GIT_REVISION_MAJOR@) +#define ECAL_VERSION_MINOR (@GIT_REVISION_MINOR@) +#define ECAL_VERSION_PATCH (@GIT_REVISION_PATCH@) +#define ECAL_VERSION "@GIT_DESCRIBE_TAG@" +#define ECAL_DATE "@GIT_REVISION_DATE@" +#define ECAL_PLATFORMTOOLSET "@eCAL_PLATFORM_TOOLSET@" + +#define ECAL_VERSION_INTEGER ECAL_VERSION_CALCULATE(ECAL_VERSION_MAJOR, ECAL_VERSION_MINOR, ECAL_VERSION_PATCH) +#define ECAL_VERSION_CALCULATE(major, minor, patch) (((major)<<16)|((minor)<<8)|(patch)) + +#define ECAL_INSTALL_APP_DIR "@eCAL_install_app_dir@" +#define ECAL_INSTALL_SAMPLES_DIR "@eCAL_install_samples_dir@" +#define ECAL_INSTALL_LIB_DIR "@eCAL_install_lib_dir@" +#define ECAL_INSTALL_CONFIG_DIR "@eCAL_install_config_dir@" +#define ECAL_INSTALL_INCLUDE_DIR "@eCAL_install_include_dir@" +#define ECAL_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@" + +#endif // ecal_defs_h_included diff --git a/src/core/src/ecal_descgate.cpp b/src/core/src/ecal_descgate.cpp new file mode 100644 index 0000000..c805c54 --- /dev/null +++ b/src/core/src/ecal_descgate.cpp @@ -0,0 +1,323 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL description gateway class +**/ + +#include +#include + +#include "ecal_descgate.h" +#include +#include +#include + +namespace eCAL +{ + CDescGate::CDescGate() : + m_topic_info_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), + m_service_info_map(std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())) + { + } + CDescGate::~CDescGate() = default; + + void CDescGate::Create() + { + } + + void CDescGate::Destroy() + { + } + + bool CDescGate::ApplyTopicDescription(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const QualityFlags description_quality_) + { + const std::unique_lock lock(m_topic_info_map.sync); + m_topic_info_map.map->remove_deprecated(); + + const auto topic_info_it = m_topic_info_map.map->find(topic_name_); + + // new element (no need to check anything, just add it) + if(topic_info_it == m_topic_info_map.map->end()) + { + // create a new topic entry + STopicInfoQuality& topic_info = (*m_topic_info_map.map)[topic_name_]; + topic_info.info = topic_info_; + topic_info.quality = description_quality_; + return true; + } + + // we do not use the [] operator here to not update the timestamp + // by accessing the map entry + // + // a topic with the same name but different type name or different description + // should NOT update the timestamp of an existing entry + // + // otherwise there could be a scenario where a "lower quality topic" would keep a + // "higher quality topic" alive (even it is no more existing) + STopicInfoQuality topic_info = (*topic_info_it).second; + + // first let's check whether the current information has a higher quality + // if it has a higher quality, we overwrite it + if (description_quality_ > topic_info.quality) + { + // overwrite attributes + topic_info.info = topic_info_; + topic_info.quality = description_quality_; + + // update attributes and return + (*m_topic_info_map.map)[topic_name_] = topic_info; + return true; + } + + // this is the same topic (topic name, topic type name, topic type description) + if (topic_info.info == topic_info_) + { + // update timestamp (by just accessing the entry) and return + (*m_topic_info_map.map)[topic_name_] = topic_info; + return false; + } + + // topic type name or topic description differ but we logged this before + if (topic_info.type_missmatch_logged) + { + return false; + } + + // topic type name or topic description differ and this is not logged yet + // so we log the error and update the entry one time + bool update_topic_info(false); + + // topic type name differs + // we log the error and update the entry one time + if (!topic_info_.encoding.empty() + && !topic_info.info.encoding.empty() + && (topic_info.info.encoding != topic_info_.encoding) + ) + { + std::string tencoding1 = topic_info.info.encoding; + std::string tencoding2 = topic_info_.encoding; + std::replace(tencoding1.begin(), tencoding1.end(), '\0', '?'); + std::replace(tencoding1.begin(), tencoding1.end(), '\t', '?'); + std::replace(tencoding2.begin(), tencoding2.end(), '\0', '?'); + std::replace(tencoding2.begin(), tencoding2.end(), '\t', '?'); + std::string msg = "eCAL Pub/Sub encoding mismatch for topic "; + msg += topic_name_; + msg += " (\'"; + msg += tencoding1; + msg += "\' <> \'"; + msg += tencoding2; + msg += "\')"; + eCAL::Logging::Log(log_level_warning, msg); + + // mark as logged + topic_info.type_missmatch_logged = true; + // and update its attributes + update_topic_info = true; + } + + // topic type name differs + // we log the error and update the entry one time + if (!topic_info_.name.empty() + && !topic_info.info.name.empty() + && (topic_info.info.name != topic_info_.name) + ) + { + std::string ttype1 = topic_info.info.name; + std::string ttype2 = topic_info_.name; + std::replace(ttype1.begin(), ttype1.end(), '\0', '?'); + std::replace(ttype1.begin(), ttype1.end(), '\t', '?'); + std::replace(ttype2.begin(), ttype2.end(), '\0', '?'); + std::replace(ttype2.begin(), ttype2.end(), '\t', '?'); + std::string msg = "eCAL Pub/Sub type mismatch for topic "; + msg += topic_name_; + msg += " (\'"; + msg += ttype1; + msg += "\' <> \'"; + msg += ttype2; + msg += "\')"; + eCAL::Logging::Log(log_level_warning, msg); + + // mark as logged + topic_info.type_missmatch_logged = true; + // and update its attributes + update_topic_info = true; + } + + // topic type description differs + // we log the error and update the entry one time + if ( !topic_info_.descriptor.empty() + && !topic_info.info.descriptor.empty() + && (topic_info.info.descriptor != topic_info_.descriptor) + ) + { + std::string msg = "eCAL Pub/Sub description mismatch for topic "; + msg += topic_name_; + eCAL::Logging::Log(log_level_warning, msg); + + // mark as logged + topic_info.type_missmatch_logged = true; + // and update its attributes + update_topic_info = true; + } + + // update topic info attributes + if (update_topic_info) + { + (*m_topic_info_map.map)[topic_name_] = topic_info; + } + + return false; + } + + void CDescGate::GetTopics(std::unordered_map& topic_info_map_) + { + std::unordered_map map; + + const std::shared_lock lock(m_topic_info_map.sync); + m_topic_info_map.map->remove_deprecated(); + map.reserve(m_topic_info_map.map->size()); + + for (const auto& topic_info : (*m_topic_info_map.map)) + { + map.emplace(topic_info.first, topic_info.second.info); + } + topic_info_map_.swap(map); + } + + void CDescGate::GetTopicNames(std::vector& topic_names_) + { + topic_names_.clear(); + + const std::shared_lock lock(m_topic_info_map.sync); + m_topic_info_map.map->remove_deprecated(); + topic_names_.reserve(m_topic_info_map.map->size()); + + for (const auto& topic_info : (*m_topic_info_map.map)) + { + topic_names_.emplace_back(topic_info.first); + } + } + + bool CDescGate::GetDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& topic_info_) + { + if (topic_name_.empty()) return(false); + + const std::shared_lock lock(m_topic_info_map.sync); + const auto topic_info_it = m_topic_info_map.map->find(topic_name_); + + if (topic_info_it == m_topic_info_map.map->end()) return(false); + topic_info_ = (*topic_info_it).second.info; + return(true); + } + + bool CDescGate::ApplyServiceDescription(const std::string& service_name_ + , const std::string& method_name_ + , const SDataTypeInformation& request_type_information_ + , const SDataTypeInformation& response_type_information_ + , const QualityFlags description_quality_) + { + std::tuple service_method_tuple = std::make_tuple(service_name_, method_name_); + + const std::unique_lock lock(m_service_info_map.sync); + m_service_info_map.map->remove_deprecated(); + + auto service_info_map_it = m_service_info_map.map->find(service_method_tuple); + if (service_info_map_it == m_service_info_map.map->end()) + { + // create a new service entry + SServiceMethodInfoQuality& service_info = (*m_service_info_map.map)[service_method_tuple]; + service_info.info.request_type = request_type_information_; + service_info.info.response_type = response_type_information_; + service_info.quality = description_quality_; + return true; + } + + // let's check whether the current information has a higher quality + // if it has a higher quality, we overwrite it + bool ret_value(false); + SServiceMethodInfoQuality service_info = (*service_info_map_it).second; + if (description_quality_ > service_info.quality) + { + service_info.info.request_type = request_type_information_; + service_info.info.response_type = response_type_information_; + service_info.quality = description_quality_; + ret_value = true; + } + + // update service entry (and its timestamp) + (*m_service_info_map.map)[service_method_tuple] = service_info; + + return ret_value; + } + + void CDescGate::GetServices(std::map, SServiceMethodInformation>& service_info_map_) + { + std::map, SServiceMethodInformation> map; + + const std::shared_lock lock(m_service_info_map.sync); + m_service_info_map.map->remove_deprecated(); + + for (const auto& service_info : (*m_service_info_map.map)) + { + map.emplace(service_info.first, service_info.second.info); + } + service_info_map_.swap(map); + } + + void CDescGate::GetServiceNames(std::vector>& service_method_names_) + { + service_method_names_.clear(); + + const std::shared_lock lock(m_service_info_map.sync); + m_service_info_map.map->remove_deprecated(); + service_method_names_.reserve(m_service_info_map.map->size()); + + for (const auto& service_info : (*m_service_info_map.map)) + { + service_method_names_.emplace_back(service_info.first); + } + } + + bool CDescGate::GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_name_, std::string& resp_type_name_) + { + std::tuple service_method_tuple = std::make_tuple(service_name_, method_name_); + + const std::shared_lock lock(m_service_info_map.sync); + auto service_info_map_it = m_service_info_map.map->find(service_method_tuple); + + if (service_info_map_it == m_service_info_map.map->end()) return false; + req_type_name_ = (*service_info_map_it).second.info.request_type.name; + resp_type_name_ = (*service_info_map_it).second.info.response_type.name; + return true; + } + + bool CDescGate::GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_type_desc_, std::string& resp_type_desc_) + { + std::tuple service_method_tuple = std::make_tuple(service_name_, method_name_); + + const std::shared_lock lock(m_service_info_map.sync); + auto service_info_map_it = m_service_info_map.map->find(service_method_tuple); + + if (service_info_map_it == m_service_info_map.map->end()) return false; + req_type_desc_ = (*service_info_map_it).second.info.request_type.descriptor; + resp_type_desc_ = (*service_info_map_it).second.info.response_type.descriptor; + return true; + } +} diff --git a/ecal/core/src/ecal_descgate.h b/src/core/src/ecal_descgate.h similarity index 67% rename from ecal/core/src/ecal_descgate.h rename to src/core/src/ecal_descgate.h index bc8f135..b2e2ee5 100644 --- a/ecal/core/src/ecal_descgate.h +++ b/src/core/src/ecal_descgate.h @@ -24,10 +24,11 @@ #pragma once #include +#include #include "ecal_global_accessors.h" #include "ecal_def.h" -#include "ecal_expmap.h" +#include "util/ecal_expmap.h" #include #include @@ -46,13 +47,15 @@ namespace eCAL // Enumeration of quality bits used for detecting how good a topic information is. enum class QualityFlags : int { - NO_QUALITY = 0, //!< Special value for initialization - - DESCRIPTION_AVAILABLE = 0x1 << 4, //!< Having a descriptor at all is the most important thing - INFO_COMES_FROM_CORRECT_TOPIC = 0x1 << 3, //!< The information comes from the current topic (and has not been borrowed from another topic) - INFO_COMES_FROM_PUBLISHER = 0x1 << 2, //!< A descriptor coming from the publisher is better than one from a subscriber, as we assume that the publisher knows best what he is publishing - INFO_COMES_FROM_THIS_PROCESS = 0x1 << 1, //!< We prefer descriptors from the current process - TYPE_AVAILABLE = 0x1 << 0, //!< Having information about the type's name available is nice but not that important to us. + NO_QUALITY = 0, //!< Special value for initialization + + DESCRIPTION_AVAILABLE = 0x1 << 4, //!< Having a descriptor at all is the most important thing + INFO_COMES_FROM_CORRECT_ENTITY = 0x1 << 3, //!< The information comes from the current topic/service + //!< and has not been borrowed from another emtity, like read by a subscriber from a publisher + INFO_COMES_FROM_PRODUCER = 0x1 << 2, //!< A descriptor coming from the producer (like a publisher) is better than one from a + //!< consumer (like a subscriber), as we assume that the publisher knows best what he is publishing + INFO_COMES_FROM_THIS_PROCESS = 0x1 << 1, //!< We prefer descriptors from the current process + TYPE_AVAILABLE = 0x1 << 0, //!< Having information about the type's name available is nice but not that important to us }; public: @@ -62,25 +65,21 @@ namespace eCAL void Create(); void Destroy(); - void ApplyTopicDescription(const std::string& topic_name_, - const std::string& topic_type_, - const std::string& topic_desc_, + bool ApplyTopicDescription(const std::string& topic_name_, + const SDataTypeInformation& topic_info_, const QualityFlags description_quality_); - void GetTopics(std::unordered_map& topic_info_map_); + void GetTopics(std::unordered_map& topic_info_map_); void GetTopicNames(std::vector& topic_names_); - bool GetTopicTypeName(const std::string& topic_name_, std::string& topic_type_); - bool GetTopicDescription(const std::string& topic_name_, std::string& topic_desc_); + bool GetDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& topic_info_); - void ApplyServiceDescription(const std::string& service_name_, + bool ApplyServiceDescription(const std::string& service_name_, const std::string& method_name_, - const std::string& req_type_name_, - const std::string& req_type_desc_, - const std::string& resp_type_name_, - const std::string& resp_type_desc_, - const QualityFlags info_quality_); + const SDataTypeInformation& request_type_information_, + const SDataTypeInformation& response_type_information_, + const QualityFlags description_quality_); - void GetServices(std::map, Util::SServiceMethodInfo>& service_info_map_); + void GetServices(std::map, SServiceMethodInformation>& service_info_map_); void GetServiceNames(std::vector>& service_method_names_); bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_name_, std::string& resp_type_name_); bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_type_desc_, std::string& resp_type_desc_); @@ -88,15 +87,15 @@ namespace eCAL protected: struct STopicInfoQuality { - Util::STopicInfo info; //!< Topic info struct with type name and descriptor. - QualityFlags description_quality = QualityFlags::NO_QUALITY; //!< QualityFlags to determine whether we may overwrite the current data with better one. E.g. we prefer the description sent by a publisher over one sent by a subscriber. - bool type_missmatch_logged = false; //!< Whether we have already logged a type-missmatch + SDataTypeInformation info; //!< Topic info struct with type encoding, name and descriptor. + QualityFlags quality = QualityFlags::NO_QUALITY; //!< QualityFlags to determine whether we may overwrite the current data with better one. E.g. we prefer the description sent by a publisher over one sent by a subscriber. + bool type_missmatch_logged = false; //!< Whether we have already logged a type-missmatch }; struct SServiceMethodInfoQuality { - Util::SServiceMethodInfo info; //!< Service info struct with type names and descriptors for request and response. - QualityFlags info_quality = QualityFlags::NO_QUALITY; //!< The Quality of the Info + SServiceMethodInformation info; //!< Service info struct with type names and descriptors for request and response. + QualityFlags quality = QualityFlags::NO_QUALITY; //!< The Quality of the Info }; // key: topic name | value: topic (type/desc), quality @@ -104,7 +103,7 @@ namespace eCAL struct STopicInfoMap { explicit STopicInfoMap(const std::chrono::milliseconds& timeout_) : - map(new TopicInfoMap(timeout_)) + map(std::make_unique(timeout_)) { }; mutable std::shared_timed_mutex sync; //!< Mutex protecting the map @@ -117,7 +116,7 @@ namespace eCAL struct SServiceMethodInfoMap { explicit SServiceMethodInfoMap(const std::chrono::milliseconds& timeout_) : - map(new ServiceMethodInfoMap(timeout_)) + map(std::make_unique(timeout_)) { }; mutable std::shared_timed_mutex sync; //!< Mutex protecting the map @@ -133,4 +132,4 @@ namespace eCAL inline CDescGate::QualityFlags& operator|= (CDescGate::QualityFlags& a, CDescGate::QualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) |= static_cast::type>(b) ); } inline CDescGate::QualityFlags& operator&= (CDescGate::QualityFlags& a, CDescGate::QualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) &= static_cast::type>(b) ); } inline CDescGate::QualityFlags& operator^= (CDescGate::QualityFlags& a, CDescGate::QualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) ^= static_cast::type>(b) ); } -}; +} diff --git a/ecal/core/src/ecal_event.cpp b/src/core/src/ecal_event.cpp similarity index 87% rename from ecal/core/src/ecal_event.cpp rename to src/core/src/ecal_event.cpp index e93ecb9..67391af 100644 --- a/ecal/core/src/ecal_event.cpp +++ b/src/core/src/ecal_event.cpp @@ -18,29 +18,25 @@ */ /** - * @brief eCAL handle helper class - windows platform + * @brief eCAL handle helper class **/ -#include #include -#include +#include "ecal_event.h" #include -#include -#include -#include #ifdef ECAL_OS_WINDOWS #include "ecal_win_main.h" -namespace eCAL +namespace { - bool gOpenEvent(EventHandleT* event_, const std::string& event_name_) + bool OpenEvent(eCAL::EventHandleT* event_, const std::string& event_name_) { if(event_ == nullptr) return(false); - EventHandleT event; + eCAL::EventHandleT event; event.name = event_name_; event.handle = ::CreateEvent(nullptr, false, false, event_name_.c_str()); if(event.handle != nullptr) @@ -50,22 +46,41 @@ namespace eCAL } return(false); } +} + +namespace eCAL +{ + bool gOpenNamedEvent(eCAL::EventHandleT* event_, const std::string& event_name_, bool /*ownership_*/) + { + return OpenEvent(event_, event_name_); + } + + bool gOpenUnnamedEvent(eCAL::EventHandleT* event_) + { + return OpenEvent(event_, ""); + } + + // deprecated + bool gOpenEvent(EventHandleT* event_, const std::string& event_name_) + { + return OpenEvent(event_, event_name_); + } bool gCloseEvent(const EventHandleT& event_) { - if(!event_.handle) return(false); + if(event_.handle == nullptr) return(false); return(::CloseHandle(event_.handle) != 0); } bool gSetEvent(const EventHandleT& event_) { - if(!event_.handle) return(false); + if(event_.handle == nullptr) return(false); return(::SetEvent(event_.handle) != 0); } bool gWaitForEvent(const EventHandleT& event_, const long timeout_) { - if(!event_.handle) return(false); + if(event_.handle == nullptr) return(false); if(timeout_ < 0) { return(::WaitForSingleObject(event_.handle, INFINITE) == WAIT_OBJECT_0); @@ -302,9 +317,10 @@ namespace eCAL class CNamedEvent { public: - explicit CNamedEvent(const std::string& name_) : + explicit CNamedEvent(const std::string& name_, bool ownership_) : m_name(name_ + "_evt"), - m_event(nullptr) + m_event(nullptr), + m_owner(ownership_) { m_name = (m_name[0] != '/') ? "/" + m_name : m_name; // make memory file path compatible for all posix systems m_event = named_event_open(m_name.c_str()); @@ -318,7 +334,10 @@ namespace eCAL { if(m_event == nullptr) return; named_event_close(m_event); - named_event_destroy(m_name.c_str()); + if(m_owner) + { + named_event_destroy(m_name.c_str()); + } } void set() @@ -371,8 +390,42 @@ namespace eCAL std::string m_name; named_event_t* m_event; + bool m_owner; }; + bool gOpenNamedEvent(EventHandleT* event_, const std::string& event_name_, bool ownership_) + { + if(event_ == nullptr) return(false); + + EventHandleT event; + event.name = event_name_; + event.handle = new CNamedEvent(event.name, ownership_); + + if(event.handle != nullptr) + { + *event_ = event; + return true; + } + return false; + } + + bool gOpenUnnamedEvent(EventHandleT* event_) + { + if(event_ == nullptr) return(false); + + EventHandleT event; + event.name = ""; + event.handle = new CEvent(); + + if(event.handle != nullptr) + { + *event_ = event; + return true; + } + return false; + } + + // deprecated bool gOpenEvent(EventHandleT* event_, const std::string& event_name_) { if(event_ == nullptr) return(false); @@ -386,7 +439,7 @@ namespace eCAL } else { - event.handle = new CNamedEvent(event.name); + event.handle = new CNamedEvent(event.name, true); } if(event.handle != nullptr) diff --git a/ecal/core/include/ecal/ecal_event.h b/src/core/src/ecal_event.h similarity index 64% rename from ecal/core/include/ecal/ecal_event.h rename to src/core/src/ecal_event.h index 5ce11a9..1b0e856 100644 --- a/ecal/core/include/ecal/ecal_event.h +++ b/src/core/src/ecal_event.h @@ -25,9 +25,10 @@ #pragma once #include -#include #include +#include "ecal_eventhandle.h" + namespace eCAL { /** @@ -38,7 +39,27 @@ namespace eCAL * * @return True if succeeded. **/ - ECAL_API bool gOpenEvent(eCAL::EventHandleT* event_, const std::string& event_name_ = ""); + bool gOpenEvent(eCAL::EventHandleT* event_, const std::string& event_name_ = ""); + + /** + * @brief Open a named event with ownership. + * + * @param [out] event_ Returned event struct. + * @param event_name_ Event name. + * @param ownership_ Event is owned by the caller and will be destroyed on CloseEvent + * + * @return True if succeeded. + **/ + bool gOpenNamedEvent(eCAL::EventHandleT* event_, const std::string& event_name_, bool ownership_); + + /** + * @brief Open an unnamed event. + * + * @param [out] event_ Returned event struct. + * + * @return True if succeeded. + **/ + bool gOpenUnnamedEvent(eCAL::EventHandleT* event_); /** * @brief Close an event. @@ -47,7 +68,7 @@ namespace eCAL * * @return True if succeeded. **/ - ECAL_API bool gCloseEvent(const EventHandleT& event_); + bool gCloseEvent(const EventHandleT& event_); /** * @brief Set an event active. @@ -56,7 +77,7 @@ namespace eCAL * * @return True if succeeded. **/ - ECAL_API bool gSetEvent(const EventHandleT& event_); + bool gSetEvent(const EventHandleT& event_); /** * @brief Wait for an event with timeout. @@ -66,19 +87,19 @@ namespace eCAL * * @return True if succeeded. **/ - ECAL_API bool gWaitForEvent(const EventHandleT& event_, long timeout_); + bool gWaitForEvent(const EventHandleT& event_, long timeout_); /** * @brief Invalidate an event. * * @return True if event is invalidated. **/ - ECAL_API bool gInvalidateEvent(EventHandleT* event_); + bool gInvalidateEvent(EventHandleT* event_); /** * @brief Check whether an event is valid or not. * * @return True if event is valid. **/ - ECAL_API bool gEventIsValid(const EventHandleT& event_); + bool gEventIsValid(const EventHandleT& event_); } diff --git a/ecal/core/include/ecal/ecal_eventhandle.h b/src/core/src/ecal_eventhandle.h similarity index 93% rename from ecal/core/include/ecal/ecal_eventhandle.h rename to src/core/src/ecal_eventhandle.h index eabb76b..ad55d79 100644 --- a/ecal/core/include/ecal/ecal_eventhandle.h +++ b/src/core/src/ecal_eventhandle.h @@ -44,7 +44,7 @@ namespace eCAL void* handle; //!< event handle /* @cond */ - bool operator==(const SEventHandle& rhs) + bool operator==(const SEventHandle& rhs) const { return(rhs.name == name && rhs.handle == handle); } @@ -53,6 +53,5 @@ namespace eCAL /* @cond */ typedef SEventHandle EventHandleT; - typedef std::vector EventHandleVecT; /* @endcond */ } diff --git a/ecal/core/src/ecal_global_accessors.cpp b/src/core/src/ecal_global_accessors.cpp similarity index 72% rename from ecal/core/src/ecal_global_accessors.cpp rename to src/core/src/ecal_global_accessors.cpp index 0f1095b..4aea1a3 100644 --- a/ecal/core/src/ecal_global_accessors.cpp +++ b/src/core/src/ecal_global_accessors.cpp @@ -21,6 +21,7 @@ * @brief eCAL core functions **/ +#include "ecal_def.h" #include "ecal_global_accessors.h" #include "ecal_globals.h" @@ -28,12 +29,10 @@ namespace eCAL { CGlobals* g_globals_ctx(nullptr); std::atomic g_globals_ctx_ref_cnt; - std::atomic g_shutdown; std::string g_default_ini_file(ECAL_DEFAULT_CFG); std::string g_host_name; - int g_host_id(0); std::string g_unit_name; std::vector g_task_parameter; @@ -43,8 +42,8 @@ namespace eCAL std::string g_process_id_s; std::string g_process_info; - eCAL_Process_eSeverity g_process_severity(proc_sev_unknown); - eCAL_Process_eSeverity_Level g_process_severity_level(proc_sev_level1); + eCAL_Process_eSeverity g_process_severity(eCAL_Process_eSeverity::proc_sev_unknown); + eCAL_Process_eSeverity_Level g_process_severity_level(eCAL_Process_eSeverity_Level::proc_sev_level1); std::atomic g_process_wclock; std::atomic g_process_wbytes; @@ -54,7 +53,6 @@ namespace eCAL std::atomic g_process_rbytes; std::atomic g_process_rbytes_sum; - CGlobals* g_globals() { return g_globals_ctx; @@ -62,81 +60,93 @@ namespace eCAL CConfig* g_config() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->config().get()); } CLog* g_log() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->log().get()); } +#if ECAL_CORE_MONITORING CMonitoring* g_monitoring() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->monitoring().get()); } +#endif +#if ECAL_CORE_TIMEPLUGIN CTimeGate* g_timegate() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->timegate().get()); } +#endif +#if ECAL_CORE_REGISTRATION CRegistrationProvider* g_registration_provider() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->registration_provider().get()); } + CRegistrationReceiver* g_registration_receiver() + { + if (g_globals() == nullptr) return(nullptr); + return(g_globals()->registration_receiver().get()); + } +#endif + CDescGate* g_descgate() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->descgate().get()); } +#if ECAL_CORE_SUBSCRIBER CSubGate* g_subgate() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->subgate().get()); } +#endif +#if ECAL_CORE_PUBLISHER CPubGate* g_pubgate() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->pubgate().get()); } +#endif +#if ECAL_CORE_SERVICE CServiceGate* g_servicegate() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->servicegate().get()); } CClientGate* g_clientgate() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->clientgate().get()); } +#endif - CRegistrationReceiver* g_registration_receiver() - { - if (!g_globals()) return(nullptr); - return(g_globals()->registration_receiver().get()); - } - -#ifndef ECAL_LAYER_ICEORYX +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) CMemFileThreadPool* g_memfile_pool() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->memfile_pool().get()); } CMemFileMap* g_memfile_map() { - if (!g_globals()) return(nullptr); + if (g_globals() == nullptr) return(nullptr); return(g_globals()->memfile_map().get()); } -#endif /* !ECAL_LAYER_ICEORYX */ +#endif } diff --git a/ecal/core/src/ecal_global_accessors.h b/src/core/src/ecal_global_accessors.h similarity index 85% rename from ecal/core/src/ecal_global_accessors.h rename to src/core/src/ecal_global_accessors.h index 633e689..30e7604 100644 --- a/ecal/core/src/ecal_global_accessors.h +++ b/src/core/src/ecal_global_accessors.h @@ -34,49 +34,69 @@ namespace eCAL class CGlobals; class CConfig; class CLog; +#if ECAL_CORE_MONITORING class CMonitoring; +#endif +#if ECAL_CORE_TIMEPLUGIN class CTimeGate; +#endif +#if ECAL_CORE_REGISTRATION class CRegistrationProvider; + class CRegistrationReceiver; +#endif class CDescGate; +#if ECAL_CORE_SUBSCRIBER class CSubGate; +#endif +#if ECAL_CORE_PUBLISHER class CPubGate; +#endif +#if ECAL_CORE_SERVICE class CServiceGate; class CClientGate; - class CRegistrationReceiver; - -#ifndef ECAL_LAYER_ICEORYX +#endif +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) class CMemFileThreadPool; class CMemFileMap; -#endif /* !ECAL_LAYER_ICEORYX */ +#endif // Declaration of getter functions for globally accessible variable instances CGlobals* g_globals(); CConfig* g_config(); CLog* g_log(); +#if ECAL_CORE_MONITORING CMonitoring* g_monitoring(); +#endif +#if ECAL_CORE_TIMEPLUGIN CTimeGate* g_timegate(); +#endif +#if ECAL_CORE_REGISTRATION CRegistrationProvider* g_registration_provider(); + CRegistrationReceiver* g_registration_receiver(); +#endif CDescGate* g_descgate(); +#if ECAL_CORE_SUBSCRIBER CSubGate* g_subgate(); +#endif +#if ECAL_CORE_PUBLISHER CPubGate* g_pubgate(); +#endif +#if ECAL_CORE_SERVICE CServiceGate* g_servicegate(); CClientGate* g_clientgate(); - CRegistrationReceiver* g_registration_receiver(); - -#ifndef ECAL_LAYER_ICEORYX +#endif +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) CMemFileThreadPool* g_memfile_pool(); CMemFileMap* g_memfile_map(); -#endif /* !ECAL_LAYER_ICEORYX */ +#endif // declaration of globally accessible variables extern CGlobals* g_globals_ctx; extern std::atomic g_globals_ctx_ref_cnt; - extern std::atomic g_shutdown; extern std::string g_default_ini_file; extern std::string g_host_name; - extern int g_host_id; extern std::string g_unit_name; extern std::vector g_task_parameter; diff --git a/ecal/core/src/ecal_globals.cpp b/src/core/src/ecal_globals.cpp similarity index 68% rename from ecal/core/src/ecal_globals.cpp rename to src/core/src/ecal_globals.cpp index 2c5aa66..daae75b 100644 --- a/ecal/core/src/ecal_globals.cpp +++ b/src/core/src/ecal_globals.cpp @@ -22,11 +22,16 @@ **/ #include "ecal_globals.h" -#include "io/udp_init.h" -#include "ecal_config_reader.h" +#include "config/ecal_config_reader.h" + +#include #include +#if ECAL_CORE_SERVICE +#include "service/ecal_service_singleton_manager.h" +#endif + namespace eCAL { CGlobals::CGlobals() : initialized(false), components(0) @@ -35,16 +40,13 @@ namespace eCAL CGlobals::~CGlobals() { Finalize(Init::All); - }; + } int CGlobals::Initialize(unsigned int components_, std::vector* config_keys_ /*= nullptr*/) { // will be set if any new module was initialized bool new_initialization(false); - // this is needed here for functions like "GetHostName" on windows - Net::Initialize(); - ///////////////////// // CONFIG ///////////////////// @@ -59,7 +61,7 @@ namespace eCAL if (!config_instance->Validate()) { - std::string emsg("Core initialization failed cause by a configuration error."); + const std::string emsg("Core initialization failed cause by a configuration error."); std::cerr << std::endl; std::cerr << "----------------------------------------------------------" << std::endl; @@ -75,6 +77,7 @@ namespace eCAL new_initialization = true; } +#if ECAL_CORE_REGISTRATION ///////////////////// // REGISTRATION PROVIDER ///////////////////// @@ -85,28 +88,29 @@ namespace eCAL } ///////////////////// - // DESCRIPTION GATE + // REGISTRATION RECEIVER ///////////////////// - if(descgate_instance == nullptr) + if(registration_receiver_instance == nullptr) { - descgate_instance = std::make_unique(); + registration_receiver_instance = std::make_unique(); new_initialization = true; } +#endif // ECAL_CORE_REGISTRATION ///////////////////// - // REGISTRATION RECEIVER + // DESCRIPTION GATE ///////////////////// - if(registration_receiver_instance == nullptr) + if (descgate_instance == nullptr) { - registration_receiver_instance = std::make_unique(); + descgate_instance = std::make_unique(); new_initialization = true; } -#ifndef ECAL_LAYER_ICEORYX +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) ///////////////////// // MEMFILE MAP ///////////////////// - if(memfile_map_instance == nullptr) + if (memfile_map_instance == nullptr) { memfile_map_instance = std::make_unique(); new_initialization = true; @@ -115,17 +119,18 @@ namespace eCAL ///////////////////// // MEMFILE POOL ///////////////////// - if(memfile_pool_instance == nullptr) + if (memfile_pool_instance == nullptr) { memfile_pool_instance = std::make_unique(); new_initialization = true; } -#endif /* !ECAL_LAYER_ICEORYX */ +#endif // defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) +#if ECAL_CORE_SUBSCRIBER ///////////////////// // SUBSCRIBER GATE ///////////////////// - if (components_ & Init::Subscriber) + if ((components_ & Init::Subscriber) != 0u) { if (subgate_instance == nullptr) { @@ -133,11 +138,13 @@ namespace eCAL new_initialization = true; } } +#endif // ECAL_CORE_SUBSCRIBER +#if ECAL_CORE_PUBLISHER ///////////////////// // PUBLISHER GATE ///////////////////// - if (components_ & Init::Publisher) + if ((components_ & Init::Publisher) != 0u) { if (pubgate_instance == nullptr) { @@ -145,9 +152,14 @@ namespace eCAL new_initialization = true; } } +#endif // ECAL_CORE_PUBLISHER - if (components_ & Init::Service) +#if ECAL_CORE_SERVICE + if ((components_ & Init::Service) != 0u) { + // Reset the service manager, so it will be able to create new services, again + eCAL::service::ServiceManager::instance()->reset(); + ///////////////////// // SERVICE GATE ///////////////////// @@ -166,11 +178,13 @@ namespace eCAL new_initialization = true; } } +#endif // ECAL_CORE_SERVICE +#if ECAL_CORE_TIMEPLUGIN ///////////////////// // TIMEGATE ///////////////////// - if (components_ & Init::TimeSync) + if ((components_ & Init::TimeSync) != 0u) { if (timegate_instance == nullptr) { @@ -178,11 +192,13 @@ namespace eCAL new_initialization = true; } } +#endif // ECAL_CORE_TIMEPLUGIN +#if ECAL_CORE_MONITORING ///////////////////// // MONITORING ///////////////////// - if (components_ & Init::Monitoring) + if ((components_ & Init::Monitoring) != 0u) { if (monitoring_instance == nullptr) { @@ -190,11 +206,12 @@ namespace eCAL new_initialization = true; } } +#endif // ECAL_CORE_MONITORING ///////////////////// // LOGGING ///////////////////// - if (components_ & Init::Logging) + if ((components_ & Init::Logging) != 0u) { if (log_instance == nullptr) { @@ -206,21 +223,32 @@ namespace eCAL ///////////////////// // CREATE ALL ///////////////////// - //if (config_instance) config_instance->Create(); - if (log_instance && (components_ & Init::Logging)) log_instance->Create(); - if (registration_provider_instance) registration_provider_instance->Create(true, true, (components_ & Init::ProcessReg) != 0x0); - if (descgate_instance) descgate_instance->Create(); - if (registration_receiver_instance) registration_receiver_instance->Create(); -#ifndef ECAL_LAYER_ICEORYX - if (memfile_pool_instance) memfile_pool_instance->Create(); -#endif /* !ECAL_LAYER_ICEORYX */ - if (subgate_instance && (components_ & Init::Subscriber)) subgate_instance->Create(); - if (pubgate_instance && (components_ & Init::Publisher)) pubgate_instance->Create(); - if (servicegate_instance && (components_ & Init::Service)) servicegate_instance->Create(); - if (clientgate_instance && (components_ & Init::Service)) clientgate_instance->Create(); - if (timegate_instance && (components_ & Init::TimeSync)) timegate_instance->Create(CTimeGate::eTimeSyncMode::realtime); - if (monitoring_instance && (components_ & Init::Monitoring)) monitoring_instance->Create(); - + //if (config_instance) config_instance->Create(); + if (log_instance && ((components_ & Init::Logging) != 0u)) log_instance->Create(); +#if ECAL_CORE_REGISTRATION + if (registration_provider_instance) registration_provider_instance->Create(true, true, (components_ & Init::ProcessReg) != 0x0); + if (registration_receiver_instance) registration_receiver_instance->Create(); +#endif + if (descgate_instance) descgate_instance->Create(); +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) + if (memfile_pool_instance) memfile_pool_instance->Create(); +#endif +#if ECAL_CORE_SUBSCRIBER + if (subgate_instance && ((components_ & Init::Subscriber) != 0u)) subgate_instance->Create(); +#endif +#if ECAL_CORE_PUBLISHER + if (pubgate_instance && ((components_ & Init::Publisher) != 0u)) pubgate_instance->Create(); +#endif +#if ECAL_CORE_SERVICE + if (servicegate_instance && ((components_ & Init::Service) != 0u)) servicegate_instance->Create(); + if (clientgate_instance && ((components_ & Init::Service) != 0u)) clientgate_instance->Create(); +#endif +#if ECAL_CORE_TIMEPLUGIN + if (timegate_instance && ((components_ & Init::TimeSync) != 0u)) timegate_instance->Create(CTimeGate::eTimeSyncMode::realtime); +#endif +#if ECAL_CORE_MONITORING + if (monitoring_instance && ((components_ & Init::Monitoring) != 0u)) monitoring_instance->Create(); +#endif initialized = true; components |= components_; @@ -239,18 +267,28 @@ namespace eCAL // check single component initialization switch (component_) { +#if ECAL_CORE_PUBLISHER case Init::Publisher: return(pubgate_instance != nullptr); +#endif +#if ECAL_CORE_SUBSCRIBER case Init::Subscriber: return(subgate_instance != nullptr); +#endif +#if ECAL_CORE_SERVICE case Init::Service: return(servicegate_instance != nullptr); +#endif +#if ECAL_CORE_MONITORING case Init::Monitoring: return(monitoring_instance != nullptr); +#endif case Init::Logging: return(log_instance != nullptr); +#if ECAL_CORE_TIMEPLUGIN case Init::TimeSync: return(timegate_instance != nullptr); +#endif default: return(0); } @@ -261,40 +299,68 @@ namespace eCAL if (!initialized) return(1); // start destruction +#if ECAL_CORE_MONITORING if (monitoring_instance) monitoring_instance->Destroy(); +#endif +#if ECAL_CORE_TIMEPLUGIN if (timegate_instance) timegate_instance->Destroy(); +#endif +#if ECAL_CORE_SERVICE + // The order here is EXTREMELY important! First, the actual service + // implementation must be stopped (->Service Manager), then the + // clientgate/servicegate. The callbacks in the service implementation carry + // raw pointers to the gate's functions, so we must make sure that everything + // has been executed, before we delete the gates. + eCAL::service::ServiceManager::instance()->stop(); if (clientgate_instance) clientgate_instance->Destroy(); if (servicegate_instance) servicegate_instance->Destroy(); +#endif +#if ECAL_CORE_PUBLISHER if (pubgate_instance) pubgate_instance->Destroy(); +#endif +#if ECAL_CORE_SUBSCRIBER if (subgate_instance) subgate_instance->Destroy(); - if (registration_receiver_instance) registration_receiver_instance->Destroy(); +#endif if (descgate_instance) descgate_instance->Destroy(); +#if ECAL_CORE_REGISTRATION + if (registration_receiver_instance) registration_receiver_instance->Destroy(); if (registration_provider_instance) registration_provider_instance->Destroy(); -#ifndef ECAL_LAYER_ICEORYX +#endif +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) if (memfile_pool_instance) memfile_pool_instance->Destroy(); if (memfile_map_instance) memfile_map_instance->Destroy(); -#endif /* !ECAL_LAYER_ICEORYX */ +#endif if (log_instance) log_instance->Destroy(); //if (config_instance) config_instance->Destroy(); +#if ECAL_CORE_MONITORING monitoring_instance = nullptr; +#endif +#if ECAL_CORE_TIMEPLUGIN timegate_instance = nullptr; +#endif +#if ECAL_CORE_SERVICE servicegate_instance = nullptr; + clientgate_instance = nullptr; +#endif +#if ECAL_CORE_PUBLISHER pubgate_instance = nullptr; +#endif +#if ECAL_CORE_SUBSCRIBER subgate_instance = nullptr; +#endif +#if ECAL_CORE_REGISTRATION registration_receiver_instance = nullptr; - descgate_instance = nullptr; registration_provider_instance = nullptr; -#ifndef ECAL_LAYER_ICEORYX +#endif + descgate_instance = nullptr; +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) memfile_pool_instance = nullptr; memfile_map_instance = nullptr; -#endif /* !ECAL_LAYER_ICEORYX */ +#endif log_instance = nullptr; config_instance = nullptr; - // last not least we close all - Net::Finalize(); - initialized = false; return(0); diff --git a/ecal/core/src/ecal_globals.h b/src/core/src/ecal_globals.h similarity index 79% rename from ecal/core/src/ecal_globals.h rename to src/core/src/ecal_globals.h index e79812b..06c3a0b 100644 --- a/ecal/core/src/ecal_globals.h +++ b/src/core/src/ecal_globals.h @@ -24,21 +24,30 @@ #pragma once #include "ecal_global_accessors.h" -#include "ecal_registration_provider.h" -#include "ecal_registration_receiver.h" -#include "ecal_descgate.h" -#include "ecal_timegate.h" -#include "ecal_log_impl.h" -#include "mon/ecal_monitoring_def.h" +#if ECAL_CORE_REGISTRATION +#include "registration/ecal_registration_provider.h" +#include "registration/ecal_registration_receiver.h" +#endif +#include "time/ecal_timegate.h" +#include "logging/ecal_log_impl.h" +#if ECAL_CORE_MONITORING +#include "monitoring/ecal_monitoring_def.h" +#endif +#if ECAL_CORE_PUBLISHER #include "pubsub/ecal_pubgate.h" +#endif +#if ECAL_CORE_SUBSCRIBER #include "pubsub/ecal_subgate.h" +#endif +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) +#include "io/shm/ecal_memfile_pool.h" +#include "io/shm/ecal_memfile_db.h" +#endif +#if ECAL_CORE_SERVICE #include "service/ecal_servicegate.h" #include "service/ecal_clientgate.h" - -#ifndef ECAL_LAYER_ICEORYX -#include "io/ecal_memfile_pool.h" -#include "io/ecal_memfile_db.h" -#endif /* !ECAL_LAYER_ICEORYX */ +#endif +#include "ecal_descgate.h" #include @@ -53,43 +62,67 @@ namespace eCAL int Initialize ( unsigned int components_, std::vector* config_keys_ = nullptr); int IsInitialized ( unsigned int component_ ); - unsigned int GetComponents() { return(components); }; + unsigned int GetComponents() const { return(components); }; int Finalize(unsigned int components_); const std::unique_ptr& config() { return config_instance; }; const std::unique_ptr& log() { return log_instance; }; +#if ECAL_CORE_MONITORING const std::unique_ptr& monitoring() { return monitoring_instance; }; +#endif +#if ECAL_CORE_TIMEPLUGIN const std::unique_ptr& timegate() { return timegate_instance; }; +#endif +#if ECAL_CORE_SUBSCRIBER const std::unique_ptr& subgate() { return subgate_instance; }; +#endif +#if ECAL_CORE_PUBLISHER const std::unique_ptr& pubgate() { return pubgate_instance; }; +#endif +#if ECAL_CORE_SERVICE const std::unique_ptr& servicegate() { return servicegate_instance; }; const std::unique_ptr& clientgate() { return clientgate_instance; }; +#endif +#if ECAL_CORE_REGISTRATION const std::unique_ptr& registration_provider() { return registration_provider_instance; }; - const std::unique_ptr& descgate() { return descgate_instance; }; const std::unique_ptr& registration_receiver() { return registration_receiver_instance; }; -#ifndef ECAL_LAYER_ICEORYX +#endif +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) const std::unique_ptr& memfile_pool() { return memfile_pool_instance; }; const std::unique_ptr& memfile_map() { return memfile_map_instance; }; -#endif /* !ECAL_LAYER_ICEORYX */ +#endif + const std::unique_ptr& descgate() { return descgate_instance; }; private: bool initialized; unsigned int components; std::unique_ptr config_instance; std::unique_ptr log_instance; +#if ECAL_CORE_MONITORING std::unique_ptr monitoring_instance; +#endif +#if ECAL_CORE_TIMEPLUGIN std::unique_ptr timegate_instance; +#endif +#if ECAL_CORE_SUBSCRIBER std::unique_ptr subgate_instance; +#endif +#if ECAL_CORE_PUBLISHER std::unique_ptr pubgate_instance; +#endif +#if ECAL_CORE_SERVICE std::unique_ptr servicegate_instance; std::unique_ptr clientgate_instance; +#endif +#if ECAL_CORE_REGISTRATION std::unique_ptr registration_provider_instance; - std::unique_ptr descgate_instance; std::unique_ptr registration_receiver_instance; -#ifndef ECAL_LAYER_ICEORYX +#endif +#if defined(ECAL_CORE_REGISTRATION_SHM) || defined(ECAL_CORE_TRANSPORT_SHM) std::unique_ptr memfile_pool_instance; std::unique_ptr memfile_map_instance; -#endif /* !ECAL_LAYER_ICEORYX */ +#endif + std::unique_ptr descgate_instance; }; } diff --git a/ecal/core/src/ecal_process.cpp b/src/core/src/ecal_process.cpp similarity index 89% rename from ecal/core/src/ecal_process.cpp rename to src/core/src/ecal_process.cpp index dadead2..2d1b23d 100644 --- a/ecal/core/src/ecal_process.cpp +++ b/src/core/src/ecal_process.cpp @@ -22,31 +22,31 @@ **/ #include -#include #include "ecal_def.h" -#include "ecal_config_reader_hlp.h" -#include "ecal_registration_provider.h" -#include "ecal_registration_receiver.h" #include "ecal_globals.h" -#include "ecal_process.h" +#include "ecal_process_stub.h" +#include "ecal_utils/command_line.h" +#include "ecal_utils/ecal_utils.h" +#include "ecal_utils/str_convert.h" + +#include "config/ecal_config_reader_hlp.h" +#include "io/udp/ecal_udp_configurations.h" + +#include #include +#include #include -#include +#include +#include +#include +#include #include -#include -#include #include -#include - -#include "sys_usage.h" - -#include -#include #include -#include -#include +#include +#include #ifdef ECAL_OS_WINDOWS #include "ecal_win_main.h" @@ -66,10 +66,6 @@ #include #include #include - -#include "ecal_process_stub.h" -#include - #endif /* ECAL_OS_LINUX */ #ifdef ECAL_OS_MACOS @@ -87,8 +83,6 @@ #include #endif // ECAL_NPCAP_SUPPORT -#include - #ifndef NDEBUG #define STD_COUT_DEBUG( x ) { std::stringstream ss; ss << x; std::cout << ss.str(); } #else @@ -129,52 +123,6 @@ namespace } return "???"; } -#if defined(ECAL_OS_WINDOWS) - std::pair get_host_id() - { - // retrieve needed buffer size for GetAdaptersInfo - ULONG alloc_adapters_size(0); - { - IP_ADAPTER_INFO AdapterInfo; - GetAdaptersInfo(&AdapterInfo, &alloc_adapters_size); - } - if(alloc_adapters_size == 0) return std::make_pair(false, 0); - - // allocate adapter memory - auto adapter_mem = std::make_unique(static_cast(alloc_adapters_size)); - - // get all adapter infos - PIP_ADAPTER_INFO pAdapter(nullptr); - pAdapter = reinterpret_cast(adapter_mem.get()); - DWORD dwStatus = GetAdaptersInfo(pAdapter, &alloc_adapters_size); - if (dwStatus != ERROR_SUCCESS) return std::make_pair(false, 0); - - // iterate adapters and create hash - int hash(0); - while(pAdapter) - { - for (UINT i = 0; i < pAdapter->AddressLength; ++i) - { - hash += (pAdapter->Address[i] << ((i & 1) * 8)); - } - pAdapter = pAdapter->Next; - } - - // return success - return std::make_pair(true, hash); - } -#elif defined(ECAL_OS_QNX) - std::pair get_host_id() - { - // TODO: Find a suitable method on QNX to calculate an unqiue host identifier - return std::make_pair(true, -1); - } -#else - std::pair get_host_id() - { - return std::make_pair(true, static_cast(gethostid())); - } -#endif } namespace eCAL @@ -205,7 +153,7 @@ namespace eCAL #endif sstream << std::endl; - if (!eCAL::IsInitialized()) + if (eCAL::IsInitialized() == 0) { sstream << "Components : NOT INITIALIZED ( call eCAL::Initialize() )"; sstream << std::endl; @@ -219,6 +167,7 @@ namespace eCAL sstream << "------------------------- NETWORK --------------------------------" << std::endl; sstream << "Host name : " << Process::GetHostName() << std::endl; + sstream << "Host group name : " << Process::GetHostGroupName() << std::endl; if (Config::IsNetworkEnabled()) { @@ -228,26 +177,18 @@ namespace eCAL { sstream << "Network mode : local" << std::endl; } - sstream << "Network ttl : " << Config::GetUdpMulticastTtl() << std::endl; + sstream << "Network ttl : " << UDP::GetMulticastTtl() << std::endl; sstream << "Network sndbuf : " << GetBufferStr(Config::GetUdpMulticastSndBufSizeBytes()) << std::endl; sstream << "Network rcvbuf : " << GetBufferStr(Config::GetUdpMulticastRcvBufSizeBytes()) << std::endl; sstream << "Multicast cfg version : v" << static_cast(Config::GetUdpMulticastConfigVersion()) << std::endl; sstream << "Multicast group : " << Config::GetUdpMulticastGroup() << std::endl; sstream << "Multicast mask : " << Config::GetUdpMulticastMask() << std::endl; - int port = Config::GetUdpMulticastPort(); + const int port = Config::GetUdpMulticastPort(); sstream << "Multicast ports : " << port << " - " << port + 10 << std::endl; sstream << "Multicast join all IFs : " << (Config::IsUdpMulticastJoinAllIfEnabled() ? "on" : "off") << std::endl; - auto bandwidth = Config::GetMaxUdpBandwidthBytesPerSecond(); - if (bandwidth < 0) - { - sstream << "Bandwidth limit (udp) : not limited" << std::endl; - } - else - { - sstream << "Bandwidth limit udp : " << GetBufferStr(bandwidth) + "/s" << std::endl; - } sstream << std::endl; +#if ECAL_CORE_TIMEPLUGIN sstream << "------------------------- TIME -----------------------------------" << std::endl; sstream << "Synchronization realtime : " << Config::GetTimesyncModuleName() << std::endl; sstream << "Synchronization replay : " << eCALPAR(TIME, SYNC_MOD_REPLAY) << std::endl; @@ -262,30 +203,14 @@ namespace eCAL g_timegate()->GetStatus(status_state, &status_msg); sstream << "Status (Code) : \"" << status_msg << "\" (" << status_state << ")" << std::endl; sstream << std::endl; - - sstream << "------------------------- PUBLISHER LAYER DEFAULTS ---------------" << std::endl; - sstream << "Layer Mode INPROC : " << LayerMode(Config::GetPublisherInprocMode()) << std::endl; - auto zero_copy = Config::IsMemfileZerocopyEnabled(); - - if (zero_copy) - { - sstream << "Layer Mode SHM (ZEROCPY) : " << LayerMode(Config::GetPublisherShmMode()) << std::endl; - } - else - { - sstream << "Layer Mode SHM : " << LayerMode(Config::GetPublisherShmMode()) << std::endl; - } - sstream << "Layer Mode TCP : " << LayerMode(Config::GetPublisherTcpMode()) << std::endl; - sstream << "Layer Mode UDP MC : " << LayerMode(Config::GetPublisherUdpMulticastMode()) << std::endl; - sstream << std::endl; - - sstream << "------------------------- SUBSCRIPTION LAYER DEFAULTS ------------" << std::endl; - sstream << "Layer Mode INPROC : " << LayerMode(Config::IsInprocRecEnabled()) << std::endl; - sstream << "Layer Mode SHM : " << LayerMode(Config::IsShmRecEnabled()) << std::endl; - sstream << "Layer Mode TCP : " << LayerMode(Config::IsTcpRecEnabled()) << std::endl; - sstream << "Layer Mode UDP MC : " << LayerMode(Config::IsUdpMulticastRecEnabled()) << std::endl; - sstream << "Npcap UDP Reciever : " << LayerMode(Config::IsNpcapEnabled()); +#endif +#if ECAL_CORE_SUBSCRIBER + sstream << "------------------------- SUBSCRIPTION LAYER DEFAULTS ------------" << std::endl; + sstream << "Layer Mode UDP MC : " << LayerMode(Config::IsUdpMulticastRecEnabled()) << std::endl; + sstream << "Drop out-of-order msgs : " << (Config::Experimental::GetDropOutOfOrderMessages() ? "on" : "off") << std::endl; +#endif #ifdef ECAL_NPCAP_SUPPORT + sstream << "Npcap UDP Reciever : " << LayerMode(Config::IsNpcapEnabled()); if(Config::IsNpcapEnabled() && !Udpcap::Initialize()) { sstream << " (Init FAILED!)"; @@ -298,7 +223,6 @@ namespace eCAL #endif // ECAL_NPCAP_SUPPORT sstream << std::endl; - // write it into std:string cfg_s_ = sstream.str(); } @@ -320,34 +244,9 @@ namespace eCAL return(g_host_name); } - int GetHostID() - { - return internal::GetHostID(); - } - - namespace internal + std::string GetHostGroupName() { - int GetHostID() - { - if (g_host_id == 0) - { - // try to get unique host id - bool success(false); - int id(0); - std::tie(success, id) = get_host_id(); - if (success) - { - g_host_id = id; - } - // never try again to not waste time - else - { - g_host_id = -1; - std::cerr << "Unable to get host id" << std::endl; - } - } - return(g_host_id); - } + return Config::GetHostGroupName().empty() ? GetHostName() : Config::GetHostGroupName(); } std::string GetUnitName() @@ -358,7 +257,7 @@ namespace eCAL std::string GetTaskParameter(const char* sep_) { std::string par_line; - for (auto par : g_task_parameter) + for (const auto& par : g_task_parameter) { if (!par_line.empty()) par_line += sep_; par_line += par; @@ -389,41 +288,6 @@ namespace eCAL #endif } - float GetProcessCpuUsage() - { - return(GetCPULoad() * 100.0f); - } - - long long GetSClock() - { - return(GetWClock()); - }; - - long long GetSBytes() - { - return(GetWBytes()); - }; - - long long GetWClock() - { - return(g_process_wclock); - }; - - long long GetWBytes() - { - return(g_process_wbytes); - }; - - long long GetRClock() - { - return(g_process_rclock); - }; - - long long GetRBytes() - { - return(g_process_rbytes); - }; - void SetState(eCAL_Process_eSeverity severity_, eCAL_Process_eSeverity_Level level_, const char* info_) { g_process_severity = severity_; @@ -434,17 +298,21 @@ namespace eCAL } } - int AddRegistrationCallback(enum eCAL_Registration_Event event_, RegistrationCallbackT callback_) + int AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_) { - if (!g_registration_receiver()) return -1; +#if ECAL_CORE_REGISTRATION + if (g_registration_receiver() == nullptr) return -1; if (g_registration_receiver()->AddRegistrationCallback(event_, callback_)) return 0; +#endif return -1; } int RemRegistrationCallback(enum eCAL_Registration_Event event_) { - if (!g_registration_receiver()) return -1; +#if ECAL_CORE_REGISTRATION + if (g_registration_receiver() == nullptr) return -1; if (g_registration_receiver()->RemRegistrationCallback(event_)) return 0; +#endif return -1; } } @@ -486,7 +354,7 @@ namespace eCAL if (g_process_name.empty()) { WCHAR pname[1024] = { 0 }; - GetModuleFileNameExW(GetCurrentProcess(), 0, pname, 1024); + GetModuleFileNameExW(GetCurrentProcess(), nullptr, pname, 1024); g_process_name = EcalUtils::StrConvert::WideToUtf8(pname); } return(g_process_name); @@ -501,14 +369,6 @@ namespace eCAL return(g_process_par); } - unsigned long GetProcessMemory() - { - PROCESS_MEMORY_COUNTERS pmc; - GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); - SIZE_T msize = pmc.PagefileUsage; - return(static_cast(msize)); - } - int StartProcess(const char* proc_name_, const char* proc_args_, const char* working_dir_, const bool create_console_, const eCAL_Process_eStartMode process_mode_, const bool block_) { int ret_pid = 0; @@ -520,7 +380,7 @@ namespace eCAL proc_name = exp_name; } - std::string proc_args = proc_args_; + const std::string proc_args = proc_args_; if (!proc_args.empty()) { proc_name += " "; @@ -533,7 +393,7 @@ namespace eCAL creation_flag = CREATE_NEW_CONSOLE; } - short win_state; + short win_state = 0; switch (process_mode_) { case 0: @@ -610,11 +470,11 @@ namespace eCAL if (block_) { // Wait until child process exits. - if (pi.hProcess) WaitForSingleObject(pi.hProcess, INFINITE); + if (pi.hProcess != nullptr) WaitForSingleObject(pi.hProcess, INFINITE); // Close process and thread handles. - if (pi.hProcess) CloseHandle(pi.hProcess); - if (pi.hThread) CloseHandle(pi.hThread); + if (pi.hProcess != nullptr) CloseHandle(pi.hProcess); + if (pi.hThread != nullptr) CloseHandle(pi.hThread); } return(ret_pid); @@ -676,8 +536,8 @@ namespace eCAL GetExitCodeProcess(pi.hProcess, &taskkill_error); // Close process and thread handles. - if (pi.hProcess) CloseHandle(pi.hProcess); - if (pi.hThread) CloseHandle(pi.hThread); + if (pi.hProcess != nullptr) CloseHandle(pi.hProcess); + if (pi.hThread != nullptr) CloseHandle(pi.hThread); return (taskkill_error == 0); } @@ -729,8 +589,8 @@ namespace eCAL GetExitCodeProcess(pi.hProcess, &taskkill_error); // Close process and thread handles. - if (pi.hProcess) CloseHandle(pi.hProcess); - if (pi.hThread) CloseHandle(pi.hThread); + if (pi.hProcess != nullptr) CloseHandle(pi.hProcess); + if (pi.hThread != nullptr) CloseHandle(pi.hThread); return (taskkill_error == 0); } @@ -759,15 +619,6 @@ namespace } } - int parseLine(char* line) - { - int i = strlen(line); - while (*line < '0' || *line > '9') line++; - line[i - 3] = '\0'; - i = atoi(line); - return i; - } - /** * @brief Checks whether all requirements for using a terminal emulator are fulfilled and then returns the according command * @return The terminal emulator command or an empty string, if the requirements are not fulfilled @@ -1132,25 +983,6 @@ namespace eCAL return(g_process_par); } - unsigned long GetProcessMemory() - { - FILE* file = fopen("/proc/self/status", "r"); - if (file == nullptr) return(0); - - int result = 0; - char line[128] = { 0 }; - while (fgets(line, 128, file) != nullptr) - { - if (strncmp(line, "VmSize:", 7) == 0) - { - result = parseLine(line); - break; - } - } - fclose(file); - return(result * 1024); - } - int StartProcess(const char* proc_name_, const char* proc_args_, const char* working_dir_, diff --git a/ecal/core/src/ecal_process_stub.cpp b/src/core/src/ecal_process_stub.cpp similarity index 100% rename from ecal/core/src/ecal_process_stub.cpp rename to src/core/src/ecal_process_stub.cpp diff --git a/ecal/core/src/ecal_process_stub.h b/src/core/src/ecal_process_stub.h similarity index 100% rename from ecal/core/src/ecal_process_stub.h rename to src/core/src/ecal_process_stub.h diff --git a/src/core/src/ecal_sample_to_topicinfo.h b/src/core/src/ecal_sample_to_topicinfo.h new file mode 100644 index 0000000..d4d5500 --- /dev/null +++ b/src/core/src/ecal_sample_to_topicinfo.h @@ -0,0 +1,56 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief helper class to copy from eCAL::pb:Sample to SDataTypeInformation +**/ + +#pragma once + +#include +#include + +#include "serialization/ecal_serialize_sample_registration.h" + +namespace eCAL +{ + inline SDataTypeInformation eCALSampleToTopicInformation(const eCAL::Registration::Sample& sample) + { + SDataTypeInformation topic; + const auto& tdatatype = sample.topic.tdatatype; + topic.encoding = tdatatype.encoding; + topic.name = tdatatype.name; + topic.descriptor = tdatatype.desc; + return topic; + } + + // This function can be removed in eCAL6. For the time being we need to enrich incoming samples with additional topic information. + inline void ModifyIncomingSampleForBackwardsCompatibility(const eCAL::Registration::Sample& sample, eCAL::Registration::Sample& modified_sample) + { + modified_sample = sample; + if (!modified_sample.topic.ttype.empty() && modified_sample.topic.tdatatype.name.empty()) + { + auto& topic_datatype = modified_sample.topic.tdatatype; + auto split_type = Util::SplitCombinedTopicType(modified_sample.topic.ttype); + topic_datatype.encoding = split_type.first; + topic_datatype.name = split_type.second; + topic_datatype.desc = modified_sample.topic.tdesc; + } + } +} diff --git a/src/core/src/ecal_util.cpp b/src/core/src/ecal_util.cpp new file mode 100644 index 0000000..e636981 --- /dev/null +++ b/src/core/src/ecal_util.cpp @@ -0,0 +1,239 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "ecal_globals.h" +#include "ecal_event.h" +#include "registration/ecal_registration_receiver.h" +#include "pubsub/ecal_pubgate.h" + +#include +#include + +namespace eCAL +{ + namespace Util + { + void EnableLoopback(bool state_) + { +#if ECAL_CORE_REGISTRATION + if (g_registration_receiver() != nullptr) g_registration_receiver()->EnableLoopback(state_); +#endif + } + + void PubShareType(bool state_) + { +#if ECAL_CORE_PUBLISHER + if (g_pubgate() != nullptr) g_pubgate()->ShareType(state_); +#endif + } + + void PubShareDescription(bool state_) + { +#if ECAL_CORE_PUBLISHER + if (g_pubgate() != nullptr) g_pubgate()->ShareDescription(state_); +#endif + } + +#if ECAL_CORE_MONITORING + // take monitoring snapshot + static Monitoring::SMonitoring GetMonitoring() + { + if (!IsInitialized(Init::Monitoring)) + { + Initialize(0, nullptr, "", Init::Monitoring); + Process::SleepMS(1000); + } + + Monitoring::SMonitoring monitoring; + if (g_monitoring() != nullptr) g_monitoring()->GetMonitoring(monitoring); + + return(monitoring); + } + + void ShutdownProcess(const std::string& process_name_) + { + const Monitoring::SMonitoring monitoring = GetMonitoring(); + const std::string host_name = Process::GetHostName(); + + std::vector proc_id_list; + for (const auto& process : monitoring.processes) + { + const std::string pname = process.pname; + if ( (pname == process_name_) + && (process.hname == host_name) + ) + { + proc_id_list.push_back(process.pid); + } + } + + for (auto id : proc_id_list) + { + ShutdownProcess(id); + } + } + + void ShutdownProcess(const int process_id_) + { + const std::string event_name = EVENT_SHUTDOWN_PROC + std::string("_") + std::to_string(process_id_); + EventHandleT event; + if (gOpenNamedEvent(&event, event_name, true)) + { + std::cout << "Shutdown local eCAL process " << process_id_ << std::endl; + gSetEvent(event); + gCloseEvent(event); + } + } + + void ShutdownProcesses() + { + const Monitoring::SMonitoring monitoring = GetMonitoring(); + const std::string host_name = eCAL::Process::GetHostName(); + + std::vector proc_id_list; + for (const auto& process : monitoring.processes) + { + const std::string uname = process.uname; + if ((uname != "eCALMon") + && (uname != "eCALPlay") + && (uname != "eCALPlayGUI") + && (uname != "eCALRec") + && (uname != "eCALRecGUI") + && (uname != "eCALSys") + && (uname != "eCALSysGUI") + && (uname != "eCALStop") + && (uname != "eCALTopic") + && (process.hname == host_name) + ) + { + proc_id_list.push_back(process.pid); + } + } + + for (auto id : proc_id_list) + { + ShutdownProcess(id); + } + } + + void ShutdownCore() + { + const Monitoring::SMonitoring monitoring = GetMonitoring(); + const std::string host_name = Process::GetHostName(); + + std::vector proc_id_list; + for (const auto& process : monitoring.processes) + { + const std::string uname = process.uname; + if (((uname == "eCALMon") + || (uname == "eCALPlay") + || (uname == "eCALPlayGUI") + || (uname == "eCALRec") + || (uname == "eCALRecGUI") + || (uname == "eCALSys") + || (uname == "eCALSysGUI") + || (uname == "eCALStop") + || (uname == "eCALTopic") + ) + && (process.hname == host_name) + ) + { + proc_id_list.push_back(process.pid); + } + } + + for (auto id : proc_id_list) + { + ShutdownProcess(id); + } + } +#endif // ECAL_CORE_MONITORING + + void GetTopics(std::unordered_map& topic_info_map_) + { + if (g_descgate() == nullptr) return; + g_descgate()->GetTopics(topic_info_map_); + } + + void GetTopicNames(std::vector& topic_names_) + { + if (g_descgate() == nullptr) return; + g_descgate()->GetTopicNames(topic_names_); + } + + bool GetTopicDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& topic_info_) + { + if (g_descgate() == nullptr) return(false); + return(g_descgate()->GetDataTypeInformation(topic_name_, topic_info_)); + } + + void GetServices(std::map, SServiceMethodInformation>& service_info_map_) + { + if (g_descgate() == nullptr) return; + g_descgate()->GetServices(service_info_map_); + } + + void GetServiceNames(std::vector>& service_method_names_) + { + if (g_descgate() == nullptr) return; + g_descgate()->GetServiceNames(service_method_names_); + } + + bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_) + { + if (g_descgate() == nullptr) return(false); + return(g_descgate()->GetServiceTypeNames(service_name_, method_name_, req_type_, resp_type_)); + } + + bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_) + { + if (g_descgate() == nullptr) return(false); + return(g_descgate()->GetServiceDescription(service_name_, method_name_, req_desc_, resp_desc_)); + } + + std::pair SplitCombinedTopicType(const std::string& combined_topic_type_) + { + auto pos = combined_topic_type_.find(':'); + if (pos == std::string::npos) + { + std::string encoding; + std::string type{ combined_topic_type_ }; + return std::make_pair(encoding, type); + } + else + { + std::string encoding = combined_topic_type_.substr(0, pos); + std::string type = combined_topic_type_.substr(pos + 1); + return std::make_pair(encoding, type); + } + } + + std::string CombinedTopicEncodingAndType(const std::string& topic_encoding_, const std::string& topic_type_) + { + if (topic_encoding_.empty()) + { + return topic_type_; + } + else + { + return topic_encoding_ + ":" + topic_type_; + } + } + } +} diff --git a/ecal/core/src/ecal_win_main.h b/src/core/src/ecal_win_main.h similarity index 98% rename from ecal/core/src/ecal_win_main.h rename to src/core/src/ecal_win_main.h index c04136f..f3d8fea 100644 --- a/ecal/core/src/ecal_win_main.h +++ b/src/core/src/ecal_win_main.h @@ -35,6 +35,7 @@ #define WIN32_LEAN_AND_CLEAN #endif // !defined(WIN32_LEAN_AND_CLEAN) +#include #include #include #include diff --git a/src/core/src/ecalc.cpp b/src/core/src/ecalc.cpp new file mode 100644 index 0000000..14ce7fc --- /dev/null +++ b/src/core/src/ecalc.cpp @@ -0,0 +1,767 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief Implementation of the eCAL dll interface +**/ + +#include + +#include +#include + +static int CopyBuffer(void* target_, int target_len_, const std::string& source_s_) +{ + if(target_ == nullptr) return(0); + if(source_s_.empty()) return(0); + if(target_len_ == ECAL_ALLOCATE_4ME) + { + void* buf_alloc = malloc(source_s_.size()); + if(buf_alloc == nullptr) return(0); + int copied = CopyBuffer(buf_alloc, static_cast(source_s_.size()), source_s_); + if(copied > 0) + { + *((void**)target_) = buf_alloc; //-V206 + return(copied); + } + else + { + // copying buffer failed, so free allocated memory. + free(buf_alloc); + } + } + else + { + if(target_len_ < static_cast(source_s_.size())) return(0); + memcpy(target_, source_s_.data(), source_s_.size()); + return(static_cast(source_s_.size())); + } + return(0); +} + +///////////////////////////////////////////////////////// +// Core +///////////////////////////////////////////////////////// +extern "C" +{ + ECALC_API const char* eCAL_GetVersionString() + { + return(ECAL_VERSION); + } + + ECALC_API const char* eCAL_GetVersionDateString() + { + return(ECAL_DATE); + } + + ECALC_API int eCAL_GetVersion(int* major_, int* minor_, int* patch_) + { + if((major_ == nullptr) || (minor_ == nullptr) || (patch_ == nullptr)) return(-1); + *major_ = ECAL_VERSION_MAJOR; + *minor_ = ECAL_VERSION_MINOR; + *patch_ = ECAL_VERSION_PATCH; + return(0); + } + + ECALC_API int eCAL_Initialize(int argc_, char **argv_, const char *unit_name_, unsigned int components_) + { + return(eCAL::Initialize(argc_, argv_, unit_name_, components_)); + } + + ECALC_API int eCAL_SetUnitName(const char *unit_name_) + { + return(eCAL::SetUnitName(unit_name_)); + } + + ECALC_API int eCAL_Finalize(unsigned int components_) + { + return(eCAL::Finalize(components_)); + } + + ECALC_API int eCAL_IsInitialized(unsigned int component_) + { + return(eCAL::IsInitialized(component_)); + } + + ECALC_API int eCAL_Ok() + { + return(eCAL::Ok()); + } + + ECALC_API void eCAL_FreeMem(void* mem_) + { + free(mem_); + } +} + +///////////////////////////////////////////////////////// +// Util +///////////////////////////////////////////////////////// +extern "C" +{ +#if ECAL_CORE_MONITORING + ECALC_API void eCAL_Util_ShutdownUnitName(const char* unit_name_) + { + std::string unit_name = unit_name_; + eCAL::Util::ShutdownProcess(unit_name); + } + + ECALC_API void eCAL_Util_ShutdownProcessID(int process_id_) + { + eCAL::Util::ShutdownProcess(process_id_); + } + + ECALC_API void eCAL_Util_ShutdownProcesses() + { + eCAL::Util::ShutdownProcesses(); + } + + ECALC_API void eCAL_Util_ShutdownCore() + { + eCAL::Util::ShutdownCore(); + } +#endif // ECAL_CORE_MONITORING + + ECALC_API void eCAL_Util_EnableLoopback(int state_) + { + 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_) return(0); + if(!topic_type_) 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_) return(0); + if (!topic_encoding_) 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_) return(0); + if(!topic_desc_) 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_GetServiceResponseTypeName(const char* service_name_, const char* method_name_, void* resp_type_, int resp_type_len_) + { + if (!service_name_) return(0); + if (!method_name_) return(0); + if (!resp_type_) return(0); + std::string req_type, 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_) return(0); + if (!method_name_) return(0); + if (!req_desc_) return(0); + std::string req_desc, 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_) return(0); + if (!method_name_) return(0); + if (!resp_desc_) return(0); + std::string req_desc, 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; + } +} + +///////////////////////////////////////////////////////// +// Process +///////////////////////////////////////////////////////// +extern "C" +{ + ECALC_API void eCAL_Process_DumpConfig() + { + eCAL::Process::DumpConfig(); + } + + ECALC_API int eCAL_Process_GetHostName(void* name_, int name_len_) + { + std::string name = eCAL::Process::GetHostName(); + if(!name.empty()) + { + return(CopyBuffer(name_, name_len_, name)); + } + return(0); + } + + ECALC_API int eCAL_Process_GetUnitName(void* name_, int name_len_) + { + std::string name = eCAL::Process::GetUnitName(); + if(!name.empty()) + { + return(CopyBuffer(name_, name_len_, name)); + } + return(0); + } + + ECALC_API int eCAL_Process_GetTaskParameter(void* par_, int par_len_, const char* sep_) + { + std::string par = eCAL::Process::GetTaskParameter(sep_); + if(!par.empty()) + { + return(CopyBuffer(par_, par_len_, par)); + } + return(0); + } + + ECALC_API void eCAL_Process_SleepMS(long time_ms_) + { + eCAL::Process::SleepMS(time_ms_); + } + + ECALC_API int eCAL_Process_GetProcessID() + { + return(eCAL::Process::GetProcessID()); + } + + ECALC_API int eCAL_Process_GetProcessName(void* name_, int name_len_) + { + std::string name = eCAL::Process::GetProcessName(); + if(!name.empty()) + { + return(CopyBuffer(name_, name_len_, name)); + } + return(0); + } + + ECALC_API int eCAL_Process_GetProcessParameter(void* par_, int par_len_) + { + std::string par = eCAL::Process::GetProcessParameter(); + if(!par.empty()) + { + return(CopyBuffer(par_, par_len_, par)); + } + return(0); + } + + ECALC_API void eCAL_Process_SetState(enum eCAL_Process_eSeverity severity_, enum eCAL_Process_eSeverity_Level level_, const char* info_) + { + eCAL::Process::SetState(severity_, level_, info_); + } + + ECALC_API int eCAL_Process_StartProcess(const char* proc_name_, const char* proc_args_, const char* working_dir_, int create_console_, enum eCAL_Process_eStartMode process_mode_, int block_) + { + return(eCAL::Process::StartProcess(proc_name_, proc_args_, working_dir_, create_console_ != 0, process_mode_, block_ != 0)); + } + + ECALC_API int eCAL_Process_StopProcessName(const char* proc_name_) + { + return(eCAL::Process::StopProcess(proc_name_)); + } + + ECALC_API int eCAL_Process_StopProcessID(int proc_id_) + { + return(eCAL::Process::StopProcess(proc_id_)); + } +} + +///////////////////////////////////////////////////////// +// Logging +///////////////////////////////////////////////////////// +extern "C" +{ + ECALC_API void eCAL_Logging_SetLogLevel(enum eCAL_Logging_eLogLevel level_) + { + eCAL::Logging::SetLogLevel(level_); + } + + ECALC_API enum eCAL_Logging_eLogLevel eCAL_Logging_GetLogLevel() + { + return(eCAL::Logging::GetLogLevel()); + } + + ECALC_API void eCAL_Logging_Log(const char* const msg_) + { + eCAL::Logging::Log(msg_); + } +} + +///////////////////////////////////////////////////////// +// Publisher +///////////////////////////////////////////////////////// +#if ECAL_CORE_PUBLISHER +static std::recursive_mutex g_pub_callback_mtx; +static void g_pub_event_callback(const char* topic_name_, const struct eCAL::SPubEventCallbackData* data_, const PubEventCallbackCT callback_, void* par_) +{ + const std::lock_guard lock(g_pub_callback_mtx); + SPubEventCallbackDataC data; + data.type = data_->type; + data.time = data_->time; + data.clock = data_->clock; + data.tid = data_->tid.c_str(); + data.tname = data_->tdatatype.name.c_str(); + data.tencoding = data_->tdatatype.encoding.c_str(); + data.tdesc = data_->tdatatype.descriptor.c_str(); + callback_(topic_name_, &data, par_); +} + +extern "C" +{ + ECALC_API ECAL_HANDLE eCAL_Pub_New() + { + auto* pub = new eCAL::CPublisher; + return(pub); + } + + ECALC_API int eCAL_Pub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_encoding_, const char* topic_type_name_, const char* topic_desc_, int topic_desc_len_) + { + if (handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + struct eCAL::SDataTypeInformation topic_info = { topic_type_encoding_, topic_type_name_, std::string(topic_desc_, static_cast(topic_desc_len_)) }; + if (!pub->Create(topic_name_, topic_info)) return(0); + return(1); + } + + ECALC_API int eCAL_Pub_Destroy(ECAL_HANDLE handle_) + { + if (handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + delete pub; + return(1); + } + + ECALC_API int eCAL_Pub_SetAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_, const char* attr_value_, int attr_value_len_) + { + if (handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + if (pub->SetAttribute(std::string(attr_name_, static_cast(attr_name_len_)), std::string(attr_value_, static_cast(attr_value_len_)))) return(1); + return(0); + } + + ECALC_API int eCAL_Pub_ClearAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_) + { + if (handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + if (pub->ClearAttribute(std::string(attr_name_, static_cast(attr_name_len_)))) return(1); + return(0); + } + + ECALC_API int eCAL_Pub_ShareType(ECAL_HANDLE handle_, int state_) + { + if (handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + pub->ShareType(state_ != 0); + return(1); + } + + ECALC_API int eCAL_Pub_ShareDescription(ECAL_HANDLE handle_, int state_) + { + if (handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + pub->ShareDescription(state_ != 0); + return(1); + } + + ECALC_API int eCAL_Pub_SetID(ECAL_HANDLE handle_, long long id_) + { + if (handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + if (pub->SetID(id_)) return(1); + return(0); + } + + ECALC_API int eCAL_Pub_IsSubscribed(ECAL_HANDLE handle_) + { + if(handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + if(pub->IsSubscribed()) return(1); + return(0); + } + + ECALC_API int eCAL_Pub_Send(ECAL_HANDLE handle_, const void* const buf_, int buf_len_, long long time_) + { + if(handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + const size_t ret = pub->Send(buf_, static_cast(buf_len_), time_); + if(static_cast(ret) == buf_len_) + { + return(buf_len_); + } + return(0); + } + + ECALC_API int eCAL_Pub_AddEventCallback(ECAL_HANDLE handle_, eCAL_Publisher_Event type_, PubEventCallbackCT callback_, void* par_) + { + if (handle_ == NULL) return(0); + eCAL::CPublisher* pub = static_cast(handle_); + auto callback = std::bind(g_pub_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); + if (pub->AddEventCallback(type_, callback)) return(1); + return(0); + } + + ECALC_API int eCAL_Pub_RemEventCallback(ECAL_HANDLE handle_, eCAL_Publisher_Event type_) + { + if (handle_ == NULL) return(0); + eCAL::CPublisher* pub = static_cast(handle_); + if (pub->RemEventCallback(type_)) return(1); + return(0); + } + + ECALC_API int eCAL_Pub_Dump(ECAL_HANDLE handle_, void* buf_, int buf_len_) + { + if(handle_ == nullptr) return(0); + auto* pub = static_cast(handle_); + const std::string dump = pub->Dump(); + if(!dump.empty()) + { + return(CopyBuffer(buf_, buf_len_, dump)); + } + return(0); + } +} +#endif + +///////////////////////////////////////////////////////// +// Subscriber +///////////////////////////////////////////////////////// +#if ECAL_CORE_SUBSCRIBER +static std::recursive_mutex g_sub_callback_mtx; +static void g_sub_receive_callback(const char* topic_name_, const struct eCAL::SReceiveCallbackData* data_, const ReceiveCallbackCT callback_, void* par_) +{ + const std::lock_guard lock(g_sub_callback_mtx); + SReceiveCallbackDataC data{}; + data.buf = data_->buf; + data.size = data_->size; + data.id = data_->id; + data.time = data_->time; + data.clock = data_->clock; + callback_(topic_name_, &data, par_); +} + +static void g_sub_event_callback(const char* topic_name_, const struct eCAL::SSubEventCallbackData* data_, const SubEventCallbackCT callback_, void* par_) +{ + const std::lock_guard lock(g_sub_callback_mtx); + SSubEventCallbackDataC data; + data.type = data_->type; + data.time = data_->time; + data.clock = data_->clock; + data.tid = data_->tid.c_str(); + data.tname = data_->tdatatype.name.c_str(); + data.tencoding = data_->tdatatype.encoding.c_str(); + data.tdesc = data_->tdatatype.descriptor.c_str(); + callback_(topic_name_, &data, par_); +} + +extern "C" +{ + ECALC_API ECAL_HANDLE eCAL_Sub_New() + { + auto* sub = new eCAL::CSubscriber; + return(sub); + } + + ECALC_API int eCAL_Sub_Create(ECAL_HANDLE handle_, const char* topic_name_, const char* topic_type_encoding_, const char* topic_type_name_, const char* topic_desc_, int topic_desc_len_) + { + if (handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + struct eCAL::SDataTypeInformation topic_info = { topic_type_encoding_, topic_type_name_, std::string(topic_desc_, static_cast(topic_desc_len_)) }; + if (!sub->Create(topic_name_, topic_info)) return(0); + return(1); + } + + ECALC_API int eCAL_Sub_Destroy(ECAL_HANDLE handle_) + { + if(handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + delete sub; + return(1); + } + + ECALC_API int eCAL_Sub_SetID(ECAL_HANDLE handle_, const long long* id_array_, const int id_num_) + { + if (handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + std::set id_set; + if (id_array_ != nullptr) + { + for (size_t i = 0; i < static_cast(id_num_); ++i) + { + id_set.insert(id_array_[i]); + } + } + if (sub->SetID(id_set)) return(1); + return(1); + } + + ECALC_API int eCAL_Sub_SetAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_, const char* attr_value_, int attr_value_len_) + { + if (handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + if (sub->SetAttribute(std::string(attr_name_, static_cast(attr_name_len_)), std::string(attr_value_, static_cast(attr_value_len_)))) return(1); + return(0); + } + + ECALC_API int eCAL_Sub_ClearAttribute(ECAL_HANDLE handle_, const char* attr_name_, int attr_name_len_) + { + if (handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + if (sub->ClearAttribute(std::string(attr_name_, static_cast(attr_name_len_)))) return(1); + return(0); + } + + ECALC_API int eCAL_Sub_Receive(ECAL_HANDLE handle_, void* buf_, int buf_len_, long long* time_, int rcv_timeout_) + { + if (buf_len_ == ECAL_ALLOCATE_4ME) + { + return eCAL_Sub_Receive_Alloc(handle_, static_cast(buf_), time_, rcv_timeout_); //-V206 + } + else + { + return eCAL_Sub_Receive_ToBuffer(handle_, buf_, buf_len_, time_, rcv_timeout_); + } + } + + ECALC_API int eCAL_Sub_Receive_ToBuffer(ECAL_HANDLE handle_, void* buf_, int buf_len_, long long* time_, int rcv_timeout_) + { + if (handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + + std::string buf; + if (sub->ReceiveBuffer(buf, time_, rcv_timeout_)) + { + return(CopyBuffer(buf_, buf_len_, buf)); + } + return(0); + } + + ECALC_API int eCAL_Sub_Receive_Alloc(ECAL_HANDLE handle_, void** buf_, long long* time_, int rcv_timeout_) + { + if (handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + + std::string buf; + if (sub->ReceiveBuffer(buf, time_, rcv_timeout_)) + { + return(CopyBuffer(buf_, ECAL_ALLOCATE_4ME, buf)); + } + return(0); + } + + ECALC_API int eCAL_Sub_Receive_Buffer_Alloc(ECAL_HANDLE handle_, void** buf_, int* buf_len_, long long* time_, int rcv_timeout_) + { + if (handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + + std::string buf; + if (sub->ReceiveBuffer(buf, time_, rcv_timeout_)) + { + CopyBuffer(buf_, ECAL_ALLOCATE_4ME, buf); + if (buf_len_) *buf_len_ = static_cast(buf.size()); + return(1); + } + return(0); + } + + ECALC_API int eCAL_Sub_AddReceiveCallback(ECAL_HANDLE handle_, ReceiveCallbackCT callback_, void* par_) + { + if(handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + auto callback = std::bind(g_sub_receive_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); + if(sub->AddReceiveCallback(callback)) return(1); + return(0); + } + + ECALC_API int eCAL_Sub_RemReceiveCallback(ECAL_HANDLE handle_) + { + if(handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + if(sub->RemReceiveCallback()) return(1); + return(0); + } + + ECALC_API int eCAL_Sub_AddEventCallback(ECAL_HANDLE handle_, eCAL_Subscriber_Event type_, SubEventCallbackCT callback_, void* par_) + { + if (handle_ == NULL) return(0); + eCAL::CSubscriber* sub = static_cast(handle_); + auto callback = std::bind(g_sub_event_callback, std::placeholders::_1, std::placeholders::_2, callback_, par_); + if (sub->AddEventCallback(type_, callback)) return(1); + return(0); + } + + ECALC_API int eCAL_Sub_RemEventCallback(ECAL_HANDLE handle_, eCAL_Subscriber_Event type_) + { + if (handle_ == NULL) return(0); + eCAL::CSubscriber* sub = static_cast(handle_); + if (sub->RemEventCallback(type_)) return(1); + return(0); + } + + ECALC_API int eCAL_Sub_GetTypeName(ECAL_HANDLE handle_, void* buf_, int buf_len_) + { + if (handle_ == NULL) return(0); + eCAL::CSubscriber* sub = static_cast(handle_); + const eCAL::SDataTypeInformation datatype_info = sub->GetDataTypeInformation(); + int buffer_len = CopyBuffer(buf_, buf_len_, datatype_info.name); + if (buffer_len != static_cast(datatype_info.name.size())) + { + return(0); + } + else + { + return(buffer_len); + } + } + + ECALC_API int eCAL_Sub_GetEncoding(ECAL_HANDLE handle_, void* buf_, int buf_len_) + { + if (handle_ == NULL) return(0); + eCAL::CSubscriber* sub = static_cast(handle_); + const eCAL::SDataTypeInformation datatype_info = sub->GetDataTypeInformation(); + int buffer_len = CopyBuffer(buf_, buf_len_, datatype_info.encoding); + if (buffer_len != static_cast(datatype_info.encoding.size())) + { + return(0); + } + else + { + return(buffer_len); + } + } + + ECALC_API int eCAL_Sub_GetDescription(ECAL_HANDLE handle_, void* buf_, int buf_len_) + { + if (handle_ == NULL) return(0); + eCAL::CSubscriber* sub = static_cast(handle_); + const eCAL::SDataTypeInformation datatype_info = sub->GetDataTypeInformation(); + int buffer_len = CopyBuffer(buf_, buf_len_, datatype_info.descriptor); + if (buffer_len != static_cast(datatype_info.descriptor.size())) + { + return(0); + } + else + { + return(buffer_len); + } + } + + ECALC_API int eCAL_Sub_Dump(ECAL_HANDLE handle_, void* buf_, int buf_len_) + { + if(handle_ == nullptr) return(0); + auto* sub = static_cast(handle_); + const std::string dump = sub->Dump(); + if(!dump.empty()) + { + return(CopyBuffer(buf_, buf_len_, dump)); + } + return(0); + } +} +#endif + +///////////////////////////////////////////////////////// +// Time +///////////////////////////////////////////////////////// +ECALC_API int eCAL_Time_GetName(void* name_, int name_len_) +{ + const std::string name = eCAL::Time::GetName(); + if (!name.empty()) + { + return(CopyBuffer(name_, name_len_, name)); + } + return(0); +} + +ECALC_API long long eCAL_Time_GetMicroSeconds() +{ + return(eCAL::Time::GetMicroSeconds()); +} + +ECALC_API long long eCAL_Time_GetNanoSeconds() +{ + return(eCAL::Time::GetNanoSeconds()); +} + +ECALC_API int eCAL_Time_SetNanoSeconds(long long time_) +{ + return(eCAL::Time::SetNanoSeconds(time_)); +} + +ECALC_API int eCAL_Time_IsTimeSynchronized() +{ + return(eCAL::Time::IsSynchronized()); +} + +ECALC_API int eCAL_Time_IsTimeMaster() +{ + return(eCAL::Time::IsMaster()); +} + +ECALC_API void eCAL_Time_SleepForNanoseconds(long long duration_nsecs_) +{ + eCAL::Time::SleepForNanoseconds(duration_nsecs_); +} + +ECALC_API int eCAL_Time_GetStatus(int* error_, char** status_message_, const int max_len_) +{ + if (max_len_ == ECAL_ALLOCATE_4ME || max_len_ > 0) + { + std::string status_message; + eCAL::Time::GetStatus(*error_, &status_message); + + if (!status_message.empty()) + { + return CopyBuffer(status_message_, max_len_, status_message); + } + return 0; + } + else + { + eCAL::Time::GetStatus(*error_, nullptr); + return 0; + } +} diff --git a/ecal/core/src/io/ecal_named_mutex.cpp b/src/core/src/io/mtx/ecal_named_mutex.cpp similarity index 100% rename from ecal/core/src/io/ecal_named_mutex.cpp rename to src/core/src/io/mtx/ecal_named_mutex.cpp diff --git a/ecal/core/src/io/ecal_named_mutex.h b/src/core/src/io/mtx/ecal_named_mutex.h similarity index 100% rename from ecal/core/src/io/ecal_named_mutex.h rename to src/core/src/io/mtx/ecal_named_mutex.h diff --git a/ecal/core/src/io/ecal_named_mutex_base.h b/src/core/src/io/mtx/ecal_named_mutex_base.h similarity index 97% rename from ecal/core/src/io/ecal_named_mutex_base.h rename to src/core/src/io/mtx/ecal_named_mutex_base.h index b2bb25a..dfd186f 100644 --- a/ecal/core/src/io/ecal_named_mutex_base.h +++ b/src/core/src/io/mtx/ecal_named_mutex_base.h @@ -31,7 +31,7 @@ namespace eCAL class CNamedMutexImplBase { public: - virtual ~CNamedMutexImplBase(){} + virtual ~CNamedMutexImplBase()= default; virtual bool IsCreated() const = 0; virtual bool IsRecoverable() const = 0; diff --git a/ecal/core/src/io/linux/ecal_named_mutex_impl.cpp b/src/core/src/io/mtx/linux/ecal_named_mutex_impl.cpp similarity index 100% rename from ecal/core/src/io/linux/ecal_named_mutex_impl.cpp rename to src/core/src/io/mtx/linux/ecal_named_mutex_impl.cpp diff --git a/ecal/core/src/io/linux/ecal_named_mutex_impl.h b/src/core/src/io/mtx/linux/ecal_named_mutex_impl.h similarity index 97% rename from ecal/core/src/io/linux/ecal_named_mutex_impl.h rename to src/core/src/io/mtx/linux/ecal_named_mutex_impl.h index 6e111be..16fba6c 100644 --- a/ecal/core/src/io/linux/ecal_named_mutex_impl.h +++ b/src/core/src/io/mtx/linux/ecal_named_mutex_impl.h @@ -23,7 +23,7 @@ #pragma once -#include "../ecal_named_mutex_base.h" +#include "io/mtx/ecal_named_mutex_base.h" typedef struct named_mutex named_mutex_t; diff --git a/ecal/core/src/io/linux/ecal_named_mutex_robust_clocklock_impl.cpp b/src/core/src/io/mtx/linux/ecal_named_mutex_robust_clocklock_impl.cpp similarity index 100% rename from ecal/core/src/io/linux/ecal_named_mutex_robust_clocklock_impl.cpp rename to src/core/src/io/mtx/linux/ecal_named_mutex_robust_clocklock_impl.cpp diff --git a/ecal/core/src/io/linux/ecal_named_mutex_robust_clocklock_impl.h b/src/core/src/io/mtx/linux/ecal_named_mutex_robust_clocklock_impl.h similarity index 97% rename from ecal/core/src/io/linux/ecal_named_mutex_robust_clocklock_impl.h rename to src/core/src/io/mtx/linux/ecal_named_mutex_robust_clocklock_impl.h index 4532943..81e6769 100644 --- a/ecal/core/src/io/linux/ecal_named_mutex_robust_clocklock_impl.h +++ b/src/core/src/io/mtx/linux/ecal_named_mutex_robust_clocklock_impl.h @@ -23,7 +23,7 @@ #pragma once -#include "../ecal_named_mutex_base.h" +#include "io/mtx/ecal_named_mutex_base.h" typedef struct named_mutex named_mutex_t; diff --git a/ecal/core/src/io/win32/ecal_named_mutex_impl.cpp b/src/core/src/io/mtx/win32/ecal_named_mutex_impl.cpp similarity index 96% rename from ecal/core/src/io/win32/ecal_named_mutex_impl.cpp rename to src/core/src/io/mtx/win32/ecal_named_mutex_impl.cpp index faa7e42..f653ae3 100644 --- a/ecal/core/src/io/win32/ecal_named_mutex_impl.cpp +++ b/src/core/src/io/mtx/win32/ecal_named_mutex_impl.cpp @@ -81,7 +81,7 @@ namespace eCAL m_was_recovered = false; // wait for access - DWORD result = WaitForSingleObject(m_mutex_handle, static_cast(timeout_)); + const DWORD result = WaitForSingleObject(m_mutex_handle, static_cast(timeout_)); if (result == WAIT_OBJECT_0) return true; else if (result == WAIT_ABANDONED) diff --git a/ecal/core/src/io/win32/ecal_named_mutex_impl.h b/src/core/src/io/mtx/win32/ecal_named_mutex_impl.h similarity index 97% rename from ecal/core/src/io/win32/ecal_named_mutex_impl.h rename to src/core/src/io/mtx/win32/ecal_named_mutex_impl.h index 56459ae..625ab6b 100644 --- a/ecal/core/src/io/win32/ecal_named_mutex_impl.h +++ b/src/core/src/io/mtx/win32/ecal_named_mutex_impl.h @@ -23,7 +23,7 @@ #pragma once -#include "../ecal_named_mutex_base.h" +#include "io/mtx/ecal_named_mutex_base.h" namespace eCAL { diff --git a/ecal/core/src/io/ecal_memfile.cpp b/src/core/src/io/shm/ecal_memfile.cpp similarity index 79% rename from ecal/core/src/io/ecal_memfile.cpp rename to src/core/src/io/shm/ecal_memfile.cpp index b520239..6c67184 100644 --- a/ecal/core/src/io/ecal_memfile.cpp +++ b/src/core/src/io/shm/ecal_memfile.cpp @@ -42,8 +42,9 @@ namespace eCAL CMemoryFile::CMemoryFile() : m_created(false), - m_access_state(access_state::closed), - m_name("") + m_auto_sanitizing(false), + m_payload_initialized(false), + m_access_state(access_state::closed) { } @@ -72,8 +73,9 @@ namespace eCAL Destroy(create_); // reset states - m_created = false; - m_access_state = access_state::closed; + m_created = false; + m_payload_initialized = false; + m_access_state = access_state::closed; m_name.clear(); // reset header and info @@ -85,7 +87,7 @@ namespace eCAL if (!memfile::db::AddFile(name_, create_, create_ ? len_ + m_header.int_hdr_size : SIZEOF_PARTIAL_STRUCT(SInternalHeader, int_hdr_size), m_memfile_info)) { #ifndef NDEBUG - printf("Could not create memory file: %s.\n\n", name_); + printf("Could not create memory file: %s.\n", name_); #endif return(false); } @@ -96,7 +98,7 @@ namespace eCAL if(!m_memfile_mutex.Create(name_, m_auto_sanitizing)) { #ifndef NDEBUG - printf("Could not create memory file mutex: %s.\n\n", name_); + printf("Could not create memory file mutex: %s.\n", name_); #endif return(false); } @@ -110,7 +112,7 @@ namespace eCAL // for performance reasons only apply consistency check if it is explicitly set if(m_memfile_mutex.Lock(PUB_MEMFILE_CREATE_TO)) { - if (m_memfile_info.mem_address) + if (m_memfile_info.mem_address != nullptr) { SInternalHeader* header = reinterpret_cast(m_memfile_info.mem_address); @@ -171,8 +173,9 @@ namespace eCAL m_memfile_mutex.Destroy(); // reset states - m_created = false; - m_access_state = access_state::closed; + m_created = false; + m_payload_initialized = false; + m_access_state = access_state::closed; m_name.clear(); // reset header and info @@ -217,7 +220,7 @@ namespace eCAL if (m_access_state != access_state::read_access) return(0); if (len_ == 0) return(0); if (len_ > static_cast(m_header.cur_data_size)) return(0); - if (!m_memfile_info.mem_address) return(0); + if (m_memfile_info.mem_address == nullptr) return(0); // return read address buf_ = static_cast(m_memfile_info.mem_address) + m_header.int_hdr_size; @@ -227,10 +230,10 @@ namespace eCAL size_t CMemoryFile::Read(void* buf_, const size_t len_, const size_t offset_) { - if (!buf_) return(0); + if (buf_ == nullptr) return(0); const void* rbuf(nullptr); - if (GetReadAddress(rbuf, len_ + offset_)) + if (GetReadAddress(rbuf, len_ + offset_) != 0u) { // copy from read buffer with offset memcpy(buf_, static_cast(rbuf) + offset_, len_); @@ -278,7 +281,7 @@ namespace eCAL if (m_access_state != access_state::write_access) return(0); if (len_ == 0) return(0); if (len_ > static_cast(m_header.max_data_size)) return(0); - if (!m_memfile_info.mem_address) return(0); + if (m_memfile_info.mem_address == nullptr) return(0); // update m_header and write into memory file header m_header.cur_data_size = (unsigned long)(len_); @@ -291,13 +294,13 @@ namespace eCAL return(len_); } - size_t CMemoryFile::Write(const void* buf_, const size_t len_, const size_t offset_) + size_t CMemoryFile::WriteBuffer(const void* buf_, const size_t len_, const size_t offset_) { - if (!m_created) return(0); - if (!buf_) return(0); + if (!m_created) return(0); + if (buf_ == nullptr) return(0); void* wbuf(nullptr); - if (GetWriteAddress(wbuf, len_ + offset_)) + if (GetWriteAddress(wbuf, len_ + offset_) != 0u) { // copy to write buffer memcpy(static_cast(wbuf) + offset_, buf_, len_); @@ -311,10 +314,49 @@ namespace eCAL } } + size_t CMemoryFile::WritePayload(CPayloadWriter& payload_, const size_t len_, const size_t offset_, bool force_full_write_ /*= false*/) + { + if (!m_created) return(0); + + void* wbuf(nullptr); + if (GetWriteAddress(wbuf, len_ + offset_) != 0u) + { + // (re)write complete buffer + if (!m_payload_initialized || force_full_write_) + { + bool const success = payload_.WriteFull(static_cast(wbuf) + offset_, len_); + if (!success) + { + printf("Could not write payload content to the memory file (CPayload::WriteFull returned false): %s.\n\n", m_name.c_str()); + } + else + { + m_payload_initialized = true; + } + } + else + { + // apply update to write buffer + bool const success = payload_.WriteModified(static_cast(wbuf) + offset_, len_); + if (!success) + { + printf("Could not write payload content to the memory file (CPayload::WriteModified returned false): %s.\n\n", m_name.c_str()); + } + } + + // return number of written bytes + return(len_); + } + else + { + return(0); + } + } + bool CMemoryFile::GetAccess(int timeout_) { - if (!m_created) return(false); - if (!m_memfile_info.mem_address) return(false); + if (!m_created) return(false); + if (m_memfile_info.mem_address == nullptr) return(false); // lock mutex if(!m_memfile_mutex.Lock(timeout_)) @@ -336,13 +378,13 @@ namespace eCAL memcpy(&m_header, m_memfile_info.mem_address, std::min(sizeof(SInternalHeader), static_cast(m_header.int_hdr_size))); // check size again - size_t len = static_cast(m_header.int_hdr_size) + static_cast(m_header.max_data_size); + size_t const len = static_cast(m_header.int_hdr_size) + static_cast(m_header.max_data_size); if (len > m_memfile_info.size) { // check file size and update memory file map memfile::db::CheckFileSize(m_name, len, m_memfile_info); - // check size again and give up if it is still to small + // check size again and give up if it is still too small if (len > m_memfile_info.size) { // unlock mutex diff --git a/ecal/core/src/io/ecal_memfile.h b/src/core/src/io/shm/ecal_memfile.h similarity index 91% rename from ecal/core/src/io/ecal_memfile.h rename to src/core/src/io/shm/ecal_memfile.h index c83dc5e..4ac38b8 100644 --- a/ecal/core/src/io/ecal_memfile.h +++ b/src/core/src/io/shm/ecal_memfile.h @@ -27,8 +27,10 @@ #include #include +#include + #include "ecal_memfile_info.h" -#include "ecal_named_mutex.h" +#include "io/mtx/ecal_named_mutex.h" namespace eCAL { @@ -140,7 +142,19 @@ namespace eCAL * * @return Number of bytes copied to the memory file. **/ - size_t Write(const void* buf_, const size_t len_, const size_t offset_); + size_t WriteBuffer(const void* buf_, size_t len_, size_t offset_); + + /** + * @brief Apply payload on the memory file. + * + * @param payload_ The payload. + * @param len_ The number of bytes to write. + * @param offset_ The offset for writing the data. + * @param force_full_write_ Force full write action. + * + * @return Number of bytes accessed (len if succeeded otherwise zero). + **/ + size_t WritePayload(CPayloadWriter& payload_, size_t len_, size_t offset_, bool force_full_write_ = false); /** * @brief Maximum data size of the whole memory file. @@ -201,6 +215,7 @@ namespace eCAL }; bool m_created; bool m_auto_sanitizing; + bool m_payload_initialized; access_state m_access_state; std::string m_name; SInternalHeader m_header; @@ -211,4 +226,4 @@ namespace eCAL CMemoryFile(const CMemoryFile&); // prevent copy-construction CMemoryFile& operator=(const CMemoryFile&); // prevent assignment }; -}; +} diff --git a/ecal/core/src/io/ecal_memfile_db.cpp b/src/core/src/io/shm/ecal_memfile_db.cpp similarity index 88% rename from ecal/core/src/io/ecal_memfile_db.cpp rename to src/core/src/io/shm/ecal_memfile_db.cpp index 48bfb50..36e1a94 100644 --- a/ecal/core/src/io/ecal_memfile_db.cpp +++ b/src/core/src/io/shm/ecal_memfile_db.cpp @@ -37,7 +37,7 @@ namespace eCAL void CMemFileMap::Destroy() { // lock memory map access - std::lock_guard lock(m_memfile_map_mtx); + const std::lock_guard lock(m_memfile_map_mtx); // erase memory files from memory map for (MemFileMapT::iterator iter = m_memfile_map.begin(); iter != m_memfile_map.end(); ++iter) @@ -64,10 +64,10 @@ namespace eCAL assert(len_ > 0); // lock memory map access - std::lock_guard lock(m_memfile_map_mtx); + const std::lock_guard lock(m_memfile_map_mtx); // check for existing memory file - MemFileMapT::iterator iter = m_memfile_map.find(name_); + const MemFileMapT::iterator iter = m_memfile_map.find(name_); if (iter == m_memfile_map.end()) { // create memory file @@ -108,11 +108,11 @@ namespace eCAL bool CMemFileMap::RemoveFile(const std::string& name_, const bool remove_) { // lock memory map access - std::lock_guard lock(m_memfile_map_mtx); + const std::lock_guard lock(m_memfile_map_mtx); // erase memory file from memory map auto& memfile_map = m_memfile_map; - MemFileMapT::iterator iter = memfile_map.find(name_); + const MemFileMapT::iterator iter = memfile_map.find(name_); if (iter != memfile_map.end()) { auto& memfile_info = iter->second; @@ -123,7 +123,7 @@ namespace eCAL memfile_info.remove |= remove_; if (memfile_info.refcnt < 1) { - bool remove_from_system = memfile_info.remove; + const bool remove_from_system = memfile_info.remove; // unmap memory file memfile::os::UnMapFile(memfile_info); @@ -150,7 +150,7 @@ namespace eCAL memfile::os::CheckFileSize(len_, false, mem_file_info_); // lock memory map access - std::lock_guard lock(m_memfile_map_mtx); + const std::lock_guard lock(m_memfile_map_mtx); // update/set info m_memfile_map[name_] = mem_file_info_; @@ -164,19 +164,19 @@ namespace eCAL { bool AddFile(const std::string& name_, const bool create_, const size_t len_, SMemFileInfo& mem_file_info_) { - if (!g_memfile_map()) return false; + if (g_memfile_map() == nullptr) return false; return g_memfile_map()->AddFile(name_, create_, len_, mem_file_info_); } bool RemoveFile(const std::string& name_, const bool remove_) { - if (!g_memfile_map()) return false; + if (g_memfile_map() == nullptr) return false; return g_memfile_map()->RemoveFile(name_, remove_); } bool CheckFileSize(const std::string& name_, const size_t len_, SMemFileInfo& mem_file_info_) { - if (!g_memfile_map()) return false; + if (g_memfile_map() == nullptr) return false; return g_memfile_map()->CheckFileSize(name_, len_, mem_file_info_); } } diff --git a/ecal/core/src/io/ecal_memfile_db.h b/src/core/src/io/shm/ecal_memfile_db.h similarity index 96% rename from ecal/core/src/io/ecal_memfile_db.h rename to src/core/src/io/shm/ecal_memfile_db.h index a716bd5..a60224b 100644 --- a/ecal/core/src/io/ecal_memfile_db.h +++ b/src/core/src/io/shm/ecal_memfile_db.h @@ -44,7 +44,7 @@ namespace eCAL bool CheckFileSize(const std::string& name_, const size_t len_, SMemFileInfo& mem_file_info_); protected: - typedef std::unordered_map MemFileMapT; + using MemFileMapT = std::unordered_map; std::mutex m_memfile_map_mtx; MemFileMapT m_memfile_map; }; diff --git a/ecal/core/src/io/ecal_memfile_header.h b/src/core/src/io/shm/ecal_memfile_header.h similarity index 98% rename from ecal/core/src/io/ecal_memfile_header.h rename to src/core/src/io/shm/ecal_memfile_header.h index 96e76e4..a016179 100644 --- a/ecal/core/src/io/ecal_memfile_header.h +++ b/src/core/src/io/shm/ecal_memfile_header.h @@ -23,7 +23,7 @@ #pragma once -#include +#include namespace eCAL { diff --git a/ecal/core/src/io/ecal_memfile_info.h b/src/core/src/io/shm/ecal_memfile_info.h similarity index 72% rename from ecal/core/src/io/ecal_memfile_info.h rename to src/core/src/io/shm/ecal_memfile_info.h index 1434400..dc37723 100644 --- a/ecal/core/src/io/ecal_memfile_info.h +++ b/src/core/src/io/shm/ecal_memfile_info.h @@ -32,8 +32,8 @@ #include "ecal_win_main.h" -typedef HANDLE MemFileT; -typedef HANDLE MapRegionT; +using MemFileT = HANDLE; +using MapRegionT = HANDLE; #endif /* ECAL_OS_WINDOWS */ @@ -48,23 +48,13 @@ namespace eCAL { struct SMemFileInfo { - SMemFileInfo() - { - refcnt = 0; - remove = false; - memfile = 0; - map_region = 0; - mem_address = 0; - size = 0; - exists = false; - } - int refcnt; - bool remove; - MemFileT memfile; - MapRegionT map_region; - void* mem_address; + int refcnt = 0; + bool remove = false; + MemFileT memfile = 0; + MapRegionT map_region = 0; + void* mem_address = 0; std::string name; - size_t size; - bool exists; + size_t size = 0; + bool exists = false; }; } diff --git a/ecal/core/src/io/ecal_memfile_naming.cpp b/src/core/src/io/shm/ecal_memfile_naming.cpp similarity index 96% rename from ecal/core/src/io/ecal_memfile_naming.cpp rename to src/core/src/io/shm/ecal_memfile_naming.cpp index a838717..011d5dd 100644 --- a/ecal/core/src/io/ecal_memfile_naming.cpp +++ b/src/core/src/io/shm/ecal_memfile_naming.cpp @@ -17,7 +17,7 @@ * ========================= eCAL LICENSE ================================= */ -#include +#include "io/shm/ecal_memfile_naming.h" #include #include diff --git a/ecal/core/src/io/ecal_memfile_naming.h b/src/core/src/io/shm/ecal_memfile_naming.h similarity index 100% rename from ecal/core/src/io/ecal_memfile_naming.h rename to src/core/src/io/shm/ecal_memfile_naming.h diff --git a/ecal/core/src/io/ecal_memfile_os.h b/src/core/src/io/shm/ecal_memfile_os.h similarity index 100% rename from ecal/core/src/io/ecal_memfile_os.h rename to src/core/src/io/shm/ecal_memfile_os.h diff --git a/ecal/core/src/io/ecal_memfile_pool.cpp b/src/core/src/io/shm/ecal_memfile_pool.cpp similarity index 75% rename from ecal/core/src/io/ecal_memfile_pool.cpp rename to src/core/src/io/shm/ecal_memfile_pool.cpp index e66aefa..77c685a 100644 --- a/ecal/core/src/io/ecal_memfile_pool.cpp +++ b/src/core/src/io/shm/ecal_memfile_pool.cpp @@ -23,6 +23,7 @@ **/ #include "ecal_def.h" +#include "ecal_event.h" #include "ecal_memfile_pool.h" #include @@ -36,7 +37,7 @@ namespace eCAL m_created(false), m_do_stop(false), m_is_observing(false), - m_timeout_read(0) + m_time_of_last_life_signal(std::chrono::steady_clock::now()) { } @@ -54,8 +55,8 @@ namespace eCAL if (m_created) return false; // open memory file events - gOpenEvent(&m_event_snd, memfile_event_); - gOpenEvent(&m_event_ack, memfile_event_ + "_ack"); + gOpenNamedEvent(&m_event_snd, memfile_event_, false); + gOpenNamedEvent(&m_event_ack, memfile_event_ + "_ack", false); // create memory file access m_memfile.Create(memfile_name_.c_str(), false); @@ -137,7 +138,7 @@ namespace eCAL { if (!m_is_observing) return false; - m_timeout_read = 0; + m_time_of_last_life_signal = std::chrono::steady_clock::now(); return true; } @@ -147,14 +148,31 @@ namespace eCAL // internal clock sample update checking uint64_t last_sample_clock(0); + // buffer to store memory file content + std::vector receive_buffer; + + // Boolean that tells whether the SHM file has new data that we have NOT already accessed + bool has_unprocessed_data = false; + // runs as long as there is no timeout and no external stop request - while((m_timeout_read < timeout_) && !m_do_stop) + while(std::chrono::steady_clock::now() - std::chrono::steady_clock::time_point(m_time_of_last_life_signal) < std::chrono::milliseconds(timeout_) + && !m_do_stop) { - // loop start in ms - auto loop_start = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + if (!has_unprocessed_data) + { + // Only wait for the new-data-event, if we haven't processed the data, yet + // check for memory file update event from shm writer (20 ms) + has_unprocessed_data = gWaitForEvent(m_event_snd, 20); - // check for memory file update event from shm writer (20 ms) - if(gWaitForEvent(m_event_snd, 20)) + if (has_unprocessed_data) + { + // We got a signal from the publisher! It is alive! So we reset the time since the last live signal + m_time_of_last_life_signal = std::chrono::steady_clock::now(); + } + } + + // If we have unprocessed data, we try to access (and process!) it + if(has_unprocessed_data) { // last chance to stop .. if(m_do_stop) break; @@ -162,6 +180,9 @@ namespace eCAL // try to open memory file (timeout 5 ms) if(m_memfile.GetReadAccess(5)) { + // We have gotten access! Now the data qualifies as processed, so next loop we will wait for the signal for new data, again. + has_unprocessed_data = false; + // read the file header SMemFileHeader mfile_hdr; ReadFileHeader(mfile_hdr); @@ -174,10 +195,7 @@ namespace eCAL } else { - // clear receive buffer - m_ecal_buffer.clear(); - - bool zero_copy_allowed = mfile_hdr.options.zero_copy != 0; + const bool zero_copy_allowed = mfile_hdr.options.zero_copy != 0; bool post_process_buffer(false); // ------------------------------------------------------------------------- // zero copy mode @@ -206,19 +224,18 @@ namespace eCAL // and close the file immediately else { + // need to resize the buffer especially if data_size = 0, otherwise it might contain stale data. + receive_buffer.resize((size_t)mfile_hdr.data_size); + // read payload // if data length == 0, there is no need to further read data // we just flag to process the empty buffer - if (mfile_hdr.data_size == 0) - { - post_process_buffer = true; - } - else + if (mfile_hdr.data_size != 0) { - m_ecal_buffer.resize((size_t)mfile_hdr.data_size); - m_memfile.Read(m_ecal_buffer.data(), (size_t)mfile_hdr.data_size, mfile_hdr.hdr_size); - post_process_buffer = true; + m_memfile.Read(receive_buffer.data(), (size_t)mfile_hdr.data_size, mfile_hdr.hdr_size); } + + post_process_buffer = true; } // store clock @@ -231,7 +248,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_, m_ecal_buffer.data(), m_ecal_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(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); } // send acknowledge event @@ -241,14 +258,6 @@ namespace eCAL } } } - - // reset timeout - m_timeout_read = 0; - } - else - { - // increase timeout in ms - m_timeout_read += std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() - loop_start; } } @@ -271,16 +280,16 @@ namespace eCAL bool CMemFileObserver::ReadFileHeader(SMemFileHeader& mfile_hdr_) { // retrieve size of received buffer - size_t buffer_size = m_memfile.CurDataSize(); + const size_t buffer_size = m_memfile.CurDataSize(); // do we have at least the first two bytes ? (hdr_size) if (buffer_size >= 2) { // read received header's size m_memfile.Read(&mfile_hdr_, 2, 0); - uint16_t rcv_hdr_size = mfile_hdr_.hdr_size; + const uint16_t rcv_hdr_size = mfile_hdr_.hdr_size; // if the header size exceeds current header version size -> limit it to that one - uint16_t hdr_bytes2copy = std::min(rcv_hdr_size, static_cast(sizeof(SMemFileHeader))); + const uint16_t hdr_bytes2copy = std::min(rcv_hdr_size, static_cast(sizeof(SMemFileHeader))); if (hdr_bytes2copy <= buffer_size) { // now read all we can get from the received header @@ -295,15 +304,24 @@ namespace eCAL // CMemFileThreadPool //////////////////////////////////////// CMemFileThreadPool::CMemFileThreadPool() : - m_created(false) + m_created(false), + m_do_cleanup(false) { } - CMemFileThreadPool::~CMemFileThreadPool() = default; + CMemFileThreadPool::~CMemFileThreadPool() + { + Destroy(); + } void CMemFileThreadPool::Create() { if(m_created) return; + + // start cleanup thread + m_do_cleanup = true; + m_cleanup_thread = std::thread(&CMemFileThreadPool::CleanupPoolThread, this); + m_created = true; } @@ -311,8 +329,16 @@ namespace eCAL { if(!m_created) return; + // stop cleanup thread + { + const std::lock_guard lock(m_do_cleanup_mtx); + m_do_cleanup = false; + m_do_cleanup_cv.notify_one(); + } + if (m_cleanup_thread.joinable()) m_cleanup_thread.join(); + // lock pool - std::lock_guard lock(m_observer_pool_sync); + const std::lock_guard lock(m_observer_pool_sync); // stop all running observers for (auto & observer : m_observer_pool) observer.second->Stop(); @@ -328,11 +354,8 @@ namespace eCAL if(!m_created) return(false); if(memfile_name_.empty()) return(false); - // remove outdated observers - //CleanupPool(); - // lock pool - std::lock_guard lock(m_observer_pool_sync); + const std::lock_guard lock(m_observer_pool_sync); // if the observer is existing reset its timeout // this should avoid that an observer will timeout in the case that @@ -369,10 +392,29 @@ namespace eCAL } } + void CMemFileThreadPool::CleanupPoolThread() + { + for (;;) + { + { + // cycling with 1 second timeout + std::unique_lock lock(m_do_cleanup_mtx); + m_do_cleanup_cv.wait_for(lock, std::chrono::milliseconds(1000), [&]() -> bool {return !m_do_cleanup; }); + if (!m_do_cleanup) + { + // cleanup thread stopped + return; + } + } + // do your job + CleanupPool(); + } + } + void CMemFileThreadPool::CleanupPool() { // lock pool - std::lock_guard lock(m_observer_pool_sync); + const std::lock_guard lock(m_observer_pool_sync); // remove outdated / finished observer from the thread pool for(auto observer = m_observer_pool.begin(); observer != m_observer_pool.end();) diff --git a/ecal/core/src/io/ecal_memfile_pool.h b/src/core/src/io/shm/ecal_memfile_pool.h similarity index 76% rename from ecal/core/src/io/ecal_memfile_pool.h rename to src/core/src/io/shm/ecal_memfile_pool.h index d692ed4..0c45884 100644 --- a/ecal/core/src/io/ecal_memfile_pool.h +++ b/src/core/src/io/shm/ecal_memfile_pool.h @@ -23,13 +23,14 @@ #pragma once -#include #include +#include "ecal_event.h" #include "ecal_memfile.h" #include "ecal_memfile_header.h" #include +#include #include #include #include @@ -38,7 +39,7 @@ namespace eCAL { - typedef std::function MemFileDataCallbackT; + using MemFileDataCallbackT = std::function; //////////////////////////////////////// // CMemFileObserver @@ -49,6 +50,11 @@ namespace eCAL CMemFileObserver(); ~CMemFileObserver(); + CMemFileObserver(const CMemFileObserver&) = delete; + CMemFileObserver& operator=(const CMemFileObserver&) = delete; + CMemFileObserver(CMemFileObserver&& rhs) = delete; + CMemFileObserver& operator=(CMemFileObserver&& rhs) = delete; + bool Create(const std::string& memfile_name_, const std::string& memfile_event_); bool Destroy(); @@ -66,7 +72,7 @@ namespace eCAL std::atomic m_do_stop; std::atomic m_is_observing; - std::atomic m_timeout_read; + std::atomic m_time_of_last_life_signal; MemFileDataCallbackT m_data_callback; @@ -74,7 +80,6 @@ namespace eCAL EventHandleT m_event_snd; EventHandleT m_event_ack; CMemoryFile m_memfile; - std::vector m_ecal_buffer; }; //////////////////////////////////////// @@ -92,10 +97,16 @@ namespace eCAL 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_); protected: + void CleanupPoolThread(); void CleanupPool(); std::atomic m_created; std::mutex m_observer_pool_sync; std::map> m_observer_pool; + + std::atomic m_do_cleanup; + std::condition_variable m_do_cleanup_cv; + std::mutex m_do_cleanup_mtx; + std::thread m_cleanup_thread; }; } diff --git a/ecal/core/src/io/ecal_memfile_sync.cpp b/src/core/src/io/shm/ecal_memfile_sync.cpp similarity index 79% rename from ecal/core/src/io/ecal_memfile_sync.cpp rename to src/core/src/io/shm/ecal_memfile_sync.cpp index 53c63ac..8978110 100644 --- a/ecal/core/src/io/ecal_memfile_sync.cpp +++ b/src/core/src/io/shm/ecal_memfile_sync.cpp @@ -21,14 +21,13 @@ * @brief synchronized memory file interface **/ -#include #include +#include "ecal_event.h" #include "ecal_memfile_header.h" #include "ecal_memfile_naming.h" #include "ecal_memfile_sync.h" -#include #include #include @@ -50,24 +49,24 @@ namespace eCAL { if (!m_created) return false; - // a local subscriber is registering with it's process id + // a local subscriber is registering with its process id // we have to open the send update event and the acknowledge event // we have ONE memory file per publisher and 1 or 2 events per memory file // the event names - std::string event_snd_name = m_memfile_name + "_" + process_id_; - std::string event_ack_name = m_memfile_name + "_" + process_id_ + "_ack"; + const std::string event_snd_name = m_memfile_name + "_" + process_id_; + const std::string event_ack_name = m_memfile_name + "_" + process_id_ + "_ack"; // check for existing process - std::lock_guard lock(m_event_handle_map_sync); - EventHandleMapT::iterator iter = m_event_handle_map.find(process_id_); + const std::lock_guard lock(m_event_handle_map_sync); + const EventHandleMapT::iterator iter = m_event_handle_map.find(process_id_); // add a new process id and create the sync and acknowledge event if (iter == m_event_handle_map.end()) { SEventHandlePair event_pair; - gOpenEvent(&event_pair.event_snd, event_snd_name); - gOpenEvent(&event_pair.event_ack, event_ack_name); + gOpenNamedEvent(&event_pair.event_snd, event_snd_name, true); + gOpenNamedEvent(&event_pair.event_ack, event_ack_name, true); m_event_handle_map.insert(std::pair(process_id_, event_pair)); return true; } @@ -78,8 +77,12 @@ namespace eCAL // event was deactivated by a sync timeout in SendSyncEvents if (!gEventIsValid(iter->second.event_ack)) { - gOpenEvent(&iter->second.event_ack, event_ack_name); + gOpenNamedEvent(&iter->second.event_ack, event_ack_name, true); } + + // Set the ack event to valid again, so we will wait for the subscriber + iter->second.event_ack_is_invalid = false; + return true; } } @@ -92,11 +95,11 @@ namespace eCAL // we close the associated sync events and // remove them from the event handle map - std::lock_guard lock(m_event_handle_map_sync); - EventHandleMapT::const_iterator iter = m_event_handle_map.find(process_id_); + const std::lock_guard lock(m_event_handle_map_sync); + const EventHandleMapT::const_iterator iter = m_event_handle_map.find(process_id_); if (iter != m_event_handle_map.end()) { - SEventHandlePair event_pair = iter->second; + const SEventHandlePair event_pair = iter->second; gCloseEvent(event_pair.event_snd); gCloseEvent(event_pair.event_ack); m_event_handle_map.erase(iter); @@ -110,15 +113,15 @@ namespace eCAL { if (!m_created) return false; - // we recreate a memory file if the file size is to small - bool file_to_small = m_memfile.MaxDataSize() < (sizeof(SMemFileHeader) + size_); + // we recreate a memory file if the file size is too small + const bool file_to_small = m_memfile.MaxDataSize() < (sizeof(SMemFileHeader) + size_); if (file_to_small) { #ifndef NDEBUG Logging::Log(log_level_debug4, m_base_name + "::CSyncMemoryFile::CheckSize - RECREATE"); #endif // estimate size of memory file - size_t memfile_size = sizeof(SMemFileHeader) + size_ + static_cast((static_cast(m_attr.reserve) / 100.0f) * static_cast(size_)); + const size_t memfile_size = sizeof(SMemFileHeader) + size_ + static_cast((static_cast(m_attr.reserve) / 100.0f) * static_cast(size_)); // recreate the file if (!Recreate(memfile_size)) return false; @@ -130,7 +133,7 @@ namespace eCAL return false; } - bool CSyncMemoryFile::Write(const SWriterData& data_) + bool CSyncMemoryFile::Write(CPayloadWriter& payload_, const SWriterAttr& data_, bool force_full_write_/* = false*/) { if (!m_created) { @@ -162,7 +165,7 @@ namespace eCAL // set zero copy memfile_hdr.options.zero_copy = static_cast(data_.zero_copy); // set acknowledge timeout - memfile_hdr.ack_timout_ms = static_cast(data_.acknowledge_timeout_ms); + memfile_hdr.ack_timout_ms = static_cast(data_.acknowledge_timeout_ms); // acquire write access bool write_access = m_memfile.GetWriteAccess(static_cast(m_attr.timeout_open_ms)); @@ -193,12 +196,12 @@ namespace eCAL size_t wbytes(0); // write the user file header - written &= m_memfile.Write(&memfile_hdr, memfile_hdr.hdr_size, wbytes) > 0; + written &= m_memfile.WriteBuffer(&memfile_hdr, memfile_hdr.hdr_size, wbytes) > 0; wbytes += memfile_hdr.hdr_size; // write the buffer if (data_.len > 0) { - written &= m_memfile.Write(data_.buf, data_.len, wbytes) > 0; + written &= m_memfile.WritePayload(payload_, data_.len, wbytes, force_full_write_) > 0; } // release write access m_memfile.ReleaseWriteAccess(); @@ -226,6 +229,11 @@ namespace eCAL return m_memfile_name; } + size_t CSyncMemoryFile::GetSize() const + { + return m_attr.min_size; + } + bool CSyncMemoryFile::Create(const std::string& base_name_, size_t size_) { if (m_created) return false; @@ -243,18 +251,18 @@ namespace eCAL // create the memory file if (!m_memfile.Create(m_memfile_name.c_str(), true, memfile_size)) { - Logging::Log(log_level_error, std::string(m_base_name + "::CSyncMemoryFile::Create - FAILED : ") + m_memfile_name); + Logging::Log(log_level_error, std::string("CSyncMemoryFile::Create FAILED : ") + m_memfile_name); return false; } #ifndef NDEBUG - Logging::Log(log_level_debug2, std::string(m_base_name + "::CSyncMemoryFile::Create - SUCCESS : ") + m_memfile_name); + Logging::Log(log_level_debug2, std::string("CSyncMemoryFile::Create SUCCESS : ") + m_memfile_name); #endif // initialize memory file with empty header struct SMemFileHeader memfile_hdr; m_memfile.GetWriteAccess(static_cast(m_attr.timeout_open_ms)); - m_memfile.Write(&memfile_hdr, memfile_hdr.hdr_size, 0); + m_memfile.WriteBuffer(&memfile_hdr, memfile_hdr.hdr_size, 0); m_memfile.ReleaseWriteAccess(); // it's created @@ -296,7 +304,7 @@ namespace eCAL // collect id's of the currently connected processes std::vector process_id_list; { - std::lock_guard lock(m_event_handle_map_sync); + const std::lock_guard lock(m_event_handle_map_sync); for (const auto& event_handle : m_event_handle_map) { process_id_list.push_back(event_handle.first); @@ -325,7 +333,7 @@ namespace eCAL // fire the publisher events // connected subscribers will read the content from the memory file - std::lock_guard lock(m_event_handle_map_sync); + const std::lock_guard lock(m_event_handle_map_sync); // "eat" old acknowledge events :) if (m_attr.timeout_ack_ms != 0) @@ -356,14 +364,20 @@ namespace eCAL long time_to_wait_ms = static_cast(std::chrono::duration_cast(time_to_wait).count()); if (time_to_wait_ms <= 0) time_to_wait_ms = 0; + if (event_handle.second.event_ack_is_invalid) + { + // The ack event has timeouted before. Thus, we don't wait for it + // anymore, until the subscriber notifies us via registration layer + // that it is still alive. + continue; + } + if (!gWaitForEvent(event_handle.second.event_ack, time_to_wait_ms)) { - // we close the event immediately to not waste time in the next - // write call, the event will be reopened later - // in ApplyLocSubscription if the connection still exists - gCloseEvent(event_handle.second.event_ack); - // invalidate it - gInvalidateEvent(&event_handle.second.event_ack); + // Remember that this event has timeouted. This will not cause the + // publisher to wait for it anymore, until the subscriber actively + // requests that via registration layer again. + event_handle.second.event_ack_is_invalid = true; #ifndef NDEBUG Logging::Log(log_level_debug2, m_base_name + "::CSyncMemoryFile::SignalWritten - ACK event timeout"); #endif @@ -378,7 +392,7 @@ namespace eCAL void CSyncMemoryFile::DisconnectAll() { - std::lock_guard lock(m_event_handle_map_sync); + const std::lock_guard lock(m_event_handle_map_sync); // fire acknowledge events, to unlock blocking send function for (const auto& event_handle : m_event_handle_map) diff --git a/ecal/core/src/io/ecal_memfile_sync.h b/src/core/src/io/shm/ecal_memfile_sync.h similarity index 81% rename from ecal/core/src/io/ecal_memfile_sync.h rename to src/core/src/io/shm/ecal_memfile_sync.h index 0fd0175..19bda5b 100644 --- a/ecal/core/src/io/ecal_memfile_sync.h +++ b/src/core/src/io/shm/ecal_memfile_sync.h @@ -23,9 +23,10 @@ #pragma once -#include +#include #include "readwrite/ecal_writer_data.h" +#include "ecal_eventhandle.h" #include "ecal_memfile.h" #include @@ -52,9 +53,11 @@ namespace eCAL bool Disconnect(const std::string& process_id_); bool CheckSize(size_t size_); - bool Write(const SWriterData& data_); + bool Write(CPayloadWriter& payload_, const SWriterAttr& data_, bool force_full_write_ = false); std::string GetName() const; + size_t GetSize() const; + bool IsCreated() const { return m_created; }; protected: bool Create(const std::string& base_name_, size_t size_); @@ -74,8 +77,9 @@ namespace eCAL { EventHandleT event_snd; EventHandleT event_ack; + bool event_ack_is_invalid = false; //!< The ack event has timeouted. Thus, we don't wait for it anymore, until the subscriber notifies us via registration layer that it is still alive. }; - typedef std::unordered_map EventHandleMapT; + using EventHandleMapT = std::unordered_map; std::mutex m_event_handle_map_sync; EventHandleMapT m_event_handle_map; }; diff --git a/ecal/core/src/io/linux/ecal_memfile_os.cpp b/src/core/src/io/shm/linux/ecal_memfile_os.cpp similarity index 87% rename from ecal/core/src/io/linux/ecal_memfile_os.cpp rename to src/core/src/io/shm/linux/ecal_memfile_os.cpp index 8857bce..5a80490 100644 --- a/ecal/core/src/io/linux/ecal_memfile_os.cpp +++ b/src/core/src/io/shm/linux/ecal_memfile_os.cpp @@ -21,7 +21,7 @@ * @brief memory file utility functions for posix platform **/ -#include "../ecal_memfile.h" +#include "io/shm/ecal_memfile.h" #include #include @@ -60,7 +60,14 @@ namespace eCAL umask(previous_umask); // reset umask to previous permissions if (mem_file_info_.memfile == -1) { - std::cout << "shm_open failed : " << mem_file_info_.name << " errno: " << strerror(errno) << std::endl; + if(create_) + { + std::cerr << "shm_open failed to CREATE memory file (memfile::os::AllocFile): " << mem_file_info_.name << " errno: " << strerror(errno) << std::endl; + } + else + { + std::cerr << "shm_open failed to OPEN memory file (memfile::os::AllocFile): " << mem_file_info_.name << " errno: " << strerror(errno) << std::endl; + } mem_file_info_.memfile = 0; mem_file_info_.name = ""; mem_file_info_.exists = false; @@ -101,7 +108,7 @@ namespace eCAL // truncate file if (::ftruncate(mem_file_info_.memfile, mem_file_info_.size) != 0) { - std::cout << "ftruncate failed : " << mem_file_info_.name << " errno: " << strerror(errno) << std::endl; + std::cerr << "ftruncate failed (memfile::os::MapFile): " << mem_file_info_.name << " errno: " << strerror(errno) << std::endl; } } @@ -113,7 +120,7 @@ namespace eCAL if (mem_file_info_.mem_address == MAP_FAILED) { mem_file_info_.mem_address = nullptr; - std::cout << "mmap failed : " << mem_file_info_.name << " errno: " << strerror(errno) << std::endl; + std::cerr << "mmap failed (memfile::os::MapFile): " << mem_file_info_.name << " errno: " << strerror(errno) << std::endl; return(false); } } diff --git a/ecal/core/src/io/win32/ecal_memfile_os.cpp b/src/core/src/io/shm/win32/ecal_memfile_os.cpp similarity index 99% rename from ecal/core/src/io/win32/ecal_memfile_os.cpp rename to src/core/src/io/shm/win32/ecal_memfile_os.cpp index 336d0db..58c3b90 100644 --- a/ecal/core/src/io/win32/ecal_memfile_os.cpp +++ b/src/core/src/io/shm/win32/ecal_memfile_os.cpp @@ -21,7 +21,7 @@ * @brief memory file utility functions for windows platform **/ -#include "../ecal_memfile.h" +#include "io/shm/ecal_memfile.h" namespace eCAL { diff --git a/src/core/src/io/udp/ecal_udp_configurations.cpp b/src/core/src/io/udp/ecal_udp_configurations.cpp new file mode 100644 index 0000000..4791981 --- /dev/null +++ b/src/core/src/io/udp/ecal_udp_configurations.cpp @@ -0,0 +1,168 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "ecal_udp_configurations.h" + +#include "ecal_def.h" +#include "ecal_udp_topic2mcast.h" + +#include + +namespace eCAL +{ + namespace UDP + { + /** + * @brief IsBroadcast() retrieves if we communicate via UDP Broadcast or UDP Multicast. + * + * @return True if broadcast mode is active. + */ + bool IsBroadcast() + { + return !Config::IsNetworkEnabled(); + } + + /** + * @brief IsNpcapEnabled() retrieves if we use the npcap UDP receiver (windows only). + * + * @return True if npcap mode is active. + */ + bool IsNpcapEnabled() + { + return Config::IsNpcapEnabled(); + } + + /** + * @brief Linux specific setting to enable joining multicast groups on all network interfacs independent of their link state. + * + * Enabling this makes sure that eCAL processes receive data if they are started before network devices are up and running. + * + * @return True if this setting is active. + */ + bool IsUdpMulticastJoinAllIfEnabled() + { + return Config::IsUdpMulticastJoinAllIfEnabled(); + } + + /** + * @brief GetLocalBroadcastAddress retrieves the broadcast address within the loopback range. + * + * This function returns the specific broadcast address 127.255.255.255, which is within + * the loopback range (127.0.0.0 to 127.255.255.255). It is commonly used for local + * communication within the same machine. Keep in mind that broadcasting to this address + * will only reach processes running on the same host. + * + * @return The loopback broadcast address "127.255.255.255" as a string. + */ + std::string GetLocalBroadcastAddress() + { + return "127.255.255.255"; + } + + std::string GetRegistrationAddress() + { + // check if the network is disabled + const bool local_only = !Config::IsNetworkEnabled(); + if (local_only) + { + return GetLocalBroadcastAddress(); + } + + // both in v1 and v2, the multicast group is returned as the adress for the registration layer + return Config::GetUdpMulticastGroup(); + } + + int GetRegistrationPort() + { + // retrieve the configured UDP multicast port from the configuration + const int configured_port = Config::GetUdpMulticastPort(); + + // add the specific offset, NET_UDP_MULTICAST_PORT_REG_OFF, to obtain the registration port + return configured_port + NET_UDP_MULTICAST_PORT_REG_OFF; + } + + std::string GetLoggingAddress() + { + // registration, logging and payload use the same addresses but different ports + return GetRegistrationAddress(); + } + + int GetLoggingPort() + { + // retrieve the configured UDP multicast port from the configuration + const int configured_port = Config::GetUdpMulticastPort(); + + // add the specific offset, NET_UDP_MULTICAST_PORT_LOG_OFF, to obtain the logging port + return configured_port + NET_UDP_MULTICAST_PORT_LOG_OFF; + } + + std::string GetPayloadAddress() + { + // registration, logging and payload use the same addresses but different ports + return GetRegistrationAddress(); + } + + std::string GetTopicPayloadAddress(const std::string& topic_name) + { + // check if the network is disabled + const bool local_only = !Config::IsNetworkEnabled(); + if (local_only) + { + // if network is disabled, return the local broadcast address + return GetLocalBroadcastAddress(); + } + + // determine the UDP multicast configuration version + if (Config::GetUdpMulticastConfigVersion() == Config::UdpConfigVersion::V1) + { + // retrieve the corresponding multicast address based on the topic name using v1 implementation + return UDP::V1::topic2mcast(topic_name, Config::GetUdpMulticastGroup(), Config::GetUdpMulticastMask()); + } + // v2 + else + { + // retrieve the corresponding multicast address based on the topic name using v2 implementation + return UDP::V2::topic2mcast(topic_name, Config::GetUdpMulticastGroup(), Config::GetUdpMulticastMask()); + } + } + + int GetPayloadPort() + { + // retrieve the configured UDP multicast port from the configuration + const int configured_port = Config::GetUdpMulticastPort(); + + // add the specific offset, NET_UDP_MULTICAST_PORT_SAMPLE_OFF, to obtain the payload port + return configured_port + NET_UDP_MULTICAST_PORT_SAMPLE_OFF; + } + + int GetMulticastTtl() + { + // check if the network is disabled + const bool local_only = !Config::IsNetworkEnabled(); + if (local_only) + { + // if network is disabled, return a TTL of 0 to restrict multicast packets to the local machine + return 1; + } + + // if network is enabled, return the configured UDP multicast TTL value + return Config::GetUdpMulticastTtl(); + } + } +} diff --git a/src/core/src/io/udp/ecal_udp_configurations.h b/src/core/src/io/udp/ecal_udp_configurations.h new file mode 100644 index 0000000..b4d640e --- /dev/null +++ b/src/core/src/io/udp/ecal_udp_configurations.h @@ -0,0 +1,143 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief common configurations for eCAL UDP communication +**/ + +#pragma once + +#include + +namespace eCAL +{ + namespace UDP + { + /** + * @brief IsBroadcast() retrieves if we communicate via UDP Broadcast or UDP Multicast. + * + * @return True if broadcast mode is active. + */ + bool IsBroadcast(); + + /** + * @brief IsNpcapEnabled() retrieves if we use the npcap UDP receiver (windows only). + * + * @return True if npcap mode is active. + */ + bool IsNpcapEnabled(); + + /** + * @brief Linux specific setting to enable joining multicast groups on all network interfacs independent of their link state. + * + * Enabling this makes sure that eCAL processes receive data if they are started before network devices are up and running. + * + * @return True if this setting is active. + */ + bool IsUdpMulticastJoinAllIfEnabled(); + + /** + * @brief GetRegistrationAddress retrieves the UDP registration address based on network configuration. + * + * If the network mode is disabled, it returns the local broadcast address. + * Otherwise, it retrieves the UDP multicast group address from the global configuration. + * + * @return The UDP registration address based on the network configuration. + */ + std::string GetRegistrationAddress(); + + /** + * @brief GetRegistrationPort retrieves the registration port based on the configured UDP multicast port. + * + * This function adds an offset, NET_UDP_MULTICAST_PORT_SAMPLE_OFF, to the UDP multicast port + * obtained from the configuration. The resulting port is used for registration communication. + * + * @return The registration port calculated by adding an offset to the configured UDP multicast port. + */ + int GetRegistrationPort(); + + /** + * @brief GetLoggingAddress retrieves the UDP logging address based on network configuration. + * + * If the network mode is disabled, it returns the local broadcast address. + * Otherwise, it retrieves the UDP multicast group address from the global configuration. + * + * @return The UDP logging address based on the network configuration. + */ + std::string GetLoggingAddress(); + + + /** + * @brief GetLoggingPort retrieves the logging port based on the configured UDP multicast port. + * + * This function adds an offset, NET_UDP_MULTICAST_PORT_SAMPLE_OFF, to the UDP multicast port + * obtained from the configuration. The resulting port is used for logging communication. + * + * @return The logging port calculated by adding an offset to the configured UDP multicast port. + */ + int GetLoggingPort(); + + /** + * @brief GetPayloadAddress retrieves the UDP payload address used as base address for udp receivers. + * + * If the network mode is disabled, it returns the local broadcast address. + * Otherwise, it retrieves the UDP multicast group address from the global configuration. + * + * @return The UDP payload address based on the network configuration. + */ + std::string GetPayloadAddress(); + + /** + * @brief GetTopicPayloadAddress retrieves the UDP payload address based on network configuration and the topic name. + * + * If the network mode is disabled, it returns the local broadcast address. + * + * If the topic name is empty, it returns base UDP multicast base group based on the global configuration. + * + * If a topic name is provided, it returns the payload address based on the topic name and +- * UDP multicast configuration. + * + * @param topic_name The name of the topic for which the payload address is requested. + * + * @return The payload address based on the network configuration and the topic name. + */ + std::string GetTopicPayloadAddress(const std::string& topic_name); + + /** + * @brief GetPayloadPort retrieves the payload port based on the configured UDP multicast port. + * + * This function adds an offset, NET_UDP_MULTICAST_PORT_SAMPLE_OFF, to the UDP multicast port + * obtained from the configuration. The resulting port is used for payload communication. + * + * @return The payload port calculated by adding an offset to the configured UDP multicast port. + */ + int GetPayloadPort(); + + /** + * @brief GetMulticastTtl retrieves the Time-to-Live (TTL) value for UDP multicast communication. + * + * If the network is disabled, it returns a TTL value of 0, indicating that multicast + * packets should not be forwarded beyond the local machine. + * If the network is enabled, it retrieves the configured UDP multicast TTL value from global configuration. + * + * @return The TTL value for UDP multicast communication based on the network configuration. + */ + int GetMulticastTtl(); + } +} diff --git a/src/core/src/io/udp/ecal_udp_sample_receiver.cpp b/src/core/src/io/udp/ecal_udp_sample_receiver.cpp new file mode 100644 index 0000000..18d773f --- /dev/null +++ b/src/core/src/io/udp/ecal_udp_sample_receiver.cpp @@ -0,0 +1,276 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP sample receiver to receive messages of type eCAL::Sample +**/ + +#include "ecal_udp_sample_receiver.h" +#include "io/udp/fragmentation/msg_type.h" + +#include + +#include +#include + +namespace eCAL +{ + namespace UDP + { + CSampleReceiver::CSampleDefragmentation::CSampleDefragmentation(CSampleReceiver* sample_receiver_) + : m_sample_receiver(sample_receiver_) + { + } + + CSampleReceiver::CSampleDefragmentation::~CSampleDefragmentation() = default; + + int CSampleReceiver::CSampleDefragmentation::OnMessageCompleted(std::vector&& msg_buffer_) + { + if (m_sample_receiver == nullptr) return(0); + + // read sample_name size + const unsigned short sample_name_size = ((unsigned short*)(msg_buffer_.data()))[0]; + // read sample_name + const std::string sample_name(msg_buffer_.data() + sizeof(sample_name_size)); + + if (m_sample_receiver->m_has_sample_callback(sample_name)) + { + // apply sample + m_sample_receiver->m_apply_sample_callback(msg_buffer_.data() + sizeof(sample_name_size) + sample_name_size, static_cast(msg_buffer_.size() - (sizeof(sample_name_size) + sample_name_size))); + } + + return(0); + } + + CSampleReceiver::CSampleReceiver(const IO::UDP::SReceiverAttr& attr_, HasSampleCallbackT has_sample_callback_, ApplySampleCallbackT apply_sample_callback_) : + m_has_sample_callback(std::move(has_sample_callback_)), m_apply_sample_callback(std::move(apply_sample_callback_)) + { + // create udp receiver + m_udp_receiver.Create(attr_); + + // allocate receive buffer + m_msg_buffer.resize(MSG_BUFFER_SIZE); + + // start receiver thread + m_udp_receiver_thread = std::make_shared([this] { ReceiveThread(); }); + m_udp_receiver_thread->start(std::chrono::milliseconds(0)); + + m_cleanup_start = std::chrono::steady_clock::now(); + } + + CSampleReceiver::~CSampleReceiver() + { + // stop receiver thread + m_udp_receiver_thread->stop(); + + // destroy udp receiver + m_udp_receiver.Destroy(); + } + + bool CSampleReceiver::AddMultiCastGroup(const char* ipaddr_) + { + return m_udp_receiver.AddMultiCastGroup(ipaddr_); + } + + bool CSampleReceiver::RemMultiCastGroup(const char* ipaddr_) + { + return m_udp_receiver.RemMultiCastGroup(ipaddr_); + } + + void CSampleReceiver::ReceiveThread() + { + // wait for any incoming message + const size_t recv_len = m_udp_receiver.Receive(m_msg_buffer.data(), m_msg_buffer.size(), CMN_UDP_RECEIVE_THREAD_CYCLE_TIME_MS); + if (recv_len > 0) + { + Process(m_msg_buffer.data(), recv_len); + } + } + + void CSampleReceiver::Process(const char* sample_buffer_, size_t sample_buffer_len_) + { + // we need at least the header information to start + if (sample_buffer_len_ < sizeof(IO::UDP::SUDPMessageHead)) return; + + // cast buffer to udp message struct + auto ecal_message = (struct IO::UDP::SUDPMessage*)sample_buffer_; + + // check for eCAL 4.x header + if ( + (ecal_message->header.head[0] == 'e') + && (ecal_message->header.head[1] == 'C') + && (ecal_message->header.head[2] == 'A') + && (ecal_message->header.head[3] == 'L') + ) + { + eCAL::Logging::Log(log_level_warning, "Received eCAL 4 traffic"); + return; + } + + // check for valid header + else if ( + (ecal_message->header.head[0] != 'E') + || (ecal_message->header.head[1] != 'C') + || (ecal_message->header.head[2] != 'A') + || (ecal_message->header.head[3] != 'L') + ) + { + eCAL::Logging::Log(log_level_warning, "Received invalid traffic (eCAL Header missing)"); + return; + } + + // check integrity + switch (ecal_message->header.type) + { + case IO::UDP::msg_type_header: + break; + case IO::UDP::msg_type_content: + case IO::UDP::msg_type_header_with_content: + if (sample_buffer_len_ < sizeof(IO::UDP::SUDPMessageHead) + static_cast(ecal_message->header.len)) + return; + break; + default: + return; + } + +#ifndef NDEBUG + // log it + switch (ecal_message->header.type) + { + case IO::UDP::msg_type_header_with_content: + eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER_WITH_CONTENT"); + break; + case IO::UDP::msg_type_header: + eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - HEADER"); + break; + case IO::UDP::msg_type_content: + eCAL::Logging::Log(log_level_debug4, "UDP Sample Received - CONTENT"); + break; + } +#endif + + switch (ecal_message->header.type) + { + case IO::UDP::msg_type_header_with_content: + { + // read sample_name size + unsigned short sample_name_size = 0; + memcpy(&sample_name_size, ecal_message->payload, 2); + // read sample_name + const std::string sample_name = ecal_message->payload + sizeof(sample_name_size); + + if (m_has_sample_callback(sample_name)) + { + // apply sample + m_apply_sample_callback(ecal_message->payload + static_cast(sizeof(sample_name_size) + sample_name_size), static_cast(static_cast(ecal_message->header.len) - (sizeof(sample_name_size) + sample_name_size))); + } + } + break; + // if we have a header only package + // we create a receive defragmentation buffer and apply it to the receive defragmentation map + // to process the following data packages + // we do not know the name here unfortunately + // so we have to wait for the first payload package :-( + case IO::UDP::msg_type_header: + { + // create new receive defragmentation buffer + std::shared_ptr receive_defragmentation_buf(nullptr); + receive_defragmentation_buf = std::make_shared(CSampleDefragmentation(this)); + m_defrag_sample_map[ecal_message->header.id] = receive_defragmentation_buf; + // apply message + receive_defragmentation_buf->ApplyMessage(*ecal_message); + } + break; + // if we have a payload package + // we check for an existing receive defragmentation buffer and apply the data to it + case IO::UDP::msg_type_content: + { + // first data package ? + if (ecal_message->header.num == 0) + { + // read sample_name size + unsigned short sample_name_size = 0; + memcpy(&sample_name_size, ecal_message->payload, 2); + // read sample_name + const std::string sample_name = ecal_message->payload + sizeof(sample_name_size); + + // remove the matching defragmentation buffer if we are not interested in this sample + if (!m_has_sample_callback(sample_name)) + { + auto riter = m_defrag_sample_map.find(ecal_message->header.id); + if (riter != m_defrag_sample_map.end()) + { +#ifndef NDEBUG + // log timeouted defragmentation buffers + eCAL::Logging::Log(log_level_debug3, "CUDPSampleReceiver::Receive - DISCARD PACKAGE FOR TOPIC: " + sample_name); +#endif + m_defrag_sample_map.erase(riter); + break; + } + } + } + + // process data package + auto iter = m_defrag_sample_map.find(ecal_message->header.id); + if (iter != m_defrag_sample_map.end()) + { + // apply message + iter->second->ApplyMessage(*ecal_message); + } + } + break; + default: + break; + } + + // cleanup finished or zombie received defragmentation buffers + auto diff_time = std::chrono::steady_clock::now() - m_cleanup_start; + const std::chrono::duration step_time = std::chrono::milliseconds(NET_UDP_RECBUFFER_CLEANUP); + if (diff_time > step_time) + { + m_cleanup_start = std::chrono::steady_clock::now(); + + for (auto riter = m_defrag_sample_map.begin(); riter != m_defrag_sample_map.end();) + { + const bool finished = riter->second->HasFinished(); + const bool timeouted = riter->second->HasTimedOut(step_time); + if (finished || timeouted) + { +#ifndef NDEBUG + const int32_t total_len = riter->second->GetMessageTotalLength(); + const int32_t current_len = riter->second->GetMessageCurrentLength(); +#endif + riter = m_defrag_sample_map.erase(riter); +#ifndef NDEBUG + // log timeouted defragmentation buffer + if (timeouted) + { + eCAL::Logging::Log(log_level_debug3, "CUDPSampleReceiver::Receive - TIMEOUT (TotalLength / CurrentLength): " + std::to_string(total_len) + " / " + std::to_string(current_len)); + } +#endif + } + else + { + ++riter; + } + } + } + } + } +} diff --git a/src/core/src/io/udp/ecal_udp_sample_receiver.h b/src/core/src/io/udp/ecal_udp_sample_receiver.h new file mode 100644 index 0000000..5178ba6 --- /dev/null +++ b/src/core/src/io/udp/ecal_udp_sample_receiver.h @@ -0,0 +1,83 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP sample receiver to receive messages of type eCAL::Sample +**/ + +#pragma once + +#include "io/udp/sendreceive/udp_receiver.h" +#include "io/udp/fragmentation/rcv_fragments.h" +#include "util/ecal_thread.h" + +#include +#include +#include +#include +#include +#include + +namespace eCAL +{ + namespace UDP + { + class CSampleReceiver + { + public: + using HasSampleCallbackT = std::function; + using ApplySampleCallbackT = std::function; + + CSampleReceiver(const IO::UDP::SReceiverAttr& attr_, HasSampleCallbackT has_sample_callback_, ApplySampleCallbackT apply_sample_callback_); + virtual ~CSampleReceiver(); + + bool AddMultiCastGroup(const char* ipaddr_); + bool RemMultiCastGroup(const char* ipaddr_); + + protected: + void ReceiveThread(); + void Process(const char* sample_buffer_, size_t sample_buffer_len_); + + HasSampleCallbackT m_has_sample_callback; + ApplySampleCallbackT m_apply_sample_callback; + + IO::UDP::CUDPReceiver m_udp_receiver; + std::shared_ptr m_udp_receiver_thread; + + std::vector m_msg_buffer; + + std::chrono::steady_clock::time_point m_cleanup_start; + + class CSampleDefragmentation : public IO::UDP::CMsgDefragmentation + { + public: + explicit CSampleDefragmentation(CSampleReceiver* sample_receiver_); + ~CSampleDefragmentation() override; + + int OnMessageCompleted(std::vector&& msg_buffer_) override; + + protected: + CSampleReceiver* m_sample_receiver; + }; + + using SampleDefragmentationMapT = std::unordered_map>; + SampleDefragmentationMapT m_defrag_sample_map; + }; + } +} diff --git a/src/core/src/io/udp/ecal_udp_sample_sender.cpp b/src/core/src/io/udp/ecal_udp_sample_sender.cpp new file mode 100644 index 0000000..c13f626 --- /dev/null +++ b/src/core/src/io/udp/ecal_udp_sample_sender.cpp @@ -0,0 +1,68 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP sample sender to send messages of type eCAL::Sample +**/ + +#include "ecal_udp_sample_sender.h" +#include "io/udp/fragmentation/snd_fragments.h" + +#include + +namespace +{ + size_t TransmitToUDP(const void* buf_, const size_t len_, const std::shared_ptr& sample_sender_, const std::string& mcast_address_) + { + return (sample_sender_->Send(buf_, len_, mcast_address_.c_str())); + } +} + +namespace eCAL +{ + namespace UDP + { + CSampleSender::CSampleSender(const IO::UDP::SSenderAttr& attr_) + { + m_udp_sender = std::make_shared(attr_); + } + + size_t CSampleSender::Send(const std::string& sample_name_, const std::vector& serialized_sample_) + { + if (!m_udp_sender) return(0); + + std::lock_guard const send_lock(m_payload_mutex); + // return value + size_t sent_sum(0); + + const size_t data_size = IO::UDP::CreateSampleBuffer(sample_name_, serialized_sample_, m_payload); + if (data_size > 0) + { + // and send it + sent_sum = SendFragmentedMessage(m_payload.data(), data_size, std::bind(TransmitToUDP, std::placeholders::_1, std::placeholders::_2, m_udp_sender, m_attr.address)); + + // log it + //std::cout << "UDP Sample Buffer Sent (" << std::to_string(sent_sum) << " Bytes)" << std::endl;; + } + + // return bytes sent + return(sent_sum); + } + } +} diff --git a/ecal/core/src/readwrite/ecal_writer_iceoryx.h b/src/core/src/io/udp/ecal_udp_sample_sender.h similarity index 58% rename from ecal/core/src/readwrite/ecal_writer_iceoryx.h rename to src/core/src/io/udp/ecal_udp_sample_sender.h index 72155c2..aeda1a3 100644 --- a/ecal/core/src/readwrite/ecal_writer_iceoryx.h +++ b/src/core/src/io/udp/ecal_udp_sample_sender.h @@ -18,35 +18,34 @@ */ /** - * @brief shared memory (iceoryx) writer + * @brief UDP sample sender to send messages of type eCAL::Sample **/ #pragma once -#include "readwrite/ecal_writer_base.h" - -#include +#include "io/udp/sendreceive/udp_sender.h" #include +#include #include +#include namespace eCAL { - // ecal shared memory (Iceoryx) writer - class CDataWriterSHM : public CDataWriterBase + namespace UDP { - public: - CDataWriterSHM(); - ~CDataWriterSHM(); - - SWriterInfo GetInfo() override; - - bool Create(const std::string& host_name_, const std::string& topic_name_, const std::string & topic_id_) override; - bool Destroy() override; - - bool Write(const SWriterData& data_) override; - - private: - std::shared_ptr m_publisher; - }; + class CSampleSender + { + public: + CSampleSender(const IO::UDP::SSenderAttr& attr_); + size_t Send(const std::string& sample_name_, const std::vector& serialized_sample_); + + private: + IO::UDP::SSenderAttr m_attr; + std::shared_ptr m_udp_sender; + + std::mutex m_payload_mutex; + std::vector m_payload; + }; + } } diff --git a/ecal/core/src/topic2mcast.h b/src/core/src/io/udp/ecal_udp_topic2mcast.h similarity index 95% rename from ecal/core/src/topic2mcast.h rename to src/core/src/io/udp/ecal_udp_topic2mcast.h index 8969761..510db51 100644 --- a/ecal/core/src/topic2mcast.h +++ b/src/core/src/io/udp/ecal_udp_topic2mcast.h @@ -23,6 +23,7 @@ #pragma once +#include #include #include #include @@ -38,7 +39,7 @@ namespace eCAL { size_t operator()(std::string const& s) const { - size_t result = static_cast(2166136261U); + auto result = static_cast(2166136261U); std::string::const_iterator end = s.end(); for (std::string::const_iterator iter = s.begin(); iter != end; @@ -60,7 +61,7 @@ namespace eCAL int i = 0; while (std::getline(ss, token, '.') && (i < 4)) //-V112 { - unsigned char mask = static_cast(atoi(token.c_str())); + auto mask = static_cast(atoi(token.c_str())); ipv4 = ipv4 << 8 | mask; i++; } @@ -121,7 +122,7 @@ namespace eCAL inline std::string topic2mcast(const std::string& tname_, const std::string& mcast_base_, const std::string& mcast_mask_) { struct fnv_hash thash; - uint32_t hash_v = static_cast(thash(tname_)); + auto hash_v = static_cast(thash(tname_)); return topic2mcast_hash(hash_v, mcast_base_, mcast_mask_); } @@ -140,7 +141,7 @@ namespace eCAL int i = 0; while (std::getline(ss, token, '.') && (i < 4)) //-V112 { - unsigned char mask = static_cast(atoi(token.c_str())); + auto mask = static_cast(atoi(token.c_str())); address_mask[i] = mask; i++; } @@ -179,7 +180,5 @@ namespace eCAL return topic2mcast_hash(hash_v, mcast_base_, mcast_mask_); } } - - } } diff --git a/ecal/core/src/io/msg_type.h b/src/core/src/io/udp/fragmentation/msg_type.h similarity index 52% rename from ecal/core/src/io/msg_type.h rename to src/core/src/io/udp/fragmentation/msg_type.h index 8346bf4..689f5d8 100644 --- a/ecal/core/src/io/msg_type.h +++ b/src/core/src/io/udp/fragmentation/msg_type.h @@ -23,43 +23,45 @@ #pragma once -#include +#include -enum eUDPMessageType +namespace IO { - msg_type_unknown = 0, - msg_type_header = 1, - msg_type_content = 2, - msg_type_header_with_content = 3 -}; - -struct alignas(4) SUDPMessageHead -{ - SUDPMessageHead() + namespace UDP { - head[0] = 'E'; - head[1] = 'C'; - head[2] = 'A'; - head[3] = 'L'; - version = 5; - type = msg_type_unknown; - id = 0; - num = 0; - len = 0; - } + enum eUDPMessageType + { + msg_type_unknown = 0, + msg_type_header = 1, + msg_type_content = 2, + msg_type_header_with_content = 3 + }; + + struct alignas(4) SUDPMessageHead + { + SUDPMessageHead() + { + head[0] = 'E'; + head[1] = 'C'; + head[2] = 'A'; + head[3] = 'L'; + } - char head[4]; //-V112 - int32_t version; - int32_t type; - int32_t id; // unique id for all message parts - int32_t num; // header: number of all parts, data: current number of that part - int32_t len; // header: complete size of message, data: current size of that part -}; + char head[4]{}; //-V112 + int32_t version = 5; + int32_t type = msg_type_unknown; + int32_t id = 0; // unique id for all message parts + int32_t num = 0; // header: number of all parts, data: current number of that part + int32_t len = 0; // header: complete size of message, data: current size of that part + }; #define MSG_BUFFER_SIZE (64*1024 - 20 /* IP header */ - 8 /* UDP header */ - 1 /* don't ask */) #define MSG_PAYLOAD_SIZE (MSG_BUFFER_SIZE-sizeof(struct SUDPMessageHead)) -struct SUDPMessage -{ - struct SUDPMessageHead header; - char payload[MSG_PAYLOAD_SIZE]; -}; + + struct SUDPMessage + { + struct SUDPMessageHead header; + char payload[MSG_PAYLOAD_SIZE]{}; + }; + } +} diff --git a/src/core/src/io/udp/fragmentation/rcv_fragments.cpp b/src/core/src/io/udp/fragmentation/rcv_fragments.cpp new file mode 100644 index 0000000..2eaf8c6 --- /dev/null +++ b/src/core/src/io/udp/fragmentation/rcv_fragments.cpp @@ -0,0 +1,151 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP sample receiver to receive messages of type eCAL::Sample +**/ + + +#include "rcv_fragments.h" +#include "msg_type.h" + +#include + +#include + +namespace IO +{ + namespace UDP + { + CMsgDefragmentation::CMsgDefragmentation() + : m_timeout(0.0) + , m_recv_mode(rcm_waiting) + , m_message_id(0) + , m_message_total_num(0) + , m_message_total_len(0) + , m_message_curr_num(0) + , m_message_curr_len(0) + { + } + + CMsgDefragmentation::~CMsgDefragmentation() = default; + + int CMsgDefragmentation::ApplyMessage(const struct SUDPMessage& ecal_message_) + { + // reset timeout + m_timeout = std::chrono::duration(0.0); + + // process current packet + switch (ecal_message_.header.type) + { + // new message started + case msg_type_header: + OnMessageStart(ecal_message_); + break; + // message data package + case msg_type_content: + if (m_recv_mode == rcm_reading) + { + OnMessageData(ecal_message_); + } + break; + } + + // we have a complete message in the receive buffer + if (m_recv_mode == rcm_completed) + { + // call complete event + OnMessageCompleted(std::move(m_recv_buffer)); + } + + return(0); + } + + int CMsgDefragmentation::OnMessageStart(const struct SUDPMessage& ecal_message_) + { + // store header info + m_message_id = ecal_message_.header.id; + m_message_total_num = ecal_message_.header.num; + m_message_total_len = ecal_message_.header.len; + + // reset current message states + m_message_curr_num = 0; + m_message_curr_len = 0; + + // prepare receive buffer + m_recv_buffer.reserve(static_cast(m_message_total_len)); + + // switch to reading mode + m_recv_mode = rcm_reading; + + return(0); + } + + int CMsgDefragmentation::OnMessageData(const struct SUDPMessage& ecal_message_) + { + // check message id + if (ecal_message_.header.id != m_message_id) + { +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET ID " + std::to_string(ecal_message_.header.id)); +#endif + m_recv_mode = rcm_aborted; + return(-1); + } + + // check current packet counter + if (ecal_message_.header.num != m_message_curr_num) + { +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET NUMBER " + std::to_string(ecal_message_.header.num) + " / " + std::to_string(m_message_curr_num)); +#endif + m_recv_mode = rcm_aborted; + return(-1); + } + + // check current packet length + if (ecal_message_.header.len <= 0) + { +#ifndef NDEBUG + // log it + eCAL::Logging::Log(log_level_debug3, "UDP Sample OnMessageData - WRONG MESSAGE PACKET LENGTH " + std::to_string(ecal_message_.header.len)); +#endif + m_recv_mode = rcm_aborted; + return(-1); + } + + // copy the message part to the receive message buffer + m_recv_buffer.resize(m_recv_buffer.size() + static_cast(ecal_message_.header.len)); + memcpy(m_recv_buffer.data() + m_recv_buffer.size() - static_cast(ecal_message_.header.len), ecal_message_.payload, static_cast(ecal_message_.header.len)); + + // increase packet counter + m_message_curr_num++; + + // increase current length + m_message_curr_len += ecal_message_.header.len; + + // last message packet ? -> switch to completed mode + if (m_message_curr_num == m_message_total_num) m_recv_mode = rcm_completed; + + return(0); + } + } +} diff --git a/src/core/src/io/udp/fragmentation/rcv_fragments.h b/src/core/src/io/udp/fragmentation/rcv_fragments.h new file mode 100644 index 0000000..cf7a11b --- /dev/null +++ b/src/core/src/io/udp/fragmentation/rcv_fragments.h @@ -0,0 +1,75 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP sample receiver to receive messages of type eCAL::Sample +**/ + +#pragma once + +#include "ecal_def.h" + +#include +#include + +namespace IO +{ + namespace UDP + { + class CMsgDefragmentation + { + public: + CMsgDefragmentation(); + virtual ~CMsgDefragmentation(); + + int ApplyMessage(const struct SUDPMessage& ecal_message_); + + bool HasFinished() { return((m_recv_mode == rcm_aborted) || (m_recv_mode == rcm_completed)); }; + bool HasTimedOut(const std::chrono::duration& diff_time_) { m_timeout += diff_time_; return(m_timeout >= std::chrono::milliseconds(NET_UDP_RECBUFFER_TIMEOUT)); }; + + int32_t GetMessageTotalLength() const { return(m_message_total_len); }; + int32_t GetMessageCurrentLength() const { return(m_message_curr_len); }; + + virtual int OnMessageCompleted(std::vector&& msg_buffer_) = 0; + + protected: + int OnMessageStart(const struct SUDPMessage& ecal_message_); + int OnMessageData(const struct SUDPMessage& ecal_message_); + + enum eReceiveMode + { + rcm_waiting = 1, + rcm_reading, + rcm_aborted, + rcm_completed + }; + + std::chrono::duration m_timeout; + std::vector m_recv_buffer; + eReceiveMode m_recv_mode; + + int32_t m_message_id; + int32_t m_message_total_num; + int32_t m_message_total_len; + + int32_t m_message_curr_num; + int32_t m_message_curr_len; + }; + } +} diff --git a/src/core/src/io/udp/fragmentation/snd_fragments.cpp b/src/core/src/io/udp/fragmentation/snd_fragments.cpp new file mode 100644 index 0000000..d72dd7a --- /dev/null +++ b/src/core/src/io/udp/fragmentation/snd_fragments.cpp @@ -0,0 +1,180 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief raw message buffer handling +**/ + +#include "snd_fragments.h" +#include "msg_type.h" + +#include +#include +#include +#include + +namespace +{ + // random number generator + unsigned long xorshf96(unsigned long& x, unsigned long& y, unsigned long& z) // period 2^96-1 + { + x ^= x << 16; + x ^= x >> 5; + x ^= x << 1; + + unsigned long t = x; + x = y; + y = z; + z = t ^ x ^ y; + + return z; + } +} + +namespace IO +{ + namespace UDP + { + size_t CreateSampleBuffer(const std::string& sample_name_, const std::vector& serialized_sample_, std::vector& payload_) + { + const unsigned short sample_name_size = (unsigned short)sample_name_.size() + 1; + const size_t sample_size = serialized_sample_.size(); + const size_t data_size = sizeof(sample_name_size) + sample_name_size + sample_size; + + // create payload buffer with reserved space for first message head + payload_.resize(data_size + sizeof(struct SUDPMessageHead)); + char* payload_data = payload_.data() + sizeof(struct SUDPMessageHead); + + // write topic name size + ((unsigned short*)payload_data)[0] = sample_name_size; + // write topic name + memcpy(payload_data + sizeof(sample_name_size), sample_name_.c_str(), sample_name_size); + // write payload + memcpy(payload_data + sizeof(sample_name_size) + sample_name_size, serialized_sample_.data(), sample_size); + + return data_size; + } + + size_t SendFragmentedMessage(char* buf_, size_t buf_len_, const TransmitCallbackT& transmit_cb_) + { + if (buf_ == nullptr) return(0); + + size_t sent_sum(0); + + auto total_packet_num = int32_t(buf_len_ / MSG_PAYLOAD_SIZE); + if (buf_len_ % MSG_PAYLOAD_SIZE) total_packet_num++; + + // create message header + struct SUDPMessageHead msg_header; + + switch (total_packet_num) + { + case 1: + { + // create start packet + msg_header.type = msg_type_header_with_content; + msg_header.id = -1; // not needed for combined header / data message + msg_header.num = 1; + msg_header.len = int32_t(buf_len_); + + // copy msg_header in send buffer + memcpy(buf_, &msg_header, sizeof(struct SUDPMessageHead)); + + // send single header + data package + size_t sent = transmit_cb_(buf_, sizeof(struct SUDPMessageHead) + buf_len_); + if (sent == 0) return(sent); + sent_sum += sent; + +#ifndef NDEBUG + // log it + //std::cout << "SendRawBuffer Packet Sent - HEADER_WITH_CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; +#endif + } + break; + default: + { + // create start package + msg_header.type = msg_type_header; + { + // create random number for message id + { + static std::mutex xorshf96_mtx; + const std::lock_guard lock(xorshf96_mtx); + + static unsigned long x = static_cast(std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()).count() + ); + static unsigned long y = 362436069; + static unsigned long z = 521288629; + + msg_header.id = static_cast(xorshf96(x, y, z)); + } + } + msg_header.num = total_packet_num; + msg_header.len = int32_t(buf_len_); + + // send start package + size_t sent = transmit_cb_(&msg_header, sizeof(struct SUDPMessageHead)); + if (sent == 0) return(sent); + sent_sum += sent; + +#ifndef NDEBUG + // log it + //std::cout << "SendRawBuffer Packet Sent - HEADER (" + std::to_string(sent) + " Bytes)" << std::endl; +#endif + + // send data packages + msg_header.type = msg_type_content; + for (int32_t current_packet_num = 0; current_packet_num < total_packet_num; current_packet_num++) + { + // calculate current payload + size_t current_snd_len = buf_len_; + if (current_snd_len > MSG_PAYLOAD_SIZE) current_snd_len = MSG_PAYLOAD_SIZE; + + if (current_snd_len > 0) + { + // reduce total send len + buf_len_ -= current_snd_len; + + // create data packet numbering + msg_header.num = current_packet_num; + msg_header.len = int32_t(current_snd_len); + + // copy msg_header in send buffer + memcpy(buf_ + static_cast(current_packet_num) * MSG_PAYLOAD_SIZE, &msg_header, sizeof(struct SUDPMessageHead)); + + // send data package + sent = transmit_cb_(buf_ + static_cast(current_packet_num) * MSG_PAYLOAD_SIZE, sizeof(struct SUDPMessageHead) + current_snd_len); + if (sent == 0) return(sent); + +#ifndef NDEBUG + // log it + //std::cout << "SendRawBuffer Packet Sent - CONTENT (" + std::to_string(sent) + " Bytes)" << std::endl; +#endif + sent_sum += sent; + } + } + } + break; + } + + return(sent_sum); + } + } +} diff --git a/ecal/core/src/io/udp_configurations.h b/src/core/src/io/udp/fragmentation/snd_fragments.h similarity index 66% rename from ecal/core/src/io/udp_configurations.h rename to src/core/src/io/udp/fragmentation/snd_fragments.h index fb67bae..b4a2793 100644 --- a/ecal/core/src/io/udp_configurations.h +++ b/src/core/src/io/udp/fragmentation/snd_fragments.h @@ -5,9 +5,9 @@ * 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. @@ -18,23 +18,20 @@ */ /** - * @brief common configurations for eCAL UDP communication + * @brief raw message buffer handling **/ -#pragma once - +#include #include +#include -namespace eCAL +namespace IO { namespace UDP { - // Return the Multicast Adress used for sending Registration information - std::string GetRegistrationMulticastAddress(); + size_t CreateSampleBuffer(const std::string& sample_name_, const std::vector& serialized_sample_, std::vector& payload_); - std::string GetLoggingMulticastAddress(); - - std::string GetTopicMulticastAddress(const std::string& topic_name); + using TransmitCallbackT = std::function; + size_t SendFragmentedMessage(char* buf_, size_t buf_len_, const TransmitCallbackT& transmit_cb_); } - -} \ No newline at end of file +} diff --git a/src/core/src/io/udp/sendreceive/linux/socket_os.h b/src/core/src/io/udp/sendreceive/linux/socket_os.h new file mode 100644 index 0000000..a436413 --- /dev/null +++ b/src/core/src/io/udp/sendreceive/linux/socket_os.h @@ -0,0 +1,88 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2022 - Teknique Limited + * + * 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 +#include +#include + + +namespace IO +{ + namespace UDP + { + inline static std::vector get_interface_index_list() + { + std::vector interface_index_list; + ifaddrs* ifa = nullptr; + ifaddrs* ifap = nullptr; + + // get a list of network interfaces + getifaddrs(&ifap); + + // create a list of network interfaces indexes + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_PACKET) + { + int index = if_nametoindex(ifa->ifa_name); + if (index) + { + interface_index_list.push_back(index); + } + } + } + + freeifaddrs(ifap); + + return interface_index_list; + } + + inline static bool set_socket_mcast_group_option(int socket, const char* ipaddr_, int option) + { + // set the multicast socket option on all interfaces + for (int iface : get_interface_index_list()) + { + group_req group_req = {}; + sockaddr_in* group = nullptr; + + memset(&group_req, 0, sizeof(group_req)); + group_req.gr_interface = iface; + group = reinterpret_cast(&group_req.gr_group); + group->sin_family = AF_INET; + group->sin_addr.s_addr = inet_addr(ipaddr_); + group->sin_port = 0; + + int rc = setsockopt(socket, IPPROTO_IP, option, &group_req, sizeof(group_source_req)); + if (rc != 0) + { + std::cerr << "setsockopt failed. Unable to set multicast group option: " << strerror(errno) << std::endl; + return(false); + } + } + + return(true); + } + } +} diff --git a/src/core/src/io/udp/sendreceive/udp_receiver.cpp b/src/core/src/io/udp/sendreceive/udp_receiver.cpp new file mode 100644 index 0000000..9eb89af --- /dev/null +++ b/src/core/src/io/udp/sendreceive/udp_receiver.cpp @@ -0,0 +1,106 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP receiver class +**/ + +#include "udp_receiver.h" +#include "io/udp/ecal_udp_configurations.h" + +#include "udp_receiver_asio.h" +#ifdef ECAL_NPCAP_SUPPORT +#include "udp_receiver_npcap.h" +#endif + +#include + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // udp receiver class + //////////////////////////////////////////////////////// + CUDPReceiver::CUDPReceiver() + : m_use_npcap(false) + { +#ifdef ECAL_NPCAP_SUPPORT + if (eCAL::UDP::IsNpcapEnabled()) + { + m_use_npcap = Udpcap::Initialize(); // Only use NPCAP if we can initialize it (or it has already been initialized successfully) + if (!m_use_npcap) + { + std::cerr << "Npcap is enabled, but cannot be initialized. Using socket fallback mode." << std::endl; + } + } +#endif //ECAL_NPCAP_SUPPORT + } + + bool CUDPReceiver::Create(const SReceiverAttr& attr_) + { + if (m_socket_impl) return false; + +#ifdef ECAL_NPCAP_SUPPORT + if (m_use_npcap) + { + m_socket_impl = std::make_shared(attr_); + return true; + } +#endif // ECAL_NPCAP_SUPPORT + + m_socket_impl = std::make_shared(attr_); + return(true); + } + + bool CUDPReceiver::Destroy() + { + if (!m_socket_impl) return(false); + + const std::lock_guard lock(m_socket_mtx); + m_socket_impl.reset(); + + return(true); + } + + bool CUDPReceiver::AddMultiCastGroup(const char* ipaddr_) + { + if (!m_socket_impl) return(false); + + const std::lock_guard lock(m_socket_mtx); + return(m_socket_impl->AddMultiCastGroup(ipaddr_)); + } + + bool CUDPReceiver::RemMultiCastGroup(const char* ipaddr_) + { + if (!m_socket_impl) return(false); + + const std::lock_guard lock(m_socket_mtx); + return(m_socket_impl->RemMultiCastGroup(ipaddr_)); + } + + size_t CUDPReceiver::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) + { + if (!m_socket_impl) return(0); + + const std::lock_guard lock(m_socket_mtx); + return(m_socket_impl->Receive(buf_, len_, timeout_, address_)); + } + } +} diff --git a/src/core/src/io/udp/sendreceive/udp_receiver.h b/src/core/src/io/udp/sendreceive/udp_receiver.h new file mode 100644 index 0000000..b91749c --- /dev/null +++ b/src/core/src/io/udp/sendreceive/udp_receiver.h @@ -0,0 +1,93 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP receiver class +**/ + +#pragma once + +#include + +#ifdef ECAL_OS_WINDOWS +#include "win32/socket_os.h" +#endif + +#ifdef ECAL_OS_LINUX +#include +#include +#include +#endif + +#include +#include +#include + +namespace IO +{ + namespace UDP + { + struct SReceiverAttr + { + std::string address; + int port = 0; + bool broadcast = false; + bool loopback = true; + int rcvbuf = 1024 * 1024; + }; + + class CUDPReceiverImpl + { + public: + explicit CUDPReceiverImpl(const SReceiverAttr& /*attr_*/) {}; + // We don't technically need a virtual destructor, if we are working with shared_ptrs... + virtual ~CUDPReceiverImpl() = default; + + // Delete copy / move operations to prevent slicing + CUDPReceiverImpl(CUDPReceiverImpl&&) = delete; + CUDPReceiverImpl& operator=(CUDPReceiverImpl&&) = delete; + CUDPReceiverImpl(const CUDPReceiverImpl&) = delete; + CUDPReceiverImpl& operator=(const CUDPReceiverImpl&) = delete; + + virtual bool AddMultiCastGroup(const char* ipaddr_) = 0; + virtual bool RemMultiCastGroup(const char* ipaddr_) = 0; + + virtual size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_) = 0; + }; + + class CUDPReceiver + { + public: + CUDPReceiver(); + + bool Create(const SReceiverAttr& attr_); + bool Destroy(); + + bool AddMultiCastGroup(const char* ipaddr_); + bool RemMultiCastGroup(const char* ipaddr_); + + size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr); + + protected: + bool m_use_npcap; + std::mutex m_socket_mtx; + std::shared_ptr m_socket_impl; + }; + } +} diff --git a/src/core/src/io/udp/sendreceive/udp_receiver_asio.cpp b/src/core/src/io/udp/sendreceive/udp_receiver_asio.cpp new file mode 100644 index 0000000..454b52e --- /dev/null +++ b/src/core/src/io/udp/sendreceive/udp_receiver_asio.cpp @@ -0,0 +1,229 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + + +#include "udp_receiver_asio.h" + +#include "io/udp/ecal_udp_configurations.h" + +#ifdef __linux__ +#include "linux/socket_os.h" +#endif + +#include + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // Default ASIO based receiver class implementation + //////////////////////////////////////////////////////// + CUDPReceiverAsio::CUDPReceiverAsio(const SReceiverAttr& attr_) : + CUDPReceiverImpl(attr_), + m_created(false), + m_broadcast(attr_.broadcast), + m_socket(m_iocontext) + { + // create socket + const asio::ip::udp::endpoint listen_endpoint(asio::ip::udp::v4(), static_cast(attr_.port)); + { + asio::error_code ec; + m_socket.open(listen_endpoint.protocol(), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to open socket: " << ec.message() << std::endl; + return; + } + } + + // set socket reuse + { + asio::error_code ec; + m_socket.set_option(asio::ip::udp::socket::reuse_address(true), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to set reuse-address option: " << ec.message() << std::endl; + } + } + + // bind socket + { + asio::error_code ec; + m_socket.bind(listen_endpoint, ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to bind socket to " << listen_endpoint.address().to_string() << ":" << listen_endpoint.port() << ": " << ec.message() << std::endl; + return; + } + } + + // set loopback option + { + const asio::ip::multicast::enable_loopback loopback(attr_.loopback); + asio::error_code ec; + m_socket.set_option(loopback, ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to enable loopback: " << ec.message() << std::endl; + } + } + + // set receive buffer size (default = 1 MB) + { + int rcvbuf = 1024 * 1024; + if (attr_.rcvbuf > 0) rcvbuf = attr_.rcvbuf; + const asio::socket_base::receive_buffer_size recbufsize(rcvbuf); + asio::error_code ec; + m_socket.set_option(recbufsize, ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to set receive buffer size: " << ec.message() << std::endl; + } + } + + // join multicast group + AddMultiCastGroup(attr_.address.c_str()); + + // state successful creation + m_created = true; + } + + CUDPReceiverAsio::~CUDPReceiverAsio() + { + // close the socket + m_socket.close(); + + // state successful destruction + m_created = false; + } + + bool CUDPReceiverAsio::AddMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // join multicast group +#ifdef __linux__ + if (eCAL::UDP::IsUdpMulticastJoinAllIfEnabled()) + { + if (!IO::UDP::set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_JOIN_GROUP)) + { + return(false); + } + } + else +#endif + { + asio::error_code ec; + m_socket.set_option(asio::ip::multicast::join_group(asio::ip::make_address(ipaddr_)), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to join multicast group: " << ec.message() << std::endl; + return(false); + } + } + } + return(true); + } + + bool CUDPReceiverAsio::RemMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // Leave multicast group +#ifdef __linux__ + if (eCAL::UDP::IsUdpMulticastJoinAllIfEnabled()) + { + if (!IO::UDP::set_socket_mcast_group_option(m_socket.native_handle(), ipaddr_, MCAST_LEAVE_GROUP)) + { + return(false); + } + } + else +#endif + { + asio::error_code ec; + m_socket.set_option(asio::ip::multicast::leave_group(asio::ip::make_address(ipaddr_)), ec); + if (ec) + { + std::cerr << "CUDPReceiverAsio: Unable to leave multicast group: " << ec.message() << std::endl; + return(false); + } + } + } + return(true); + } + + size_t CUDPReceiverAsio::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_) + { + if (!m_created) return 0; + + size_t reclen(0); + m_socket.async_receive_from(asio::buffer(buf_, len_), m_sender_endpoint, + [&reclen](std::error_code ec, std::size_t length) + { + if (!ec) + { + reclen = length; + } + }); + + // run for timeout ms + RunIOContext(asio::chrono::milliseconds(timeout_)); + + // retrieve underlying raw socket information + if (address_ != nullptr) + { + if (m_sender_endpoint.address().is_v4()) + { + auto in4 = reinterpret_cast(m_sender_endpoint.data()); + address_->sin_addr = in4->sin_addr; + address_->sin_family = in4->sin_family; + address_->sin_port = in4->sin_port; + memset(&(address_->sin_zero), 0, 8); + } + else + { + std::cout << "CUDPReceiverAsio: ipv4 address conversion failed." << std::endl; + } + } + + return (reclen); + } + + void CUDPReceiverAsio::RunIOContext(const asio::chrono::steady_clock::duration& timeout) + { + // restart the io_context, as it may have been left in the "stopped" state by a previous operation + m_iocontext.restart(); + + // block until the asynchronous operation has completed, or timed out + m_iocontext.run_for(timeout); + + // stop the context if even the operation was not successful completed + if (!m_iocontext.stopped()) + { + // cancel the outstanding asynchronous operation + m_socket.cancel(); + + // run the io_context again until the operation completes + m_iocontext.run(); + } + } + } +} diff --git a/ecal/core/src/io/udp_receiver_asio.h b/src/core/src/io/udp/sendreceive/udp_receiver_asio.h similarity index 50% rename from ecal/core/src/io/udp_receiver_asio.h rename to src/core/src/io/udp/sendreceive/udp_receiver_asio.h index 44b4a0d..f173bfb 100644 --- a/ecal/core/src/io/udp_receiver_asio.h +++ b/src/core/src/io/udp/sendreceive/udp_receiver_asio.h @@ -19,7 +19,7 @@ #pragma once -#include +#include "udp_receiver.h" #ifdef _MSC_VER #pragma warning(push) @@ -30,28 +30,31 @@ #pragma warning(pop) #endif -namespace eCAL +namespace IO { - class CUDPReceiverAsio : public CUDPReceiverBase + namespace UDP { - public: - CUDPReceiverAsio(const SReceiverAttr& attr_); - - // this virtual function is called during construction/destruction, - // so, mark it as final to ensure that no derived classes override it. - bool AddMultiCastGroup(const char* ipaddr_) final override; - bool RemMultiCastGroup(const char* ipaddr_) override; - - size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; - - protected: - void RunIOContext(const asio::chrono::steady_clock::duration& timeout); - - bool m_created; - bool m_broadcast; - bool m_unicast; - asio::io_context m_iocontext; - asio::ip::udp::socket m_socket; - asio::ip::udp::endpoint m_sender_endpoint; - }; -} \ No newline at end of file + class CUDPReceiverAsio : public CUDPReceiverImpl + { + public: + explicit CUDPReceiverAsio(const SReceiverAttr& attr_); + ~CUDPReceiverAsio() override; + + // this virtual function is called during construction/destruction, + // so, mark it as final to ensure that no derived classes override it. + bool AddMultiCastGroup(const char* ipaddr_) final; + bool RemMultiCastGroup(const char* ipaddr_) override; + + size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_) override; + + protected: + void RunIOContext(const asio::chrono::steady_clock::duration& timeout); + + bool m_created; + bool m_broadcast; + asio::io_context m_iocontext; + asio::ip::udp::socket m_socket; + asio::ip::udp::endpoint m_sender_endpoint; + }; + } +} diff --git a/src/core/src/io/udp/sendreceive/udp_receiver_npcap.cpp b/src/core/src/io/udp/sendreceive/udp_receiver_npcap.cpp new file mode 100644 index 0000000..1e6fe31 --- /dev/null +++ b/src/core/src/io/udp/sendreceive/udp_receiver_npcap.cpp @@ -0,0 +1,130 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "udp_receiver_npcap.h" + +#include + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // Npcap based receiver class implementation + //////////////////////////////////////////////////////// + CUDPReceiverPcap::CUDPReceiverPcap(const SReceiverAttr& attr_) + : CUDPReceiverImpl(attr_) + , m_created(false) + , m_broadcast(attr_.broadcast) + { + // set receive buffer size (default = 1 MB) + int rcvbuf = 1024 * 1024; + if (attr_.rcvbuf > 0) + { + rcvbuf = attr_.rcvbuf; + } + if (!m_socket.setReceiveBufferSize(rcvbuf)) + { + std::cerr << "CUDPReceiverPcap: Unable to set receive buffer size." << std::endl; + } + + // bind socket + if (!m_socket.bind(Udpcap::HostAddress::Any(), static_cast(attr_.port))) + { + std::cerr << "CUDPReceiverPcap: Unable to bind socket." << std::endl; + return; + } + + // set loopback option + if (!m_broadcast) + { + m_socket.setMulticastLoopbackEnabled(attr_.loopback); + } + + // join multicast group + AddMultiCastGroup(attr_.address.c_str()); + + // state successful creation + m_created = true; + } + + CUDPReceiverPcap::~CUDPReceiverPcap() + { + // close socket + m_socket.close(); + + // state successful destruction + m_created = false; + } + + bool CUDPReceiverPcap::AddMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // join multicast group + if (!m_socket.joinMulticastGroup(Udpcap::HostAddress(ipaddr_))) + { + std::cerr << "CUDPReceiverPcap: Unable to join multicast group." << std::endl; + return(false); + } + } + return(true); + } + + bool CUDPReceiverPcap::RemMultiCastGroup(const char* ipaddr_) + { + if (!m_broadcast) + { + // leave multicast group + if (!m_socket.leaveMulticastGroup(Udpcap::HostAddress(ipaddr_))) + { + std::cerr << "CUDPReceiverPcap: Unable to leave multicast group." << std::endl; + return(false); + } + } + return(true); + } + + size_t CUDPReceiverPcap::Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ /* = nullptr */) + { + if (!m_created) return 0; + + size_t bytes_received; + if (address_) + { + Udpcap::HostAddress source_address; + uint16_t source_port; + bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_), &source_address, &source_port); + + if (bytes_received && source_address.isValid()) + { + address_->sin_addr.s_addr = source_address.toInt(); + address_->sin_family = AF_INET; + address_->sin_port = source_port; + memset(&(address_->sin_zero), 0, 8); + } + } + else + { + bytes_received = m_socket.receiveDatagram(buf_, len_, static_cast(timeout_)); + } + return bytes_received; + } + } +} diff --git a/ecal/core/src/io/udp_receiver_npcap.h b/src/core/src/io/udp/sendreceive/udp_receiver_npcap.h similarity index 52% rename from ecal/core/src/io/udp_receiver_npcap.h rename to src/core/src/io/udp/sendreceive/udp_receiver_npcap.h index 843e9ab..7727810 100644 --- a/ecal/core/src/io/udp_receiver_npcap.h +++ b/src/core/src/io/udp/sendreceive/udp_receiver_npcap.h @@ -20,31 +20,34 @@ #pragma once -#include +#include "udp_receiver.h" +#include "config/ecal_config_reader_hlp.h" -#include "ecal_config_reader_hlp.h" #include #include -namespace eCAL +namespace IO { - //////////////////////////////////////////////////////// - // Npcap based receiver class implementation - //////////////////////////////////////////////////////// - class CUDPReceiverPcap : public CUDPReceiverBase + namespace UDP { - public: - CUDPReceiverPcap(const SReceiverAttr& attr_); - - bool AddMultiCastGroup(const char* ipaddr_) override; - bool RemMultiCastGroup(const char* ipaddr_) override; - - size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; - - protected: - bool m_created; - bool m_unicast; - Udpcap::UdpcapSocket m_socket; - }; - -} \ No newline at end of file + //////////////////////////////////////////////////////// + // Npcap based receiver class implementation + //////////////////////////////////////////////////////// + class CUDPReceiverPcap : public CUDPReceiverImpl + { + public: + CUDPReceiverPcap(const SReceiverAttr& attr_); + ~CUDPReceiverPcap(); + + bool AddMultiCastGroup(const char* ipaddr_) override; + bool RemMultiCastGroup(const char* ipaddr_) override; + + size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; + + protected: + bool m_created; + bool m_broadcast; + Udpcap::UdpcapSocket m_socket; + }; + } +} diff --git a/src/core/src/io/udp/sendreceive/udp_sender.cpp b/src/core/src/io/udp/sendreceive/udp_sender.cpp new file mode 100644 index 0000000..ce59546 --- /dev/null +++ b/src/core/src/io/udp/sendreceive/udp_sender.cpp @@ -0,0 +1,132 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief UDP sender class +**/ + +#include "udp_sender.h" + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4834) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace IO +{ + namespace UDP + { + //////////////////////////////////////////////////////// + // udp sender class implementation + //////////////////////////////////////////////////////// + class CUDPSenderImpl + { + public: + explicit CUDPSenderImpl(const SSenderAttr& attr_); + size_t Send(const void* buf_, size_t len_, const char* ipaddr_ = nullptr); + + protected: + bool m_broadcast; + asio::io_context m_iocontext; + asio::ip::udp::endpoint m_endpoint; + asio::ip::udp::socket m_socket; + unsigned short m_port; + }; + + CUDPSenderImpl::CUDPSenderImpl(const SSenderAttr& attr_) : + m_broadcast(attr_.broadcast), + m_endpoint(asio::ip::make_address(attr_.address), static_cast(attr_.port)), + m_socket(m_iocontext, m_endpoint.protocol()), + m_port(static_cast(attr_.port)) + { + if (m_broadcast) + { + // set unicast packet TTL + const asio::ip::unicast::hops ttl(attr_.ttl); + asio::error_code ec; + m_socket.set_option(ttl, ec); + if (ec) + std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; + } + else + { + // set multicast packet TTL + { + const asio::ip::multicast::hops ttl(attr_.ttl); + asio::error_code ec; + m_socket.set_option(ttl, ec); + if (ec) + std::cerr << "CUDPSender: Setting TTL failed: " << ec.message() << std::endl; + } + + // set loopback option + { + const asio::ip::multicast::enable_loopback loopback(attr_.loopback); + asio::error_code ec; + m_socket.set_option(loopback, ec); + if (ec) + std::cerr << "CUDPSender: Error setting loopback option: " << ec.message() << std::endl; + } + } + + if (m_broadcast) + { + asio::error_code ec; + m_socket.set_option(asio::socket_base::broadcast(true), ec); + if (ec) + std::cerr << "CUDPSender: Setting broadcast mode failed: " << ec.message() << std::endl; + } + } + + size_t CUDPSenderImpl::Send(const void* buf_, const size_t len_, const char* ipaddr_) + { + const asio::socket_base::message_flags flags(0); + asio::error_code ec; + size_t sent(0); + if ((ipaddr_ != nullptr) && (ipaddr_[0] != '\0')) sent = m_socket.send_to(asio::buffer(buf_, len_), asio::ip::udp::endpoint(asio::ip::make_address(ipaddr_), m_port), flags, ec); + else sent = m_socket.send_to(asio::buffer(buf_, len_), m_endpoint, flags, ec); + if (ec) + { + std::cout << "CUDPSender::Send failed with: \'" << ec.message() << "\'" << std::endl; + return (0); + } + return(sent); + } + + //////////////////////////////////////////////////////// + // udp sender class + //////////////////////////////////////////////////////// + CUDPSender::CUDPSender(const SSenderAttr& attr_) + { + m_socket_impl = std::make_shared(attr_); + } + + size_t CUDPSender::Send(const void* buf_, const size_t len_, const char* ipaddr_) + { + if (!m_socket_impl) return(0); + return(m_socket_impl->Send(buf_, len_, ipaddr_)); + } + } +} diff --git a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_decoder.h b/src/core/src/io/udp/sendreceive/udp_sender.h similarity index 53% rename from lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_decoder.h rename to src/core/src/io/udp/sendreceive/udp_sender.h index 9bea5ea..ade2673 100644 --- a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_decoder.h +++ b/src/core/src/io/udp/sendreceive/udp_sender.h @@ -18,44 +18,50 @@ */ /** - * @brief eCALMonitor proto message decoding class + * @brief UDP sender class **/ #pragma once -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4100 4127 4146 4505 4800 4189 4592) // disable proto warnings +#include + +#ifdef ECAL_OS_WINDOWS +#include "win32/socket_os.h" #endif -#include -#ifdef _MSC_VER -#pragma warning(pop) + +#ifdef ECAL_OS_LINUX +#include +#include +#include #endif #include #include -#include -#include -namespace eCAL -{ -namespace protobuf +namespace IO { - class MessageFilter; - class MessageVisitor; - class CProtoDecoder + namespace UDP { - public: - CProtoDecoder(); + struct SSenderAttr + { + std::string address; + int port = 0; + int ttl = 0; + bool broadcast = false; + bool loopback = true; + int sndbuf = 1024 * 1024; + }; - bool ProcProtoMsg(const google::protobuf::Message& msg_, const std::string& name_="", const std::string& prefix_="", bool is_array_=false, size_t index_=0); + class CUDPSenderImpl; - void SetVisitor(std::shared_ptr visitor_) { visitor = std::move(visitor_); }; + class CUDPSender + { + public: + explicit CUDPSender(const SSenderAttr& attr_); + size_t Send(const void* buf_, size_t len_, const char* ipaddr_ = nullptr); - private: - - std::shared_ptr visitor; - }; - -} + protected: + std::shared_ptr m_socket_impl; + }; + } } diff --git a/ecal/core/src/ecal_win_socket.h b/src/core/src/io/udp/sendreceive/win32/socket_os.h similarity index 100% rename from ecal/core/src/ecal_win_socket.h rename to src/core/src/io/udp/sendreceive/win32/socket_os.h diff --git a/ecal/core/src/ecal_log.cpp b/src/core/src/logging/ecal_log.cpp similarity index 59% rename from ecal/core/src/ecal_log.cpp rename to src/core/src/logging/ecal_log.cpp index 353208c..530e993 100644 --- a/ecal/core/src/ecal_log.cpp +++ b/src/core/src/logging/ecal_log.cpp @@ -25,9 +25,6 @@ #include "ecal_log_impl.h" -#include -#include - namespace eCAL { namespace Logging @@ -39,7 +36,7 @@ namespace eCAL **/ void SetLogLevel(const eCAL_Logging_eLogLevel level_) { - if(g_log()) g_log()->SetLogLevel(level_); + if(g_log() != nullptr) g_log()->SetLogLevel(level_); } /** @@ -49,8 +46,8 @@ namespace eCAL **/ eCAL_Logging_eLogLevel GetLogLevel() { - if(g_log()) return(g_log()->GetLogLevel()); - else return(log_level_none); + if(g_log() != nullptr) return(g_log()->GetLogLevel()); + else return(log_level_none); } /** @@ -60,46 +57,20 @@ namespace eCAL **/ void Log(const std::string& msg_) { - if(g_log()) g_log()->Log(msg_); - } - - /** - * @brief Mark the start of the user core process. - **/ - void StartCoreTimer() - { - if(g_log()) g_log()->StartCoreTimer(); - } - - /** - * @brief Mark the stop of the user core process. - **/ - void StopCoreTimer() - { - if(g_log()) g_log()->StopCoreTimer(); + if(g_log() != nullptr) g_log()->Log(msg_); } /** - * @brief Set the current measured core time in s (for user implemented measuring). - **/ - void SetCoreTime(const double time_) - { - if(g_log()) g_log()->SetCoreTime(std::chrono::duration(time_)); - } - - /** - * @brief Returns the current measured core time in s. + * @brief Get logging as serialized protobuf string. + * + * @param [out] log_ String to store the logging information. + * + * @return Monitoring buffer length or zero if failed. **/ - double GetCoreTime() + int GetLogging(std::string& log_) { - if(g_log()) - { - return(g_log()->GetCoreTime().count()); - } - else - { - return(0.0); - } + if (g_log() != nullptr) g_log()->GetLogging(log_); + return static_cast(log_.size()); } } } diff --git a/ecal/core/src/ecal_log_impl.cpp b/src/core/src/logging/ecal_log_impl.cpp similarity index 54% rename from ecal/core/src/ecal_log_impl.cpp rename to src/core/src/logging/ecal_log_impl.cpp index cae7028..3857f19 100644 --- a/ecal/core/src/ecal_log_impl.cpp +++ b/src/core/src/logging/ecal_log_impl.cpp @@ -25,55 +25,42 @@ #include #include -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" - -#include "ecal_def.h" - #include "ecal_log_impl.h" - -#include "io/udp_configurations.h" +#include "io/udp/ecal_udp_configurations.h" +#include "serialization/ecal_serialize_logging.h" #include -#include +#include #include #include -#include #include #include -#include -#include #include #include #include -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - #ifdef ECAL_OS_WINDOWS #include "ecal_win_main.h" #include -static bool isDirectory(const std::string& path_) +namespace { - if (path_.empty()) return false; + bool isDirectory(const std::string& path_) + { + if (path_.empty()) return false; - return EcalUtils::Filesystem::IsDir(path_, EcalUtils::Filesystem::Current); -} + return EcalUtils::Filesystem::IsDir(path_, EcalUtils::Filesystem::Current); + } -static std::string get_time_str() -{ - auto t = std::time(nullptr); - auto tm = *std::localtime(&t); - std::stringstream tstream; - tstream << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S"); - return(tstream.str()); + std::string get_time_str() + { + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + std::stringstream tstream; + tstream << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S"); + return(tstream.str()); + } } #endif @@ -110,16 +97,13 @@ namespace eCAL { CLog::CLog() : m_created(false), - m_udp_sender(new CUDPSender()), 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_core_time_start(std::chrono::nanoseconds(0)) + m_filter_mask_udp(log_level_info | log_level_warning | log_level_error | log_level_fatal | log_level_debug1 | log_level_debug2) { - m_core_time = std::chrono::duration(-1.0); } CLog::~CLog() @@ -140,41 +124,44 @@ namespace eCAL m_filter_mask_udp = Config::GetUdpLogFilter(); // create log file - if(m_filter_mask_file) + if(m_filter_mask_file != 0) { // check ECAL_DATA - std::string ecal_log_path = Util::GeteCALLogPath(); + const std::string ecal_log_path = Util::GeteCALLogPath(); if (!isDirectory(ecal_log_path)) return; - std::string tstring = get_time_str(); + const std::string tstring = get_time_str(); 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"); } - // create log udp sender - if(m_filter_mask_udp) + if(m_filter_mask_udp != 0) { - SSenderAttr attr; - bool local_only = !Config::IsNetworkEnabled(); - // for local only communication we switch to local broadcasting to bypass vpn's or firewalls - if (local_only) - { - attr.broadcast = true; - } - else - { - attr.broadcast = false; - } - attr.ipaddr = UDP::GetLoggingMulticastAddress(); - attr.port = Config::GetUdpMulticastPort() + NET_UDP_MULTICAST_PORT_LOG_OFF; - attr.loopback = true; - attr.ttl = Config::GetUdpMulticastTtl(); - attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); - - m_udp_sender->Create(attr); + // set logging send network attributes + IO::UDP::SSenderAttr attr; + attr.address = UDP::GetLoggingAddress(); + attr.port = UDP::GetLoggingPort(); + attr.ttl = UDP::GetMulticastTtl(); + attr.broadcast = UDP::IsBroadcast(); + attr.loopback = true; + attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); + + // create udp logging sender + m_udp_logging_sender = std::make_unique(attr); } + // set logging receive network attributes + IO::UDP::SReceiverAttr attr; + attr.address = UDP::GetLoggingAddress(); + attr.port = UDP::GetLoggingPort(); + attr.broadcast = UDP::IsBroadcast(); + attr.loopback = true; + attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); + + // 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)); + m_created = true; } @@ -182,50 +169,48 @@ namespace eCAL { if(!m_created) return; - std::lock_guard lock(m_log_sync); + const std::lock_guard lock(m_log_sync); - m_udp_sender->Destroy(); + m_udp_logging_sender.reset(); - if(m_logfile) fclose(m_logfile); + if(m_logfile != nullptr) fclose(m_logfile); m_logfile = nullptr; m_created = false; } void CLog::SetLogLevel(const eCAL_Logging_eLogLevel level_) - { - std::lock_guard lock(m_log_sync); + { + const std::lock_guard lock(m_log_sync); m_level = level_; - }; + } eCAL_Logging_eLogLevel CLog::GetLogLevel() - { - std::lock_guard lock(m_log_sync); + { + const std::lock_guard lock(m_log_sync); return(m_level); - }; + } void CLog::Log(const eCAL_Logging_eLogLevel level_, const std::string& msg_) { - std::lock_guard lock(m_log_sync); + const std::lock_guard lock(m_log_sync); if(!m_created) return; if(msg_.empty()) return; - eCAL_Logging_Filter log_con = level_ & m_filter_mask_con; - eCAL_Logging_Filter log_file = level_ & m_filter_mask_file; - eCAL_Logging_Filter log_udp = level_ & m_filter_mask_udp; - if(!(log_con | log_file | log_udp)) 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; + if((log_con | log_file | log_udp) == 0) return; - static eCAL::pb::LogMessage ecal_msg; - static std::string ecal_msg_s; - auto log_time = eCAL::Time::ecal_clock::now(); + auto log_time = eCAL::Time::ecal_clock::now(); - if(log_con) + if(log_con != 0) { std::cout << msg_ << std::endl; } - if(log_file && m_logfile) + if((log_file != 0) && (m_logfile != nullptr)) { std::stringstream msg_stream; msg_stream << std::chrono::duration_cast(log_time.time_since_epoch()).count(); @@ -274,22 +259,22 @@ namespace eCAL fflush(m_logfile); } - if(log_udp && m_udp_sender) + if((log_udp != 0) && m_udp_logging_sender) { - ecal_msg.Clear(); - ecal_msg.set_time(std::chrono::duration_cast(log_time.time_since_epoch()).count()); - ecal_msg.set_hname(m_hname); - ecal_msg.set_pid(m_pid); - ecal_msg.set_pname(m_pname); - ecal_msg.set_uname(eCAL::Process::GetUnitName()); - ecal_msg.set_level(level_); - ecal_msg.set_content(msg_); - - ecal_msg_s = ecal_msg.SerializeAsString(); - if(!ecal_msg_s.empty()) - { - m_udp_sender->Send((void*)ecal_msg_s.data(), ecal_msg_s.size()); - } + // set up log message + Logging::LogMessage 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); } } @@ -298,31 +283,38 @@ namespace eCAL Log(m_level, msg_); } - void CLog::StartCoreTimer() + void CLog::GetLogging(std::string& log_msg_list_string_) { - std::lock_guard lock(m_log_sync); - - m_core_time_start = std::chrono::steady_clock::now(); + // clear target list string + log_msg_list_string_.clear(); + + // serialize message list to target list string + SerializeToBuffer(m_log_msglist, log_msg_list_string_); + + // empty message list + m_log_msglist.log_messages.clear(); } - void CLog::StopCoreTimer() + bool CLog::HasSample(const std::string& sample_name_) { - std::lock_guard lock(m_log_sync); - - m_core_time = std::chrono::steady_clock::now() - m_core_time_start; + return (sample_name_ == "_log_message_"); } - void CLog::SetCoreTime(const std::chrono::duration& time_) + bool CLog::ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_) { - std::lock_guard lock(m_log_sync); - - m_core_time = time_; - } - - std::chrono::duration CLog::GetCoreTime() - { - std::lock_guard lock(m_log_sync); - - return(m_core_time); + // TODO: Limit maximum size of collected log messages ! + Logging::LogMessage log_message; + if (DeserializeFromBuffer(serialized_sample_data_, serialized_sample_size_, log_message)) + { + // 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()) + { + const std::lock_guard lock(m_log_msglist_sync); + m_log_msglist.log_messages.emplace_back(log_message); + } + return true; + } + return false; } } diff --git a/ecal/core/src/ecal_log_impl.h b/src/core/src/logging/ecal_log_impl.h similarity index 57% rename from ecal/core/src/ecal_log_impl.h rename to src/core/src/logging/ecal_log_impl.h index cce4b93..bbed11d 100644 --- a/ecal/core/src/ecal_log_impl.h +++ b/src/core/src/logging/ecal_log_impl.h @@ -23,7 +23,9 @@ #pragma once -#include "io/udp_sender.h" +#include "io/udp/ecal_udp_sample_receiver.h" +#include "io/udp/ecal_udp_sample_sender.h" +#include "serialization/ecal_struct_logging.h" #include #include @@ -33,9 +35,10 @@ #include "ecal_global_accessors.h" #include +#include #include #include -#include +#include namespace eCAL { @@ -67,7 +70,7 @@ namespace eCAL * * @param level_ The level. **/ - void SetLogLevel(const eCAL_Logging_eLogLevel level_); + void SetLogLevel(eCAL_Logging_eLogLevel level_); /** * @brief Set the current log level. @@ -82,7 +85,7 @@ namespace eCAL * @param level_ The level. * @param msg_ The message. **/ - void Log(const eCAL_Logging_eLogLevel level_, const std::string& msg_); + void Log(eCAL_Logging_eLogLevel level_, const std::string& msg_); /** * @brief Log a message (with the current log level). @@ -91,55 +94,38 @@ namespace eCAL **/ void Log(const std::string& msg_); - /** - * @brief Mark the start of the user core process. - * - **/ - void StartCoreTimer(); - - /** - * @brief Mark the stop of the user core process. - * - **/ - void StopCoreTimer(); - - /** - * @brief Set the current measured core time in s (for user implemented measuring). - * - **/ - void SetCoreTime(const std::chrono::duration& time_); - - /** - * @brief Returns the current measured core time in s. - * - **/ - std::chrono::duration GetCoreTime(); + void GetLogging(std::string& log_msg_list_string_); private: - char ParseLogLevel(const std::string& filter_); + bool HasSample(const std::string& sample_name_); + bool ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_); CLog(const CLog&); // prevent copy-construction CLog& operator=(const CLog&); // prevent assignment - std::mutex m_log_sync; + std::mutex m_log_sync; + std::vector m_log_message_vec; - std::atomic m_created; - std::unique_ptr m_udp_sender; + std::atomic m_created; + std::unique_ptr m_udp_logging_sender; - std::string m_hname; - int m_pid; - std::string m_pname; + // log messages + std::mutex m_log_msglist_sync; + Logging::LogMessageList m_log_msglist; - std::string m_logfile_name; - FILE* m_logfile; + // udp logging receiver + std::shared_ptr m_log_receiver; - 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; + std::string m_hname; + int m_pid; + std::string m_pname; - std::chrono::duration m_core_time; + std::string m_logfile_name; + FILE* m_logfile; - std::chrono::steady_clock::time_point m_core_time_start; + 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; }; } diff --git a/ecal/core/src/mon/ecal_monitoring_def.cpp b/src/core/src/monitoring/ecal_monitoring_def.cpp similarity index 53% rename from ecal/core/src/mon/ecal_monitoring_def.cpp rename to src/core/src/monitoring/ecal_monitoring_def.cpp index 8a99c73..8c7266b 100644 --- a/ecal/core/src/mon/ecal_monitoring_def.cpp +++ b/src/core/src/monitoring/ecal_monitoring_def.cpp @@ -21,6 +21,8 @@ * @brief Global monitoring class **/ +#include + #include "ecal_monitoring_def.h" #include "ecal_monitoring_impl.h" #include "ecal_global_accessors.h" @@ -29,13 +31,12 @@ namespace eCAL { CMonitoring::CMonitoring() { - m_monitoring_impl = new CMonitoringImpl; + m_monitoring_impl = std::make_unique(); } CMonitoring::~CMonitoring() { - delete m_monitoring_impl; - m_monitoring_impl = nullptr; + m_monitoring_impl.reset(); } void CMonitoring::Create() @@ -63,30 +64,14 @@ namespace eCAL m_monitoring_impl->SetFilterState(state_); } - void CMonitoring::Monitor(eCAL::pb::Monitoring& monitoring_) - { - m_monitoring_impl->GetMonitoringMsg(monitoring_); - } - - void CMonitoring::Monitor(eCAL::pb::Logging& logging_) - { - m_monitoring_impl->GetLoggingMsg(logging_); - } - - int CMonitoring::PubMonitoring(bool state_, std::string& name_) - { - return(m_monitoring_impl->PubMonitoring(state_, name_)); - } - - int CMonitoring::PubLogging(bool state_, std::string& name_) + void CMonitoring::GetMonitoring(std::string& monitoring_, unsigned int entities_) { - return(m_monitoring_impl->PubLogging(state_, name_)); + m_monitoring_impl->GetMonitoring(monitoring_, entities_); } - size_t CMonitoring::ApplySample(const eCAL::pb::Sample & ecal_sample_) + void CMonitoring::GetMonitoring(eCAL::Monitoring::SMonitoring& monitoring_, unsigned int entities_) { - if(m_monitoring_impl) return m_monitoring_impl->ApplySample(ecal_sample_, eCAL::pb::eTLayerType::tl_none); - return 0; + m_monitoring_impl->GetMonitoring(monitoring_, entities_); } namespace Monitoring @@ -96,50 +81,37 @@ namespace eCAL //////////////////////////////////////////////////////// int SetExclFilter(const std::string& filter_) { - if (g_monitoring()) g_monitoring()->SetExclFilter(filter_); + if (g_monitoring() != nullptr) g_monitoring()->SetExclFilter(filter_); return(0); } int SetInclFilter(const std::string& filter_) { - if (g_monitoring()) g_monitoring()->SetInclFilter(filter_); + if (g_monitoring() != nullptr) g_monitoring()->SetInclFilter(filter_); return(0); } int SetFilterState(const bool state_) { - if (g_monitoring()) g_monitoring()->SetFilterState(state_); + if (g_monitoring() != nullptr) g_monitoring()->SetFilterState(state_); return(0); } - int GetMonitoring(std::string& mon_) + int GetMonitoring(std::string& mon_, unsigned int entities_) { - eCAL::pb::Monitoring monitoring; - if (g_monitoring()) g_monitoring()->Monitor(monitoring); - - mon_ = monitoring.SerializeAsString(); + mon_.clear(); + if (g_monitoring() != nullptr) g_monitoring()->GetMonitoring(mon_, entities_); return((int)mon_.size()); } - int GetLogging(std::string& log_) - { - eCAL::pb::Logging logging; - if (g_monitoring()) g_monitoring()->Monitor(logging); - - log_ = logging.SerializeAsString(); - return((int)log_.size()); - } - - int PubMonitoring(bool state_, std::string name_ /* = "ecal.monitoring"*/) + int GetMonitoring(SMonitoring& mon_, unsigned int entities_) { - if (g_monitoring()) return(g_monitoring()->PubMonitoring(state_, name_)); - return -1; - } - - int PubLogging(bool state_, std::string name_ /* = "ecal.logging"*/) - { - if (g_monitoring()) return(g_monitoring()->PubLogging(state_, name_)); - return -1; + if (g_monitoring() != nullptr) + { + g_monitoring()->GetMonitoring(mon_, entities_); + return(static_cast(mon_.processes.size() + mon_.publisher.size() + mon_.subscriber.size() + mon_.server.size() + mon_.clients.size())); + } + return(0); } } } diff --git a/ecal/core/src/mon/ecal_monitoring_def.h b/src/core/src/monitoring/ecal_monitoring_def.h similarity index 74% rename from ecal/core/src/mon/ecal_monitoring_def.h rename to src/core/src/monitoring/ecal_monitoring_def.h index c3a176d..725938b 100644 --- a/ecal/core/src/mon/ecal_monitoring_def.h +++ b/src/core/src/monitoring/ecal_monitoring_def.h @@ -23,16 +23,10 @@ #pragma once -#include +#include -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif +#include +#include namespace eCAL { @@ -53,16 +47,11 @@ namespace eCAL void SetInclFilter(const std::string& filter_); void SetFilterState(bool state_); - void Monitor(eCAL::pb::Monitoring& monitoring_); - void Monitor(eCAL::pb::Logging& logging_); - - int PubMonitoring(bool state_, std::string& name_); - int PubLogging(bool state_, std::string& name_); - - size_t ApplySample(const eCAL::pb::Sample& ecal_sample_); + void GetMonitoring(std::string& monitoring_, unsigned int entities_ = Monitoring::Entity::All); + void GetMonitoring(eCAL::Monitoring::SMonitoring& monitoring_, unsigned int entities_ = Monitoring::Entity::All); protected: - CMonitoringImpl* m_monitoring_impl; + std::unique_ptr m_monitoring_impl; private: CMonitoring(const CMonitoring&); // prevent copy-construction diff --git a/src/core/src/monitoring/ecal_monitoring_impl.cpp b/src/core/src/monitoring/ecal_monitoring_impl.cpp new file mode 100644 index 0000000..bcd438f --- /dev/null +++ b/src/core/src/monitoring/ecal_monitoring_impl.cpp @@ -0,0 +1,784 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief Global monitoring class (implementation) +**/ + +#include +#include + +#include "io/udp/ecal_udp_configurations.h" +#include "config/ecal_config_reader_hlp.h" +#include "ecal_monitoring_impl.h" + +#include + +#include "registration/ecal_registration_receiver.h" +#include "serialization/ecal_serialize_monitoring.h" + + +namespace eCAL +{ + //////////////////////////////////////// + // Monitoring Implementation + //////////////////////////////////////// + CMonitoringImpl::CMonitoringImpl() : + 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())) + { + } + + void CMonitoringImpl::Create() + { + if (m_init) return; + + // get name of this host + m_host_name = Process::GetHostName(); + + // utilize registration receiver to enrich monitor information + g_registration_receiver()->SetCustomApplySampleCallback([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); + + m_init = true; + } + + void CMonitoringImpl::Destroy() + { + g_registration_receiver()->RemCustomApplySampleCallback(); + m_init = false; + } + + void CMonitoringImpl::SetExclFilter(const std::string& filter_) + { + m_topic_filter_excl_s = filter_; + } + + void CMonitoringImpl::SetInclFilter(const std::string& filter_) + { + m_topic_filter_incl_s = filter_; + } + + void CMonitoringImpl::SetFilterState(bool state_) + { + 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); + } + } + 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(); + } + } + } + + bool CMonitoringImpl::ApplySample(const Registration::Sample& sample_, eTLayerType /*layer_*/) + { + switch (sample_.cmd_type) + { + case bct_none: + case bct_set_sample: + break; + case bct_reg_process: + { + // register process + RegisterProcess(sample_); + } + break; + case bct_unreg_process: + { + // unregister process + UnregisterProcess(sample_); + } + break; + case bct_reg_service: + { + // register service + RegisterServer(sample_); + } + break; + case bct_unreg_service: + { + // unregister service + UnregisterServer(sample_); + } + break; + case bct_reg_client: + { + // register client + RegisterClient(sample_); + } + break; + case bct_unreg_client: + { + // unregister client + UnregisterClient(sample_); + } + break; + case bct_reg_publisher: + { + // register publisher + RegisterTopic(sample_, CMonitoringImpl::publisher); + } + break; + case bct_unreg_publisher: + { + // unregister publisher + UnregisterTopic(sample_, CMonitoringImpl::publisher); + } + break; + case bct_reg_subscriber: + { + // register subscriber + RegisterTopic(sample_, CMonitoringImpl::subscriber); + } + break; + case bct_unreg_subscriber: + { + // unregister subscriber + UnregisterTopic(sample_, CMonitoringImpl::subscriber); + } + break; + default: + { + Logging::Log(log_level_debug1, "CMonitoringImpl::ApplySample : unknown sample type"); + } + break; + } + + return true; + } + + bool CMonitoringImpl::RegisterTopic(const Registration::Sample& sample_, enum ePubSub pubsub_type_) + { + const auto& sample_topic = sample_.topic; + const int process_id = sample_topic.pid; + const std::string& topic_name = sample_topic.tname; + const int32_t topic_size = sample_topic.tsize; + bool topic_tlayer_ecal_udp_mc(false); + bool topic_tlayer_ecal_shm(false); + bool topic_tlayer_ecal_tcp(false); + for (const auto& layer : sample_topic.tlayer) + { + topic_tlayer_ecal_udp_mc |= (layer.type == tl_ecal_udp_mc) && layer.confirmed; + topic_tlayer_ecal_shm |= (layer.type == tl_ecal_shm) && layer.confirmed; + topic_tlayer_ecal_tcp |= (layer.type == tl_ecal_tcp) && layer.confirmed; + } + 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; + + // check blacklist topic filter + { + 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); + } + } + + // check whitelist topic filter + bool is_topic_in_filter(false); + { + 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; + } + } + } + + if (!is_topic_in_filter) return (false); + + ///////////////////////////////// + // register in topic map + ///////////////////////////////// + STopicMonMap* pTopicMap = GetMap(pubsub_type_); + if (pTopicMap != nullptr) + { + // acquire access + const std::lock_guard lock(pTopicMap->sync); + + // common infos + const std::string& host_name = sample_topic.hname; + const std::string& host_group_name = sample_topic.hgname; + const std::string& process_name = sample_topic.pname; + const std::string& unit_name = sample_topic.uname; + const std::string& topic_id = sample_topic.tid; + std::string direction; + switch (pubsub_type_) + { + case publisher: + direction = "publisher"; + break; + case subscriber: + direction = "subscriber"; + break; + default: + break; + } + std::string topic_datatype_encoding = sample_topic.tdatatype.encoding; + std::string topic_datatype_name = sample_topic.tdatatype.name; + std::string topic_datatype_desc = sample_topic.tdatatype.desc; + auto attr = sample_topic.attr; + + // try to get topic info + const std::string topic_name_id = topic_name + topic_id; + Monitoring::STopicMon& TopicInfo = (*pTopicMap->map)[topic_name_id]; + + // set static content + TopicInfo.hname = host_name; + TopicInfo.hgname = host_group_name; + TopicInfo.pid = process_id; + TopicInfo.pname = process_name; + TopicInfo.uname = unit_name; + TopicInfo.tname = topic_name; + TopicInfo.direction = direction; + TopicInfo.tid = topic_id; + + // update flexible content + TopicInfo.rclock++; + TopicInfo.tdatatype.encoding = std::move(topic_datatype_encoding); + TopicInfo.tdatatype.name = std::move(topic_datatype_name); + TopicInfo.tdatatype.descriptor = std::move(topic_datatype_desc); + + // attributes + TopicInfo.attr = std::map{attr.begin(), attr.end()}; + + // layer + TopicInfo.tlayer.clear(); + // tlayer udp_mc + { + eCAL::Monitoring::TLayer tlayer; + tlayer.type = eCAL::Monitoring::tl_ecal_udp_mc; + tlayer.confirmed = topic_tlayer_ecal_udp_mc; + TopicInfo.tlayer.push_back(tlayer); + } + // tlayer shm + { + eCAL::Monitoring::TLayer tlayer; + tlayer.type = eCAL::Monitoring::tl_ecal_shm; + tlayer.confirmed = topic_tlayer_ecal_shm; + TopicInfo.tlayer.push_back(tlayer); + } + // tlayer tcp + { + eCAL::Monitoring::TLayer tlayer; + tlayer.type = eCAL::Monitoring::tl_ecal_tcp; + tlayer.confirmed = topic_tlayer_ecal_tcp; + TopicInfo.tlayer.push_back(tlayer); + } + + TopicInfo.tsize = static_cast(topic_size); + TopicInfo.connections_loc = static_cast(connections_loc); + TopicInfo.connections_ext = static_cast(connections_ext); + TopicInfo.did = did; + TopicInfo.dclock = dclock; + TopicInfo.message_drops = message_drops; + TopicInfo.dfreq = dfreq; + } + + return(true); + } + + bool CMonitoringImpl::UnregisterTopic(const Registration::Sample& sample_, enum ePubSub pubsub_type_) + { + const auto& sample_topic = sample_.topic; + const std::string& topic_name = sample_topic.tname; + const std::string& topic_id = sample_topic.tid; + + // unregister from topic map + STopicMonMap* pTopicMap = GetMap(pubsub_type_); + if (pTopicMap != nullptr) + { + // acquire access + const std::lock_guard lock(pTopicMap->sync); + + // remove topic info + const std::string topic_name_id = topic_name + topic_id; + pTopicMap->map->erase(topic_name_id); + } + + return(true); + } + + bool CMonitoringImpl::RegisterProcess(const Registration::Sample& sample_) + { + const auto& sample_process = sample_.process; + const std::string& host_name = sample_process.hname; + const std::string& host_group_name = sample_process.hgname; + const std::string& process_name = sample_process.pname; + const int process_id = sample_process.pid; + const std::string& process_param = sample_process.pparam; + const std::string& unit_name = sample_process.uname; + const long long process_datawrite = sample_process.datawrite; + const long long process_dataread = sample_process.dataread; + const auto& sample_process_state = sample_process.state; + const int process_state_severity = sample_process_state.severity; + const int process_state_severity_level = sample_process_state.severity_level; + const std::string& process_state_info = sample_process_state.info; + const int process_tsync_state = sample_process.tsync_state; + const std::string& process_tsync_mod_name = sample_process.tsync_mod_name; + const int component_init_state = sample_process.component_init_state; + const std::string& component_init_info = sample_process.component_init_info; + const std::string& ecal_runtime_version = sample_process.ecal_runtime_version; + + // create map key + const std::string process_name_id = process_name + std::to_string(process_id); + + // acquire access + const std::lock_guard lock(m_process_map.sync); + + // try to get process info + Monitoring::SProcessMon& ProcessInfo = (*m_process_map.map)[process_name_id]; + + // set static content + ProcessInfo.hname = host_name; + ProcessInfo.hgname = host_group_name; + ProcessInfo.pname = process_name; + ProcessInfo.uname = unit_name; + ProcessInfo.pid = process_id; + ProcessInfo.pparam = process_param; + + // update flexible content + ProcessInfo.rclock++; + ProcessInfo.datawrite = process_datawrite; + ProcessInfo.dataread = process_dataread; + ProcessInfo.state_severity = process_state_severity; + ProcessInfo.state_severity_level = process_state_severity_level; + ProcessInfo.state_info = process_state_info; + ProcessInfo.tsync_state = process_tsync_state; + ProcessInfo.tsync_mod_name = process_tsync_mod_name; + ProcessInfo.component_init_state = component_init_state; + ProcessInfo.component_init_info = component_init_info; + ProcessInfo.ecal_runtime_version = ecal_runtime_version; + + return(true); + } + + bool CMonitoringImpl::UnregisterProcess(const Registration::Sample& sample_) + { + const auto& sample_process = sample_.process; + const std::string& process_name = sample_process.pname; + const int process_id = sample_process.pid; + + // create map key + const std::string process_name_id = process_name + std::to_string(process_id); + + // acquire access + const std::lock_guard lock(m_process_map.sync); + + // remove process info + m_process_map.map->erase(process_name_id); + + return(true); + } + + bool CMonitoringImpl::RegisterServer(const Registration::Sample& sample_) + { + const auto& sample_service = sample_.service; + const std::string& host_name = sample_service.hname; + const std::string& service_name = sample_service.sname; + const std::string& service_id = sample_service.sid; + const std::string& process_name = sample_service.pname; + const std::string& unit_name = sample_service.uname; + const int32_t process_id = sample_service.pid; + const uint32_t tcp_port_v0 = sample_service.tcp_port_v0; + const uint32_t tcp_port_v1 = sample_service.tcp_port_v1; + + // create map key + const std::string service_name_id = service_name + service_id + std::to_string(process_id); + + // acquire access + const std::lock_guard lock(m_server_map.sync); + + // try to get service info + Monitoring::SServerMon& ServerInfo = (*m_server_map.map)[service_name_id]; + + // set static content + ServerInfo.hname = host_name; + ServerInfo.sname = service_name; + ServerInfo.sid = service_id; + ServerInfo.pname = process_name; + ServerInfo.uname = unit_name; + ServerInfo.pid = process_id; + ServerInfo.tcp_port_v0 = tcp_port_v0; + ServerInfo.tcp_port_v1 = tcp_port_v1; + + // update flexible content + ServerInfo.rclock++; + ServerInfo.methods.clear(); + for (int i = 0; i < sample_.service.methods.size(); ++i) + { + struct Monitoring::SMethodMon method; + auto sample_service_methods = sample_.service.methods[i]; + method.mname = sample_service_methods.mname; + method.req_type = sample_service_methods.req_type; + method.req_desc = sample_service_methods.req_desc; + method.resp_type = sample_service_methods.resp_type; + method.resp_desc = sample_service_methods.resp_desc; + method.call_count = sample_service_methods.call_count; + ServerInfo.methods.push_back(method); + } + + return(true); + } + + bool CMonitoringImpl::UnregisterServer(const Registration::Sample& sample_) + { + const auto& sample_service = sample_.service; + const std::string& service_name = sample_service.sname; + const std::string& service_id = sample_service.sid; + const int process_id = sample_service.pid; + + // create map key + const std::string service_name_id = service_name + service_id + std::to_string(process_id); + + // acquire access + const std::lock_guard lock(m_server_map.sync); + + // remove service info + m_server_map.map->erase(service_name_id); + + return(true); + } + + bool CMonitoringImpl::RegisterClient(const Registration::Sample& sample_) + { + const auto& sample_client = sample_.client; + const std::string& host_name = sample_client.hname; + const std::string& service_name = sample_client.sname; + const std::string& service_id = sample_client.sid; + const std::string& process_name = sample_client.pname; + const std::string& unit_name = sample_client.uname; + const int process_id = sample_client.pid; + + // create map key + const std::string service_name_id = service_name + service_id + std::to_string(process_id); + + // acquire access + const std::lock_guard lock(m_clients_map.sync); + + // try to get service info + Monitoring::SClientMon& ClientInfo = (*m_clients_map.map)[service_name_id]; + + // set static content + ClientInfo.hname = host_name; + ClientInfo.sname = service_name; + ClientInfo.sid = service_id; + ClientInfo.pname = process_name; + ClientInfo.uname = unit_name; + ClientInfo.pid = process_id; + + // update flexible content + ClientInfo.rclock++; + + return(true); + } + + bool CMonitoringImpl::UnregisterClient(const Registration::Sample& sample_) + { + const auto& sample_client = sample_.client; + const std::string& service_name = sample_client.sname; + const std::string& service_id = sample_client.sid; + const int process_id = sample_client.pid; + + // create map key + const std::string service_name_id = service_name + service_id + std::to_string(process_id); + + // acquire access + const std::lock_guard lock(m_clients_map.sync); + + // remove service info + m_clients_map.map->erase(service_name_id); + + return(true); + } + + CMonitoringImpl::STopicMonMap* CMonitoringImpl::GetMap(enum ePubSub pubsub_type_) + { + STopicMonMap* pHostMap = nullptr; + switch (pubsub_type_) + { + case publisher: + pHostMap = &m_publisher_map; + break; + case subscriber: + pHostMap = &m_subscriber_map; + break; + } + return(pHostMap); + } + + void CMonitoringImpl::GetMonitoring(std::string& monitoring_, unsigned int entities_) + { + // create monitoring struct + Monitoring::SMonitoring monitoring; + + if ((entities_ & Monitoring::Entity::Process) != 0u) + { + MonitorProcs(monitoring); + } + + if ((entities_ & Monitoring::Entity::Publisher) != 0u) + { + MonitorTopics(m_publisher_map, monitoring, "publisher"); + } + + if ((entities_ & Monitoring::Entity::Subscriber) != 0u) + { + MonitorTopics(m_subscriber_map, monitoring, "subscriber"); + } + + if ((entities_ & Monitoring::Entity::Server) != 0u) + { + MonitorServer(monitoring); + } + + if ((entities_ & Monitoring::Entity::Client) != 0u) + { + MonitorClients(monitoring); + } + + // serialize struct to target string + SerializeToBuffer(monitoring, monitoring_); + } + + void CMonitoringImpl::GetMonitoring(Monitoring::SMonitoring& monitoring_, unsigned int entities_) + { + if ((entities_ & Monitoring::Entity::Process) != 0u) + { + // clear target + monitoring_.processes.clear(); + + // lock map + const std::lock_guard lock(m_process_map.sync); + + // reserve target + monitoring_.processes.reserve(m_process_map.map->size()); + + // iterate map + m_process_map.map->remove_deprecated(); + for (const auto& process : (*m_process_map.map)) + { + monitoring_.processes.emplace_back(process.second); + } + } + + if ((entities_ & Monitoring::Entity::Publisher) != 0u) + { + // clear target + monitoring_.publisher.clear(); + + // lock map + const std::lock_guard lock(m_publisher_map.sync); + + // reserve target + monitoring_.publisher.reserve(m_publisher_map.map->size()); + + // iterate map + m_publisher_map.map->remove_deprecated(); + for (const auto& publisher : (*m_publisher_map.map)) + { + monitoring_.publisher.emplace_back(publisher.second); + } + } + + if ((entities_ & Monitoring::Entity::Subscriber) != 0u) + { + // clear target + monitoring_.subscriber.clear(); + + // lock map + const std::lock_guard lock(m_subscriber_map.sync); + + // reserve target + monitoring_.subscriber.reserve(m_subscriber_map.map->size()); + + // iterate map + m_subscriber_map.map->remove_deprecated(); + for (const auto& subscriber : (*m_subscriber_map.map)) + { + monitoring_.subscriber.emplace_back(subscriber.second); + } + } + + if ((entities_ & Monitoring::Entity::Server) != 0u) + { + // clear target + monitoring_.server.clear(); + + // lock map + const std::lock_guard lock(m_server_map.sync); + + // reserve target + monitoring_.server.reserve(m_server_map.map->size()); + + // iterate map + m_server_map.map->remove_deprecated(); + for (const auto& server : (*m_server_map.map)) + { + monitoring_.server.emplace_back(server.second); + } + } + + if ((entities_ & Monitoring::Entity::Client) != 0u) + { + // clear target + monitoring_.clients.clear(); + + // lock map + const std::lock_guard lock(m_clients_map.sync); + + // reserve target + monitoring_.clients.reserve(m_clients_map.map->size()); + + // iterate map + m_clients_map.map->remove_deprecated(); + for (const auto& client : (*m_clients_map.map)) + { + monitoring_.clients.emplace_back(client.second); + } + } + } + + void CMonitoringImpl::MonitorProcs(Monitoring::SMonitoring& monitoring_) + { + // acquire access + const std::lock_guard lock(m_process_map.sync); + + // iterate map + m_process_map.map->remove_deprecated(); + for (const auto& process : (*m_process_map.map)) + { + // add process + monitoring_.processes.push_back(process.second); + } + } + + void CMonitoringImpl::MonitorServer(Monitoring::SMonitoring& monitoring_) + { + // acquire access + const std::lock_guard lock(m_server_map.sync); + + // iterate map + m_server_map.map->remove_deprecated(); + for (const auto& server : (*m_server_map.map)) + { + // add service + monitoring_.server.push_back(server.second); + } + } + + void CMonitoringImpl::MonitorClients(Monitoring::SMonitoring& monitoring_) + { + // acquire access + const std::lock_guard lock(m_clients_map.sync); + + // iterate map + m_clients_map.map->remove_deprecated(); + for (const auto& client : (*m_clients_map.map)) + { + // add client + monitoring_.clients.push_back(client.second); + } + } + + void CMonitoringImpl::MonitorTopics(STopicMonMap& map_, Monitoring::SMonitoring& monitoring_, const std::string& direction_) + { + // acquire access + const std::lock_guard lock(map_.sync); + + // iterate map + map_.map->remove_deprecated(); + for (const auto& topic : (*map_.map)) + { + if (direction_ == "publisher") + { + monitoring_.publisher.push_back(topic.second); + } + if (direction_ == "subscriber") + { + monitoring_.subscriber.push_back(topic.second); + } + } + } + + 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/src/core/src/monitoring/ecal_monitoring_impl.h b/src/core/src/monitoring/ecal_monitoring_impl.h new file mode 100644 index 0000000..bfce029 --- /dev/null +++ b/src/core/src/monitoring/ecal_monitoring_impl.h @@ -0,0 +1,169 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief Global monitoring class (implementation) +**/ + +#pragma once + +#include + +#include "ecal_def.h" +#include "util/ecal_expmap.h" + +#include "serialization/ecal_serialize_sample_registration.h" + +#include +#include +#include +#include + +#ifdef ECAL_OS_LINUX +#include // strcasecmp +#endif + +namespace eCAL +{ + //////////////////////////////////////// + // Monitoring Declaration + //////////////////////////////////////// + class CMonitoringImpl + { + public: + CMonitoringImpl(); + ~CMonitoringImpl() = default; + + void Create(); + void Destroy(); + + void SetExclFilter(const std::string& filter_); + void SetInclFilter(const std::string& filter_); + void SetFilterState(bool state_); + + void GetMonitoring(std::string& monitoring_, unsigned int entities_); + void GetMonitoring(Monitoring::SMonitoring& monitoring_, unsigned int entities_); + + protected: + bool ApplySample(const Registration::Sample& ecal_sample_, eTLayerType /*layer_*/); + + bool RegisterProcess(const Registration::Sample& sample_); + bool UnregisterProcess(const Registration::Sample& sample_); + + bool RegisterServer(const Registration::Sample& sample_); + bool UnregisterServer(const Registration::Sample& sample_); + + bool RegisterClient(const Registration::Sample& sample_); + bool UnregisterClient(const Registration::Sample& sample_); + + enum ePubSub + { + publisher = 1, + subscriber = 2, + }; + + bool RegisterTopic(const Registration::Sample& sample_, enum ePubSub pubsub_type_); + bool UnregisterTopic(const Registration::Sample& sample_, enum ePubSub pubsub_type_); + + using TopicMonMapT = Util::CExpMap; + struct STopicMonMap + { + explicit STopicMonMap(const std::chrono::milliseconds& timeout_) : + map(std::make_unique(timeout_)) + { + }; + std::mutex sync; + std::unique_ptr map; + }; + + using ProcessMonMapT = Util::CExpMap; + struct SProcessMonMap + { + explicit SProcessMonMap(const std::chrono::milliseconds& timeout_) : + map(std::make_unique(timeout_)) + { + }; + std::mutex sync; + std::unique_ptr map; + }; + + using ServerMonMapT = Util::CExpMap; + struct SServerMonMap + { + explicit SServerMonMap(const std::chrono::milliseconds& timeout_) : + map(std::make_unique(timeout_)) + { + }; + std::mutex sync; + std::unique_ptr map; + }; + + using ClientMonMapT = Util::CExpMap; + struct SClientMonMap + { + explicit SClientMonMap(const std::chrono::milliseconds& timeout_) : + map(std::make_unique(timeout_)) + { + }; + 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_); + void MonitorServer(Monitoring::SMonitoring& monitoring_); + 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; + + // database + SProcessMonMap m_process_map; + STopicMonMap m_publisher_map; + STopicMonMap m_subscriber_map; + SServerMonMap m_server_map; + SClientMonMap m_clients_map; + }; +} diff --git a/src/core/src/pubsub/ecal_pubgate.cpp b/src/core/src/pubsub/ecal_pubgate.cpp new file mode 100644 index 0000000..ba50336 --- /dev/null +++ b/src/core/src/pubsub/ecal_pubgate.cpp @@ -0,0 +1,248 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL publisher gateway class +**/ + +#include "ecal_pubgate.h" +#include "ecal_sample_to_topicinfo.h" +#include "ecal_globals.h" + +#include +#include + +namespace +{ + bool ApplyTopicToDescGate(const std::string& topic_name_, const eCAL::SDataTypeInformation& topic_info_) + { + if (eCAL::g_descgate() != nullptr) + { + // Calculate the quality of the current info + eCAL::CDescGate::QualityFlags quality = eCAL::CDescGate::QualityFlags::NO_QUALITY; + if (!topic_info_.name.empty() || !topic_info_.encoding.empty()) + quality |= eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; + if (!topic_info_.descriptor.empty()) + quality |= eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_ENTITY; + + return eCAL::g_descgate()->ApplyTopicDescription(topic_name_, topic_info_, quality); + } + return false; + } +} + +namespace eCAL +{ + ////////////////////////////////////////////////////////////////// + // CPubGate + ////////////////////////////////////////////////////////////////// + std::atomic CPubGate::m_created; + CPubGate::CPubGate() : + m_share_type(true), + m_share_desc(true) + { + } + + CPubGate::~CPubGate() + { + Destroy(); + } + + void CPubGate::Create() + { + if(m_created) return; + m_created = true; + } + + void CPubGate::Destroy() + { + if(!m_created) return; + + // destroy all remaining publisher + const std::unique_lock lock(m_topic_name_datawriter_sync); + for (auto iter = m_topic_name_datawriter_map.begin(); iter != m_topic_name_datawriter_map.end(); ++iter) + { + iter->second->Destroy(); + } + + m_created = false; + } + + void CPubGate::ShareType(bool state_) + { + m_share_type = state_; + } + + void CPubGate::ShareDescription(bool state_) + { + m_share_desc = state_; + } + + bool CPubGate::Register(const std::string& topic_name_, const std::shared_ptr& datawriter_) + { + if(!m_created) return(false); + + // register writer and multicast group + const std::unique_lock lock(m_topic_name_datawriter_sync); + m_topic_name_datawriter_map.emplace(std::pair>(topic_name_, datawriter_)); + + return(true); + } + + bool CPubGate::Unregister(const std::string& topic_name_, const std::shared_ptr& datawriter_) + { + if(!m_created) return(false); + bool ret_state = false; + + const std::unique_lock lock(m_topic_name_datawriter_sync); + auto res = m_topic_name_datawriter_map.equal_range(topic_name_); + for(auto iter = res.first; iter != res.second; ++iter) + { + if(iter->second == datawriter_) + { + m_topic_name_datawriter_map.erase(iter); + ret_state = true; + break; + } + } + + return(ret_state); + } + + void CPubGate::ApplyLocSubRegistration(const Registration::Sample& ecal_sample_) + { + if(!m_created) return; + + const auto& ecal_sample = ecal_sample_.topic; + const std::string& topic_name = ecal_sample.tname; + CDataWriter::SLocalSubscriptionInfo subscription_info; + subscription_info.topic_id = ecal_sample.tid; + subscription_info.process_id = std::to_string(ecal_sample.pid); + const SDataTypeInformation topic_information{ eCALSampleToTopicInformation(ecal_sample_) }; + + std::string reader_par; +#if 0 + for (const auto& layer : ecal_sample.tlayer()) + { + // layer parameter as protobuf message + // this parameter is not used at all currently + // for local subscriber registrations + reader_par = layer.par_layer().SerializeAsString(); + } +#endif + + // store description + ApplyTopicToDescGate(topic_name, topic_information); + + // register local subscriber + const std::shared_lock lock(m_topic_name_datawriter_sync); + auto res = m_topic_name_datawriter_map.equal_range(topic_name); + for(TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) + { + iter->second->ApplyLocSubscription(subscription_info, topic_information, reader_par); + } + } + + void CPubGate::ApplyLocSubUnregistration(const Registration::Sample& ecal_sample_) + { + if (!m_created) return; + + const auto& ecal_sample = ecal_sample_.topic; + const std::string& topic_name = ecal_sample.tname; + CDataWriter::SLocalSubscriptionInfo subscription_info; + subscription_info.topic_id = ecal_sample.tid; + subscription_info.process_id = std::to_string(ecal_sample.pid); + + // unregister local subscriber + const std::shared_lock lock(m_topic_name_datawriter_sync); + auto res = m_topic_name_datawriter_map.equal_range(topic_name); + for (TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) + { + iter->second->RemoveLocSubscription(subscription_info); + } + } + + void CPubGate::ApplyExtSubRegistration(const Registration::Sample& ecal_sample_) + { + if(!m_created) return; + + const auto& ecal_sample = ecal_sample_.topic; + const std::string& topic_name = ecal_sample.tname; + CDataWriter::SExternalSubscriptionInfo subscription_info; + subscription_info.host_name = ecal_sample.hname; + subscription_info.topic_id = ecal_sample.tid; + subscription_info.process_id = std::to_string(ecal_sample.pid); + const SDataTypeInformation topic_information{ eCALSampleToTopicInformation(ecal_sample_) }; + + std::string reader_par; +#if 0 + for (const auto& layer : ecal_sample.tlayer()) + { + // layer parameter as protobuf message + // this parameter is not used at all currently + // for external subscriber registrations + reader_par = layer.par_layer().SerializeAsString(); + } +#endif + + // store description + ApplyTopicToDescGate(topic_name, topic_information); + + // register external subscriber + const std::shared_lock lock(m_topic_name_datawriter_sync); + auto res = m_topic_name_datawriter_map.equal_range(topic_name); + for(TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) + { + iter->second->ApplyExtSubscription(subscription_info, topic_information, reader_par); + } + } + + void CPubGate::ApplyExtSubUnregistration(const Registration::Sample& ecal_sample_) + { + if (!m_created) return; + + const auto& ecal_sample = ecal_sample_.topic; + const std::string& topic_name = ecal_sample.tname; + CDataWriter::SExternalSubscriptionInfo subscription_info; + subscription_info.host_name = ecal_sample.hname; + subscription_info.topic_id = ecal_sample.tid; + subscription_info.process_id = std::to_string(ecal_sample.pid); + + // unregister external subscriber + const std::shared_lock lock(m_topic_name_datawriter_sync); + auto res = m_topic_name_datawriter_map.equal_range(topic_name); + for (TopicNameDataWriterMapT::const_iterator iter = res.first; iter != res.second; ++iter) + { + iter->second->RemoveExtSubscription(subscription_info); + } + } + + void CPubGate::RefreshRegistrations() + { + if (!m_created) return; + + // refresh publisher registrations + const std::shared_lock lock(m_topic_name_datawriter_sync); + for (const auto& iter : m_topic_name_datawriter_map) + { + iter.second->RefreshRegistration(); + } + } +} diff --git a/ecal/core/src/pubsub/ecal_pubgate.h b/src/core/src/pubsub/ecal_pubgate.h similarity index 63% rename from ecal/core/src/pubsub/ecal_pubgate.h rename to src/core/src/pubsub/ecal_pubgate.h index 2ed3608..7b42a79 100644 --- a/ecal/core/src/pubsub/ecal_pubgate.h +++ b/src/core/src/pubsub/ecal_pubgate.h @@ -23,13 +23,13 @@ #pragma once -#include "ecal_global_accessors.h" -#include "ecal_def.h" - #include "readwrite/ecal_writer.h" +#include "serialization/ecal_struct_sample_registration.h" -#include #include +#include +#include +#include #include namespace eCAL @@ -44,16 +44,19 @@ namespace eCAL void Destroy(); void ShareType(bool state_); - bool TypeShared() { return m_share_type; }; + bool TypeShared() const { return m_share_type; }; void ShareDescription(bool state_); - bool DescriptionShared() { return m_share_desc; }; + bool DescriptionShared() const { return m_share_desc; }; + + bool Register(const std::string& topic_name_, const std::shared_ptr& datawriter_); + bool Unregister(const std::string& topic_name_, const std::shared_ptr& datawriter_); - bool Register(const std::string& topic_name_, CDataWriter* datawriter_); - bool Unregister(const std::string& topic_name_, CDataWriter* datawriter_); + void ApplyLocSubRegistration(const Registration::Sample& ecal_sample_); + void ApplyLocSubUnregistration(const Registration::Sample& ecal_sample_); - void ApplyLocSubRegistration(const eCAL::pb::Sample& ecal_sample_); - void ApplyExtSubRegistration(const eCAL::pb::Sample& ecal_sample_); + void ApplyExtSubRegistration(const Registration::Sample& ecal_sample_); + void ApplyExtSubUnregistration(const Registration::Sample& ecal_sample_); void RefreshRegistrations(); @@ -62,8 +65,8 @@ namespace eCAL bool m_share_type; bool m_share_desc; - typedef std::multimap TopicNameDataWriterMapT; + using TopicNameDataWriterMapT = std::multimap>; std::shared_timed_mutex m_topic_name_datawriter_sync; TopicNameDataWriterMapT m_topic_name_datawriter_map; }; -}; +} diff --git a/src/core/src/pubsub/ecal_publisher.cpp b/src/core/src/pubsub/ecal_publisher.cpp new file mode 100644 index 0000000..f2ef152 --- /dev/null +++ b/src/core/src/pubsub/ecal_publisher.cpp @@ -0,0 +1,315 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief common data publisher based on eCAL +**/ + +#include + +#include "ecal_globals.h" +#include "readwrite/ecal_writer.h" +#include "readwrite/ecal_writer_buffer_payload.h" + +#include +#include + +namespace +{ + bool ApplyTopicToDescGate(const std::string& topic_name_, const eCAL::SDataTypeInformation& data_type_info_) + { + if (eCAL::g_descgate() != nullptr) + { + // Calculate the quality of the current info + eCAL::CDescGate::QualityFlags quality = eCAL::CDescGate::QualityFlags::NO_QUALITY; + if (!data_type_info_.name.empty() || !data_type_info_.encoding.empty()) + quality |= eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; + if (!data_type_info_.descriptor.empty()) + quality |= eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_ENTITY; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PRODUCER; + + return eCAL::g_descgate()->ApplyTopicDescription(topic_name_, data_type_info_, quality); + } + return false; + } +} + +namespace eCAL +{ + CPublisher::CPublisher() : + m_datawriter(nullptr), + m_id(0), + m_created(false), + m_initialized(false) + { + } + + CPublisher::CPublisher(const std::string& topic_name_, const SDataTypeInformation& data_type_info_) + : CPublisher() + { + Create(topic_name_, data_type_info_); + } + + CPublisher::CPublisher(const std::string& topic_name_) + : CPublisher(topic_name_, SDataTypeInformation{}) + {} + + CPublisher::~CPublisher() + { + Destroy(); + } + + /** + * @brief CPublisher are move-enabled + **/ + CPublisher::CPublisher(CPublisher&& rhs) noexcept : + m_datawriter(std::move(rhs.m_datawriter)), + m_id(rhs.m_id), + m_created(rhs.m_created), + m_initialized(rhs.m_initialized) + { + rhs.m_created = false; + rhs.m_initialized = false; + } + + /** + * @brief CPublisher are move-enabled + **/ + CPublisher& CPublisher::operator=(CPublisher&& rhs) noexcept + { + // Call destroy, to clean up the current state, then afterwards move all elements + Destroy(); + + m_datawriter = std::move(rhs.m_datawriter); + m_id = rhs.m_id; + m_created = rhs.m_created; + m_initialized = rhs.m_initialized; + + rhs.m_created = false; + rhs.m_initialized = false; + + return *this; + } + + bool CPublisher::Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_) + { + if (m_created) return(false); + if (topic_name_.empty()) return(false); + if (g_globals() == nullptr) return(false); + + // initialize globals + if (g_globals()->IsInitialized(Init::Publisher) == 0) + { + g_globals()->Initialize(Init::Publisher); + m_initialized = true; + } + + // create data writer + m_datawriter = std::make_shared(); + if (!m_datawriter->Create(topic_name_, data_type_info_)) + { +#ifndef NDEBUG + // log it + if (g_log() != nullptr) g_log()->Log(log_level_debug1, topic_name_ + "::CPublisher::Create - FAILED"); +#endif + return(false); + } +#ifndef NDEBUG + // log it + if (g_log() != nullptr) g_log()->Log(log_level_debug1, topic_name_ + "::CPublisher::Create - SUCCESS"); +#endif + // register publisher gateway (for publisher memory file and event name) + g_pubgate()->Register(topic_name_, m_datawriter); + + // register to description gateway for type / description checking + ApplyTopicToDescGate(topic_name_, data_type_info_); + + // we made it :-) + m_created = true; + + return(m_created); + } + + bool CPublisher::Create(const std::string& topic_name_) + { + return Create(topic_name_, SDataTypeInformation()); + } + + bool CPublisher::Destroy() + { + if(!m_created) return(false); + if(g_globals() == nullptr) return(false); + + // destroy data writer + m_datawriter->Destroy(); + + // unregister data writer + if(g_pubgate() != nullptr) g_pubgate()->Unregister(m_datawriter->GetTopicName(), m_datawriter); +#ifndef NDEBUG + // log it + if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(m_datawriter->GetTopicName() + "::CPublisher::Destroy")); +#endif + + // free datawriter + m_datawriter.reset(); + + // we made it :-) + m_created = false; + + // if we initialize the globals then we finalize + // here to decrease reference counter + if (m_initialized) + { + g_globals()->Finalize(Init::Publisher); + m_initialized = false; + } + + return(true); + } + + bool CPublisher::SetDataTypeInformation(const SDataTypeInformation& data_type_info_) + { + if (m_datawriter == nullptr) return false; + ApplyTopicToDescGate(m_datawriter->GetTopicName(), data_type_info_); + return m_datawriter->SetDataTypeInformation(data_type_info_); + } + + bool CPublisher::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) + { + if(m_datawriter == nullptr) return false; + return m_datawriter->SetAttribute(attr_name_, attr_value_); + } + + bool CPublisher::ClearAttribute(const std::string& attr_name_) + { + if(m_datawriter == nullptr) return false; + return m_datawriter->ClearAttribute(attr_name_); + } + + bool CPublisher::ShareType(bool state_ /*= true*/) + { + if (m_datawriter == nullptr) return false; + m_datawriter->ShareType(state_); + return true; + } + + bool CPublisher::ShareDescription(bool state_ /*= true*/) + { + if (m_datawriter == nullptr) return false; + m_datawriter->ShareDescription(state_); + return true; + } + + bool CPublisher::SetID(long long id_) + { + m_id = id_; + return(true); + } + + size_t CPublisher::Send(const void* const buf_, const size_t len_, const long long time_ /* = DEFAULT_TIME_ARGUMENT */) const + { + CBufferPayloadWriter payload{ buf_, len_ }; + return Send(payload, time_); + } + + size_t CPublisher::Send(CPayloadWriter& payload_, long long time_) const + { + if (!m_created) return(0); + + // in an optimization case the + // publisher can send an empty package + // or we do not have any subscription at all + // then the data writer will only do some statistics + // for the monitoring layer and return + if (!IsSubscribed()) + { + m_datawriter->RefreshSendCounter(); + return(payload_.GetSize()); + } + + // send content via data writer layer + const long long write_time = (time_ == DEFAULT_TIME_ARGUMENT) ? eCAL::Time::GetMicroSeconds() : time_; + const size_t written_bytes = m_datawriter->Write(payload_, write_time, m_id); + + // return number of bytes written + return written_bytes; + } + + size_t CPublisher::Send(const std::string& s_, long long time_) const + { + return(Send(s_.data(), s_.size(), time_)); + } + + bool CPublisher::AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_) + { + if (m_datawriter == nullptr) return(false); + RemEventCallback(type_); + return(m_datawriter->AddEventCallback(type_, std::move(callback_))); + } + + bool CPublisher::RemEventCallback(eCAL_Publisher_Event type_) + { + if (m_datawriter == nullptr) return(false); + return(m_datawriter->RemEventCallback(type_)); + } + + bool CPublisher::IsSubscribed() const + { +#if ECAL_CORE_REGISTRATION + if(m_datawriter == nullptr) return(false); + return(m_datawriter->IsSubscribed()); +#else // ECAL_CORE_REGISTRATION + return(true); +#endif // ECAL_CORE_REGISTRATION + } + + size_t CPublisher::GetSubscriberCount() const + { + if (m_datawriter == nullptr) return(0); + return(m_datawriter->GetSubscriberCount()); + } + + std::string CPublisher::GetTopicName() const + { + if(m_datawriter == nullptr) return(""); + return(m_datawriter->GetTopicName()); + } + + SDataTypeInformation CPublisher::GetDataTypeInformation() const + { + if (m_datawriter == nullptr) return(SDataTypeInformation{}); + return(m_datawriter->GetDataTypeInformation()); + } + + std::string CPublisher::Dump(const std::string& indent_ /* = "" */) const + { + std::stringstream out; + + out << indent_ << "----------------------" << std::endl; + out << indent_ << " class CPublisher" << std::endl; + out << indent_ << "----------------------" << std::endl; + out << indent_ << "m_created: " << m_created << std::endl; + if((m_datawriter != nullptr) && m_datawriter->IsCreated()) out << indent_ << m_datawriter->Dump(" "); + out << std::endl; + + return(out.str()); + } +} diff --git a/src/core/src/pubsub/ecal_subgate.cpp b/src/core/src/pubsub/ecal_subgate.cpp new file mode 100644 index 0000000..a197c5a --- /dev/null +++ b/src/core/src/pubsub/ecal_subgate.cpp @@ -0,0 +1,348 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL subscriber gateway class +**/ + +#include "pubsub/ecal_subgate.h" +#include "ecal_sample_to_topicinfo.h" +#include "ecal_globals.h" + +#include +#include + +namespace +{ + bool ApplyTopicToDescGate(const std::string& topic_name_, const eCAL::SDataTypeInformation& topic_info_) + { + if (eCAL::g_descgate() != nullptr) + { + // Calculate the quality of the current info + eCAL::CDescGate::QualityFlags quality = eCAL::CDescGate::QualityFlags::NO_QUALITY; + if (!topic_info_.name.empty() || !topic_info_.encoding.empty()) + quality |= eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; + if (!topic_info_.descriptor.empty()) + quality |= eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_ENTITY; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PRODUCER; + + return eCAL::g_descgate()->ApplyTopicDescription(topic_name_, topic_info_, quality); + } + return false; + } +} + +namespace eCAL +{ + ////////////////////////////////////////////////////////////////// + // CSubGate + ////////////////////////////////////////////////////////////////// + std::atomic CSubGate::m_created; + + CSubGate::CSubGate() = default; + + CSubGate::~CSubGate() + { + Destroy(); + } + + void CSubGate::Create() + { + if(m_created) return; + + // initialize data reader layers + CDataReader::InitializeLayers(); + + m_created = true; + } + + void CSubGate::Destroy() + { + if(!m_created) return; + + // destroy all remaining subscriber + const std::unique_lock lock(m_topic_name_datareader_sync); + for (auto iter = m_topic_name_datareader_map.begin(); iter != m_topic_name_datareader_map.end(); ++iter) + { + iter->second->Destroy(); + } + + m_created = false; + } + + bool CSubGate::Register(const std::string& topic_name_, const std::shared_ptr& datareader_) + { + if(!m_created) return(false); + + // register reader + const std::unique_lock lock(m_topic_name_datareader_sync); + m_topic_name_datareader_map.emplace(std::pair>(topic_name_, datareader_)); + + return(true); + } + + bool CSubGate::Unregister(const std::string& topic_name_, const std::shared_ptr& datareader_) + { + if(!m_created) return(false); + bool ret_state = false; + + const std::unique_lock lock(m_topic_name_datareader_sync); + auto res = m_topic_name_datareader_map.equal_range(topic_name_); + for(auto iter = res.first; iter != res.second; ++iter) + { + if(iter->second == datareader_) + { + m_topic_name_datareader_map.erase(iter); + ret_state = true; + break; + } + } + + return(ret_state); + } + + bool CSubGate::HasSample(const std::string& sample_name_) + { + const std::shared_lock lock(m_topic_name_datareader_sync); + return(m_topic_name_datareader_map.find(sample_name_) != m_topic_name_datareader_map.end()); + } + + bool CSubGate::ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_, eTLayerType layer_) + { + if(!m_created) return false; + + Payload::Sample ecal_sample; + if (!DeserializeFromBuffer(serialized_sample_data_, serialized_sample_size_, ecal_sample)) return false; + + size_t applied_size(0); + switch (ecal_sample.cmd_type) + { + case bct_set_sample: + { +#ifndef NDEBUG + // check layer + if (layer_ == eTLayerType::tl_none) + { + // log it + Logging::Log(log_level_error, ecal_sample.topic.tname + " : payload received without layer definition !"); + } +#endif + + // extract payload + const char* payload_addr = nullptr; + size_t payload_size = 0; + switch (ecal_sample.content.payload.type) + { + case eCAL::Payload::pl_raw: + payload_addr = ecal_sample.content.payload.raw_addr; + payload_size = ecal_sample.content.payload.raw_size; + break; + case eCAL::Payload::pl_vec: + payload_addr = ecal_sample.content.payload.vec.data(); + payload_size = ecal_sample.content.payload.vec.size(); + break; + default: + break; + } + + // update globals + g_process_rclock++; + g_process_rbytes_sum += payload_size; + + std::vector> readers_to_apply; + + // Lock the sync map only while extracting the relevant shared pointers to the Datareaders. + // Apply the samples to the readers afterward. + { + // 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); + std::transform( + res.first, res.second, std::back_inserter(readers_to_apply), [](const auto& match) { return match.second; } + ); + } + + const auto& ecal_sample_content = ecal_sample.content; + for (const auto& reader : readers_to_apply) + { + applied_size = reader->AddSample( + ecal_sample.topic.tid, + payload_addr, + payload_size, + ecal_sample_content.id, + ecal_sample_content.clock, + ecal_sample_content.time, + static_cast(ecal_sample_content.hash), + layer_ + ); + } + } + break; + default: + break; + } + + 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_) + { + if (!m_created) return false; + + // update globals + g_process_rclock++; + g_process_rbytes_sum += len_; + + // apply sample to data reader + size_t applied_size(0); + std::vector> readers_to_apply; + + // Lock the sync map only while extracting the relevant shared pointers to the Datareaders. + // 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_); + std::transform( + res.first, res.second, std::back_inserter(readers_to_apply), [](const auto& match) { return match.second; } + ); + } + + + for (const auto& reader : readers_to_apply) + { + applied_size = reader->AddSample(topic_id_, buf_, len_, id_, clock_, time_, hash_, layer_); + } + + return (applied_size > 0); + } + + void CSubGate::ApplyLocPubRegistration(const Registration::Sample& ecal_sample_) + { + if(!m_created) return; + + // check topic name + const auto& ecal_sample = ecal_sample_.topic; + const std::string& topic_name = ecal_sample.tname; + if (topic_name.empty()) return; + + // store description + const std::string& topic_id = ecal_sample.tid; + const SDataTypeInformation topic_info{ eCALSampleToTopicInformation(ecal_sample_) }; + ApplyTopicToDescGate(topic_name, topic_info); + + // get process id + const std::string process_id = std::to_string(ecal_sample_.topic.pid); + + // handle local publisher connection + const std::shared_lock lock(m_topic_name_datareader_sync); + auto res = m_topic_name_datareader_map.equal_range(topic_name); + for (auto iter = res.first; iter != res.second; ++iter) + { + // apply layer specific parameter + for (const auto& tlayer : ecal_sample_.topic.tlayer) + { + iter->second->ApplyLocLayerParameter(process_id, topic_id, tlayer.type, tlayer.par_layer); + } + // inform for local publisher connection + iter->second->ApplyLocPublication(process_id, topic_id, topic_info); + } + } + + void CSubGate::ApplyLocPubUnregistration(const Registration::Sample& ecal_sample_) + { + if (!m_created) return; + + // check topic name + const auto& ecal_sample = ecal_sample_.topic; + const std::string& topic_name = ecal_sample.tname; + const std::string& topic_id = ecal_sample.tid; + const std::string process_id = std::to_string(ecal_sample_.topic.pid); + + // store description + const SDataTypeInformation topic_info{ eCALSampleToTopicInformation(ecal_sample_) }; + ApplyTopicToDescGate(topic_name, topic_info); + + // unregister local publisher + const std::shared_lock lock(m_topic_name_datareader_sync); + auto res = m_topic_name_datareader_map.equal_range(topic_name); + for (auto iter = res.first; iter != res.second; ++iter) + { + iter->second->RemoveLocPublication(process_id, topic_id); + } + } + + void CSubGate::ApplyExtPubRegistration(const Registration::Sample& ecal_sample_) + { + if(!m_created) return; + + const auto& ecal_sample = ecal_sample_.topic; + const std::string& host_name = ecal_sample.hname; + const std::string& topic_name = ecal_sample.tname; + const std::string& topic_id = ecal_sample.tid; + const SDataTypeInformation topic_info{ eCALSampleToTopicInformation(ecal_sample_) }; + const std::string process_id = std::to_string(ecal_sample.pid); + + // handle external publisher connection + const std::shared_lock lock(m_topic_name_datareader_sync); + auto res = m_topic_name_datareader_map.equal_range(topic_name); + for (auto iter = res.first; iter != res.second; ++iter) + { + // apply layer specific parameter + for (const auto& tlayer : ecal_sample_.topic.tlayer) + { + iter->second->ApplyExtLayerParameter(host_name, tlayer.type, tlayer.par_layer); + } + + // inform for external publisher connection + iter->second->ApplyExtPublication(host_name, process_id, topic_id, topic_info); + } + } + + void CSubGate::ApplyExtPubUnregistration(const Registration::Sample& ecal_sample_) + { + if (!m_created) return; + + const auto& ecal_sample = ecal_sample_.topic; + const std::string& host_name = ecal_sample.hname; + const std::string& topic_name = ecal_sample.tname; + const std::string& topic_id = ecal_sample.tid; + const std::string process_id = std::to_string(ecal_sample.pid); + + // unregister local subscriber + const std::shared_lock lock(m_topic_name_datareader_sync); + auto res = m_topic_name_datareader_map.equal_range(topic_name); + for (auto iter = res.first; iter != res.second; ++iter) + { + iter->second->RemoveExtPublication(host_name, process_id, topic_id); + } + } + + void CSubGate::RefreshRegistrations() + { + if (!m_created) return; + + // refresh reader registrations + const std::shared_lock lock(m_topic_name_datareader_sync); + for (const auto& iter : m_topic_name_datareader_map) + { + iter.second->RefreshRegistration(); + } + } +} diff --git a/ecal/core/src/pubsub/ecal_subgate.h b/src/core/src/pubsub/ecal_subgate.h similarity index 59% rename from ecal/core/src/pubsub/ecal_subgate.h rename to src/core/src/pubsub/ecal_subgate.h index 872aa8f..7d44063 100644 --- a/ecal/core/src/pubsub/ecal_subgate.h +++ b/src/core/src/pubsub/ecal_subgate.h @@ -23,12 +23,11 @@ #pragma once -#include "ecal_thread.h" - #include "readwrite/ecal_reader.h" #include #include +#include #include #include @@ -43,15 +42,19 @@ namespace eCAL void Create(); void Destroy(); - bool Register(const std::string& topic_name_, CDataReader* datareader_); - bool Unregister(const std::string& topic_name_, CDataReader* datareader_); + bool Register(const std::string& topic_name_, const std::shared_ptr& datareader_); + bool Unregister(const std::string& topic_name_, const std::shared_ptr& datareader_); bool HasSample(const std::string& sample_name_); - size_t ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_); - size_t 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_, eCAL::pb::eTLayerType layer_); - void ApplyLocPubRegistration(const eCAL::pb::Sample& ecal_sample_); - void ApplyExtPubRegistration(const eCAL::pb::Sample& ecal_sample_); + 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_); + + void ApplyLocPubRegistration(const Registration::Sample& ecal_sample_); + void ApplyLocPubUnregistration(const Registration::Sample& ecal_sample_); + + void ApplyExtPubRegistration(const Registration::Sample& ecal_sample_); + void ApplyExtPubUnregistration(const Registration::Sample& ecal_sample_); void RefreshRegistrations(); @@ -59,11 +62,8 @@ namespace eCAL static std::atomic m_created; // database data reader - typedef std::unordered_multimap TopicNameDataReaderMapT; + using TopicNameDataReaderMapT = std::unordered_multimap>; std::shared_timed_mutex m_topic_name_datareader_sync; TopicNameDataReaderMapT m_topic_name_datareader_map; - - eCAL::CThread m_subtimeout_thread; - int CheckTimeouts(); }; -}; +} diff --git a/ecal/core/src/pubsub/ecal_subscriber.cpp b/src/core/src/pubsub/ecal_subscriber.cpp similarity index 55% rename from ecal/core/src/pubsub/ecal_subscriber.cpp rename to src/core/src/pubsub/ecal_subscriber.cpp index 9c39496..2b10c38 100644 --- a/ecal/core/src/pubsub/ecal_subscriber.cpp +++ b/src/core/src/pubsub/ecal_subscriber.cpp @@ -23,37 +23,52 @@ #include -#include "ecal_def.h" #include "ecal_globals.h" -#include "ecal_registration_provider.h" -#include "ecal_subgate.h" - #include "readwrite/ecal_reader.h" #include #include +namespace +{ + bool ApplyTopicToDescGate(const std::string& topic_name_, const eCAL::SDataTypeInformation& topic_info_) + { + if (eCAL::g_descgate() != nullptr) + { + // Calculate the quality of the current info + eCAL::CDescGate::QualityFlags quality = eCAL::CDescGate::QualityFlags::NO_QUALITY; + if (!topic_info_.name.empty() || !topic_info_.encoding.empty()) + quality |= eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; + if (!topic_info_.descriptor.empty()) + quality |= eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_ENTITY; + + return eCAL::g_descgate()->ApplyTopicDescription(topic_name_, topic_info_, quality); + } + return false; + } +} namespace eCAL { CSubscriber::CSubscriber() : - m_datareader(nullptr), - m_created(false), - m_initialized(false) + m_datareader(nullptr), + m_created(false), + m_initialized(false) { - InitializeQOS(); } - CSubscriber::CSubscriber(const std::string& topic_name_, const std::string& topic_type_ /* = "" */, const std::string& topic_desc_ /* = "" */) : - m_datareader(nullptr), - m_created(false), - m_initialized(false) + CSubscriber::CSubscriber(const std::string& topic_name_, const SDataTypeInformation& topic_info_) + : CSubscriber() { - InitializeQOS(); - - Create(topic_name_, topic_type_, topic_desc_); + Create(topic_name_, topic_info_); } + CSubscriber::CSubscriber(const std::string& topic_name_) + : CSubscriber(topic_name_, SDataTypeInformation{}) + {} + CSubscriber::~CSubscriber() { Destroy(); @@ -61,75 +76,66 @@ namespace eCAL CSubscriber::CSubscriber(CSubscriber&& rhs) noexcept : m_datareader(std::move(rhs.m_datareader)), - m_qos(rhs.m_qos), m_created(rhs.m_created), m_initialized(rhs.m_initialized) { - InitializeQOS(); - rhs.m_created = false; rhs.m_initialized = false; } CSubscriber& CSubscriber::operator=(CSubscriber&& rhs) noexcept { - m_datareader = std::move(rhs.m_datareader); + // Call destroy, to clean up the current state, then afterwards move all elements + Destroy(); - m_qos = rhs.m_qos; + m_datareader = std::move(rhs.m_datareader); m_created = rhs.m_created; m_initialized = rhs.m_initialized; - InitializeQOS(); rhs.m_created = false; rhs.m_initialized = false; return *this; - }; + } + + bool CSubscriber::Create(const std::string& topic_name_) + { + return Create(topic_name_, SDataTypeInformation{}); + } - bool CSubscriber::Create(const std::string& topic_name_, const std::string& topic_type_ /* = "" */, const std::string& topic_desc_ /* = "" */) + bool CSubscriber::Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_) { - if(m_created) return(false); - if(!g_globals()) return(false); - if(topic_name_.size() == 0) return(false); + if (m_created) return(false); + if (g_globals() == nullptr) return(false); + if (topic_name_.empty()) return(false); // initialize globals - if (!g_globals()->IsInitialized(Init::Subscriber)) + if (g_globals()->IsInitialized(Init::Subscriber) == 0) { g_globals()->Initialize(Init::Subscriber); m_initialized = true; } // create data reader - m_datareader = new CDataReader; - // set qos - m_datareader->SetQOS(m_qos); + m_datareader = std::make_shared(); // create it - if(!m_datareader->Create(topic_name_, topic_type_, topic_desc_)) + if (!m_datareader->Create(topic_name_, topic_info_)) { #ifndef NDEBUG // log it - if (g_log()) g_log()->Log(log_level_debug1, std::string(topic_name_ + "::CSubscriber::Create - FAILED")); + if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(topic_name_ + "::CSubscriber::Create - FAILED")); #endif return(false); } #ifndef NDEBUG // log it - if (g_log()) g_log()->Log(log_level_debug1, std::string(topic_name_ + "::CSubscriber::Create - SUCCESS")); + if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(topic_name_ + "::CSubscriber::Create - SUCCESS")); #endif // register to subscriber gateway for publisher memory file receive thread g_subgate()->Register(topic_name_, m_datareader); - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!topic_type_.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!topic_desc_.empty()) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - // register to description gateway for type / description checking - g_descgate()->ApplyTopicDescription(topic_name_, topic_type_, topic_desc_, quality); + ApplyTopicToDescGate(topic_name_, topic_info_); // we made it :-) m_created = true; @@ -139,27 +145,25 @@ namespace eCAL bool CSubscriber::Destroy() { - if(!m_created) return(false); - if(!g_globals()) return(false); + if(!m_created) return(false); + if(g_globals() == nullptr) return(false); // remove receive callback RemReceiveCallback(); // first unregister data reader - if(g_subgate()) g_subgate()->Unregister(m_datareader->GetTopicName(), m_datareader); - if(g_registration_provider()) g_registration_provider()->UnregisterTopic(m_datareader->GetTopicName(), m_datareader->GetTopicID()); + if(g_subgate() != nullptr) g_subgate()->Unregister(m_datareader->GetTopicName(), m_datareader); #ifndef NDEBUG // log it - if (g_log()) g_log()->Log(log_level_debug1, std::string(m_datareader->GetTopicName() + "::CSubscriber::Destroy")); + if (g_log() != nullptr) g_log()->Log(log_level_debug1, std::string(m_datareader->GetTopicName() + "::CSubscriber::Destroy")); #endif // destroy local data reader m_datareader->Destroy(); // free datareader - delete m_datareader; - m_datareader = nullptr; - + m_datareader.reset(); + // we made it :-) m_created = false; @@ -174,47 +178,25 @@ namespace eCAL return(true); } - bool CSubscriber::SetQOS(const QOS::SReaderQOS& qos_) - { - if (m_created) return false; - m_qos = qos_; - return true; - } - - QOS::SReaderQOS CSubscriber::GetQOS() - { - return m_qos; - } - bool CSubscriber::SetID(const std::set& id_set_) { - if (!m_datareader) return(false); + if (m_datareader == nullptr) return(false); m_datareader->SetID(id_set_); return(true); } bool CSubscriber::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) { - if(!m_datareader) return false; + if(m_datareader == nullptr) return false; return m_datareader->SetAttribute(attr_name_, attr_value_); } bool CSubscriber::ClearAttribute(const std::string& attr_name_) { - if(!m_datareader) return false; + if(m_datareader == nullptr) return false; return m_datareader->ClearAttribute(attr_name_); } - size_t CSubscriber::Receive(std::string& buf_, long long* time_ /* = nullptr */, int rcv_timeout_ /* = 0 */) const - { - if(!m_created) return(0); - if (m_datareader->Receive(buf_, time_, rcv_timeout_)) - { - return(buf_.size()); - } - return(0); - } - bool CSubscriber::ReceiveBuffer(std::string& buf_, long long* time_ /* = nullptr */, int rcv_timeout_ /* = 0 */) const { if (!m_created) return(false); @@ -223,64 +205,46 @@ namespace eCAL bool CSubscriber::AddReceiveCallback(ReceiveCallbackT callback_) { - if(!m_datareader) return(false); + if(m_datareader == nullptr) return(false); RemReceiveCallback(); - return(m_datareader->AddReceiveCallback(callback_)); + return(m_datareader->AddReceiveCallback(std::move(callback_))); } bool CSubscriber::RemReceiveCallback() { - if(!m_datareader) return(false); + if(m_datareader == nullptr) return(false); return(m_datareader->RemReceiveCallback()); } bool CSubscriber::AddEventCallback(eCAL_Subscriber_Event type_, SubEventCallbackT callback_) { - if (!m_datareader) return(false); + if (m_datareader == nullptr) return(false); RemEventCallback(type_); return(m_datareader->AddEventCallback(type_, callback_)); } bool CSubscriber::RemEventCallback(eCAL_Subscriber_Event type_) { - if (!m_datareader) return(false); + if (m_datareader == nullptr) return(false); return(m_datareader->RemEventCallback(type_)); } size_t CSubscriber::GetPublisherCount() const { - if(!m_datareader) return(0); + if (m_datareader == nullptr) return(0); return(m_datareader->GetPublisherCount()); } std::string CSubscriber::GetTopicName() const { - if(!m_datareader) return(""); + if(m_datareader == nullptr) return(""); return(m_datareader->GetTopicName()); } - - std::string CSubscriber::GetTypeName() const - { - if(!m_datareader) return(""); - return(m_datareader->GetTypeName()); - } - - std::string CSubscriber::GetDescription() const - { - if(!m_datareader) return(""); - return(m_datareader->GetDescription()); - } - - bool CSubscriber::SetTimeout(int timeout_) - { - if (!m_datareader) return(false); - return(m_datareader->SetTimeout(timeout_)); - } - - void CSubscriber::InitializeQOS() + + SDataTypeInformation CSubscriber::GetDataTypeInformation() const { - m_qos = QOS::SReaderQOS(); - m_qos.reliability = QOS::best_effort_reliability_qos; + if (m_datareader == nullptr) return(SDataTypeInformation{}); + return(m_datareader->GetDataTypeInformation()); } std::string CSubscriber::Dump(const std::string& indent_ /* = "" */) const @@ -291,7 +255,7 @@ namespace eCAL out << indent_ << " class CSubscriber " << std::endl; out << indent_ << "----------------------" << std::endl; out << indent_ << "m_created: " << m_created << std::endl; - if(m_datareader && m_datareader->IsCreated()) out << indent_ << m_datareader->Dump(" "); + if((m_datareader != nullptr) && m_datareader->IsCreated()) out << indent_ << m_datareader->Dump(" "); out << std::endl; return(out.str()); diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/src/core/src/readwrite/ecal_reader.cpp similarity index 59% rename from ecal/core/src/readwrite/ecal_reader.cpp rename to src/core/src/readwrite/ecal_reader.cpp index 6973123..e449ae4 100644 --- a/ecal/core/src/readwrite/ecal_reader.cpp +++ b/src/core/src/readwrite/ecal_reader.cpp @@ -24,26 +24,30 @@ #include #include -#include "ecal_def.h" -#include "ecal_registration_provider.h" -#include "ecal_descgate.h" +#if ECAL_CORE_REGISTRATION +#include "registration/ecal_registration_provider.h" +#endif #include "ecal_reader.h" -#include "ecal_process.h" +#include "ecal_global_accessors.h" +#include "ecal_reader_layer.h" -#include "readwrite/ecal_reader_udp_mc.h" +#if ECAL_CORE_TRANSPORT_UDP +#include "udp/ecal_reader_udp_mc.h" +#endif -#ifdef ECAL_LAYER_ICEORYX -#include "readwrite/ecal_reader_iceoryx.h" -#else /* ECAL_LAYER_ICEORYX */ -#include "readwrite/ecal_reader_shm.h" -#endif /* ECAL_LAYER_ICEORYX */ +#if ECAL_CORE_TRANSPORT_SHM +#include "shm/ecal_reader_shm.h" +#endif -#include "readwrite/ecal_reader_tcp.h" +#if ECAL_CORE_TRANSPORT_TCP +#include "tcp/ecal_reader_tcp.h" +#endif #include #include #include #include +#include namespace eCAL { @@ -52,21 +56,16 @@ namespace eCAL //////////////////////////////////////// CDataReader::CDataReader() : m_host_name(Process::GetHostName()), - m_host_id(Process::internal::GetHostID()), + m_host_group_name(Process::GetHostGroupName()), m_pid(Process::GetProcessID()), m_pname(Process::GetProcessName()), - m_topic_name(""), - m_topic_id(""), - m_topic_type(""), m_topic_size(0), m_connected(false), m_read_buf_received(false), m_read_time(0), - m_receive_timeout(0), m_receive_time(0), m_clock(0), m_clock_old(0), - m_rec_time(), m_freq(0), m_message_drops(0), m_loc_published(false), @@ -76,7 +75,6 @@ namespace eCAL m_use_udp_mc_confirmed(false), m_use_shm_confirmed(false), m_use_tcp_confirmed(false), - m_use_inproc_confirmed(false), m_created(false) { } @@ -86,15 +84,14 @@ namespace eCAL Destroy(); } - bool CDataReader::Create(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_) + bool CDataReader::Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_) { if(m_created) return(false); // set defaults m_topic_name = topic_name_; m_topic_id.clear(); - m_topic_type = topic_type_; - m_topic_desc = topic_desc_; + m_topic_info = topic_info_; m_clock = 0; m_clock_old = 0; m_message_drops = 0; @@ -110,13 +107,10 @@ namespace eCAL m_topic_id = counter.str(); // set registration expiration - std::chrono::milliseconds registration_timeout(Config::GetRegistrationTimeoutMs()); + const std::chrono::milliseconds registration_timeout(Config::GetRegistrationTimeoutMs()); m_loc_pub_map.set_expiration(registration_timeout); m_ext_pub_map.set_expiration(registration_timeout); - // set sample hash map expiration - m_sample_hash.set_expiration(std::chrono::milliseconds(500)); - // allow to share topic type m_use_ttype = Config::IsTopicTypeSharingEnabled(); @@ -127,7 +121,7 @@ namespace eCAL SubscribeToLayers(); // register - DoRegister(false); + Register(false); // mark as created m_created = true; @@ -149,16 +143,19 @@ namespace eCAL // reset receive callback { - std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_sync); m_receive_callback = nullptr; } // reset event callback map { - std::lock_guard lock(m_event_callback_map_sync); + const std::lock_guard lock(m_event_callback_map_sync); m_event_callback_map.clear(); } + // unregister + Unregister(); + // reset defaults m_created = false; m_clock = 0; @@ -170,7 +167,6 @@ namespace eCAL m_use_udp_mc_confirmed = false; m_use_shm_confirmed = false; m_use_tcp_confirmed = false; - m_use_inproc_confirmed = false; return(true); } @@ -178,152 +174,180 @@ namespace eCAL void CDataReader::InitializeLayers() { // initialize udp multicast layer +#if ECAL_CORE_TRANSPORT_UDP if (Config::IsUdpMulticastRecEnabled()) { CUDPReaderLayer::Get()->Initialize(); } +#endif // initialize tcp layer +#if ECAL_CORE_TRANSPORT_TCP if (Config::IsTcpRecEnabled()) { CTCPReaderLayer::Get()->Initialize(); } +#endif } void CDataReader::SubscribeToLayers() { // subscribe topic to udp multicast layer +#if ECAL_CORE_TRANSPORT_UDP if (Config::IsUdpMulticastRecEnabled()) { - CUDPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id, m_qos); + CUDPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id); } +#endif // subscribe topic to tcp layer +#if ECAL_CORE_TRANSPORT_TCP if (Config::IsTcpRecEnabled()) { - CTCPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id, m_qos); + CTCPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id); } +#endif } void CDataReader::UnsubscribeFromLayers() { // unsubscribe topic from udp multicast layer +#if ECAL_CORE_TRANSPORT_UDP if (Config::IsUdpMulticastRecEnabled()) { CUDPReaderLayer::Get()->RemSubscription(m_host_name, m_topic_name, m_topic_id); } +#endif // unsubscribe topic from tcp multicast layer +#if ECAL_CORE_TRANSPORT_TCP if (Config::IsTcpRecEnabled()) { CTCPReaderLayer::Get()->RemSubscription(m_host_name, m_topic_name, m_topic_id); } +#endif } - bool CDataReader::DoRegister(const bool force_) + bool CDataReader::Register(const bool force_) { - if(!m_created) return(false); +#if ECAL_CORE_REGISTRATION if(m_topic_name.empty()) return(false); // create command parameter - eCAL::pb::Sample ecal_reg_sample; - ecal_reg_sample.set_cmd_type(eCAL::pb::bct_reg_subscriber); - auto ecal_reg_sample_mutable_topic = ecal_reg_sample.mutable_topic(); - ecal_reg_sample_mutable_topic->set_hname(m_host_name); - ecal_reg_sample_mutable_topic->set_hid(m_host_id); - ecal_reg_sample_mutable_topic->set_tname(m_topic_name); - ecal_reg_sample_mutable_topic->set_tid(m_topic_id); - if (m_use_ttype) ecal_reg_sample_mutable_topic->set_ttype(m_topic_type); - if (m_use_tdesc) ecal_reg_sample_mutable_topic->set_tdesc(m_topic_desc); - *ecal_reg_sample_mutable_topic->mutable_attr() = google::protobuf::Map { m_attr.begin(), m_attr.end() }; - ecal_reg_sample_mutable_topic->set_tsize(google::protobuf::int32(m_topic_size)); + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_reg_subscriber; + auto& ecal_reg_sample_topic = ecal_reg_sample.topic; + ecal_reg_sample_topic.hname = m_host_name; + ecal_reg_sample_topic.hgname = m_host_group_name; + ecal_reg_sample_topic.tname = m_topic_name; + ecal_reg_sample_topic.tid = m_topic_id; + // topic_information + // Remove eCAL6!! + if (m_use_ttype) ecal_reg_sample_topic.ttype = Util::CombinedTopicEncodingAndType(m_topic_info.encoding, m_topic_info.name); + if (m_use_tdesc) ecal_reg_sample_topic.tdesc = m_topic_info.descriptor; + { + auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; + if (m_use_ttype) + { + ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; + ecal_reg_sample_tdatatype.name = m_topic_info.name; + } + if (m_use_tdesc) + { + ecal_reg_sample_tdatatype.desc = m_topic_info.descriptor; + } + } + ecal_reg_sample_topic.attr = m_attr; + ecal_reg_sample_topic.tsize = static_cast(m_topic_size); + +#if ECAL_CORE_TRANSPORT_UDP // udp multicast layer { - auto tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_ecal_udp_mc); - tlayer->set_version(1); - tlayer->set_confirmed(m_use_udp_mc_confirmed); + Registration::TLayer udp_tlayer; + udp_tlayer.type = tl_ecal_udp_mc; + udp_tlayer.version = 1; + udp_tlayer.confirmed = m_use_udp_mc_confirmed; + ecal_reg_sample_topic.tlayer.push_back(udp_tlayer); } +#endif + +#if ECAL_CORE_TRANSPORT_SHM // shm layer { - auto tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_ecal_shm); - tlayer->set_version(1); - tlayer->set_confirmed(m_use_shm_confirmed); + Registration::TLayer shm_tlayer; + shm_tlayer.type = tl_ecal_shm; + shm_tlayer.version = 1; + shm_tlayer.confirmed = m_use_shm_confirmed; + ecal_reg_sample_topic.tlayer.push_back(shm_tlayer); } +#endif + +#if ECAL_CORE_TRANSPORT_TCP // tcp layer { - auto tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_ecal_tcp); - tlayer->set_version(1); - tlayer->set_confirmed(m_use_tcp_confirmed); - } - // inproc layer - { - auto tlayer = ecal_reg_sample_mutable_topic->add_tlayer(); - tlayer->set_type(eCAL::pb::tl_inproc); - tlayer->set_version(1); - tlayer->set_confirmed(m_use_inproc_confirmed); + Registration::TLayer tcp_tlayer; + tcp_tlayer.type = tl_ecal_tcp; + tcp_tlayer.version = 1; + tcp_tlayer.confirmed = m_use_tcp_confirmed; + ecal_reg_sample_topic.tlayer.push_back(tcp_tlayer); } - ecal_reg_sample_mutable_topic->set_pid(m_pid); - ecal_reg_sample_mutable_topic->set_pname(m_pname); - ecal_reg_sample_mutable_topic->set_uname(Process::GetUnitName()); - ecal_reg_sample_mutable_topic->set_dclock(m_clock); - ecal_reg_sample_mutable_topic->set_dfreq(m_freq); - ecal_reg_sample_mutable_topic->set_message_drops(google::protobuf::int32(m_message_drops)); +#endif - size_t loc_connections(0); - size_t ext_connections(0); - { - std::lock_guard lock(m_pub_map_sync); - loc_connections = m_loc_pub_map.size(); - ext_connections = m_ext_pub_map.size(); - } - ecal_reg_sample_mutable_topic->set_connections_loc(google::protobuf::int32(loc_connections)); - ecal_reg_sample_mutable_topic->set_connections_ext(google::protobuf::int32(ext_connections)); + ecal_reg_sample_topic.pid = m_pid; + ecal_reg_sample_topic.pname = m_pname; + ecal_reg_sample_topic.uname = Process::GetUnitName(); + ecal_reg_sample_topic.dclock = m_clock; + ecal_reg_sample_topic.dfreq = m_freq; + ecal_reg_sample_topic.message_drops = static_cast(m_message_drops); - // qos HistoryKind - switch (m_qos.history_kind) - { - case QOS::keep_last_history_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_history(eCAL::pb::QOS::keep_last_history_qos); - break; - case QOS::keep_all_history_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_history(eCAL::pb::QOS::keep_all_history_qos); - break; - } - ecal_reg_sample_mutable_topic->mutable_tqos()->set_history_depth(m_qos.history_kind_depth); - // qos Reliability - switch (m_qos.reliability) - { - case QOS::best_effort_reliability_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_reliability(eCAL::pb::QOS::best_effort_reliability_qos); - break; - case QOS::reliable_reliability_qos: - ecal_reg_sample_mutable_topic->mutable_tqos()->set_reliability(eCAL::pb::QOS::reliable_reliability_qos); - break; - } + // we do not know the number of connections .. + ecal_reg_sample_topic.connections_loc = 0; + ecal_reg_sample_topic.connections_ext = 0; // register subscriber - if(g_registration_provider()) g_registration_provider()->RegisterTopic(m_topic_name, m_topic_id, ecal_reg_sample, force_); + if(g_registration_provider() != nullptr) g_registration_provider()->RegisterTopic(m_topic_name, m_topic_id, ecal_reg_sample, force_); #ifndef NDEBUG // log it Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::DoRegister"); #endif + +#endif // ECAL_CORE_REGISTRATION return(true); } - bool CDataReader::SetQOS(const QOS::SReaderQOS& qos_) + bool CDataReader::Unregister() { - m_qos = qos_; - return (!m_created); +#if ECAL_CORE_REGISTRATION + if (m_topic_name.empty()) return(false); + + // create command parameter + Registration::Sample ecal_unreg_sample; + ecal_unreg_sample.cmd_type = bct_unreg_subscriber; + auto& ecal_reg_sample_topic = ecal_unreg_sample.topic; + ecal_reg_sample_topic.hname = m_host_name; + ecal_reg_sample_topic.hgname = m_host_group_name; + ecal_reg_sample_topic.pname = m_pname; + ecal_reg_sample_topic.pid = m_pid; + ecal_reg_sample_topic.tname = m_topic_name; + ecal_reg_sample_topic.tid = m_topic_id; + ecal_reg_sample_topic.uname = Process::GetUnitName(); + + // unregister subscriber + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterTopic(m_topic_name, m_topic_id, ecal_unreg_sample, true); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::Unregister"); +#endif + +#endif // ECAL_CORE_REGISTRATION + return(true); } bool CDataReader::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) { auto current_val = m_attr.find(attr_name_); - bool force = current_val == m_attr.end() || current_val->second != attr_value_; + const bool force = current_val == m_attr.end() || current_val->second != attr_value_; m_attr[attr_name_] = attr_value_; #ifndef NDEBUG @@ -332,7 +356,7 @@ namespace eCAL #endif // register it - DoRegister(force); + Register(force); return(true); } @@ -349,7 +373,7 @@ namespace eCAL #endif // register it - DoRegister(force); + Register(force); return(true); } @@ -360,7 +384,7 @@ namespace eCAL std::unique_lock read_buffer_lock(m_read_buf_mutex); - // No need to wait (for whatever time) if something has been received) + // No need to wait (for whatever time) if something has been received if (!m_read_buf_received) { if (rcv_timeout_ms_ < 0) @@ -386,7 +410,7 @@ namespace eCAL m_read_buf_received = false; // apply time - if(time_) *time_ = m_read_time; + if(time_ != nullptr) *time_ = m_read_time; // return success return(true); @@ -395,25 +419,24 @@ namespace eCAL return(false); } - size_t CDataReader::AddSample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eCAL::pb::eTLayerType layer_) + size_t CDataReader::AddSample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_) { // ensure thread safety - std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_sync); if (!m_created) return(0); // store receive layer - m_use_udp_mc_confirmed |= layer_ == eCAL::pb::tl_ecal_udp_mc; - m_use_shm_confirmed |= layer_ == eCAL::pb::tl_ecal_shm; - m_use_tcp_confirmed |= layer_ == eCAL::pb::tl_ecal_tcp; - m_use_inproc_confirmed |= layer_ == eCAL::pb::tl_inproc; + m_use_udp_mc_confirmed |= layer_ == tl_ecal_udp_mc; + m_use_shm_confirmed |= layer_ == tl_ecal_shm; + m_use_tcp_confirmed |= layer_ == tl_ecal_tcp; + + // number of hash values to track for duplicates + constexpr int hash_queue_size(64); // use hash to discard multiple receives of the same payload - // first we remove outdated hashes - m_sample_hash.remove_deprecated(); - // if this hash is still in the map - // we received it recently (on another transport layer ?) + // if a hash is in the queue we received this message recently (on another transport layer ?) // so we return and do not process this sample again - if (m_sample_hash.find(hash_) != m_sample_hash.end()) + if(std::find(m_sample_hash_queue.begin(), m_sample_hash_queue.end(), hash_) != m_sample_hash_queue.end()) { #ifndef NDEBUG // log it @@ -421,8 +444,11 @@ namespace eCAL #endif return(size_); } - // this is a new sample -> store it's hash - m_sample_hash[hash_] = 0; + // this is a new sample -> store its hash + m_sample_hash_queue.push_back(hash_); + + // limit size of hash queue to the last 64 messages + while (m_sample_hash_queue.size() > hash_queue_size) m_sample_hash_queue.pop_front(); // check id if (!m_id_set.empty()) @@ -433,7 +459,7 @@ namespace eCAL // check the current message clock // if the function returns false we detected // - a dropped message - // - an out of order message + // - an out-of-order message // - a multiple sent message if (!CheckMessageClock(tid_, clock_)) { @@ -482,7 +508,7 @@ namespace eCAL if(!processed) { // push sample into read buffer - std::lock_guard read_buffer_lock(m_read_buf_mutex); + const std::lock_guard read_buffer_lock(m_read_buf_mutex); m_read_buf.clear(); m_read_buf.assign(payload_, payload_ + size_); m_read_time = time_; @@ -505,12 +531,12 @@ namespace eCAL // store receive callback { - std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_sync); #ifndef NDEBUG // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddReceiveCallback"); #endif - m_receive_callback = callback_; + m_receive_callback = std::move(callback_); } return(true); @@ -522,7 +548,7 @@ namespace eCAL // reset receive callback { - std::lock_guard lock(m_receive_callback_sync); + const std::lock_guard lock(m_receive_callback_sync); #ifndef NDEBUG // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemReceiveCallback"); @@ -539,12 +565,12 @@ namespace eCAL // store event callback { - std::lock_guard lock(m_event_callback_map_sync); #ifndef NDEBUG // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddEventCallback"); #endif - m_event_callback_map[type_] = callback_; + const std::lock_guard lock(m_event_callback_map_sync); + m_event_callback_map[type_] = std::move(callback_); } return(true); @@ -556,56 +582,77 @@ namespace eCAL // reset event callback { - std::lock_guard lock(m_event_callback_map_sync); #ifndef NDEBUG // log it Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemEventCallback"); #endif + const std::lock_guard lock(m_event_callback_map_sync); m_event_callback_map[type_] = nullptr; } return(true); } - bool CDataReader::SetTimeout(int timeout_) - { - m_receive_timeout = timeout_; - return(false); - } - void CDataReader::SetID(const std::set& id_set_) { m_id_set = id_set_; } - void CDataReader::ApplyLocPublication(const std::string& process_id_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_) + void CDataReader::ApplyLocPublication(const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_) { - Connect(tid_, ttype_, tdesc_); + Connect(tid_, tinfo_); + + // add key to local publisher map + const std::string topic_key = process_id_ + tid_; { - std::lock_guard lock(m_pub_map_sync); - m_loc_pub_map[process_id_] = true; + const std::lock_guard lock(m_pub_map_sync); + m_loc_pub_map[topic_key] = true; } + m_loc_published = true; } - void CDataReader::ApplyExtPublication(const std::string& host_name_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_) + void CDataReader::RemoveLocPublication(const std::string& process_id_, const std::string& tid_) + { + // remove key from local publisher map + const std::string topic_key = process_id_ + tid_; + { + const std::lock_guard lock(m_pub_map_sync); + m_loc_pub_map.erase(topic_key); + } + } + + void CDataReader::ApplyExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_) { - Connect(tid_, ttype_, tdesc_); + Connect(tid_, tinfo_); + + // add key to external publisher map + const std::string topic_key = host_name_ + process_id_ + tid_; { - std::lock_guard lock(m_pub_map_sync); - m_ext_pub_map[host_name_] = true; + const std::lock_guard lock(m_pub_map_sync); + m_ext_pub_map[topic_key] = true; } + m_ext_published = true; } - void CDataReader::ApplyLocLayerParameter(const std::string& process_id_, eCAL::pb::eTLayerType type_, const std::string& parameter_) + void CDataReader::RemoveExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_) + { + // remove key from external publisher map + const std::string topic_key = host_name_ + process_id_ + tid_; + { + const std::lock_guard lock(m_pub_map_sync); + m_ext_pub_map.erase(topic_key); + } + } + + void CDataReader::ApplyLocLayerParameter(const std::string& process_id_, const std::string& topic_id_, eTLayerType type_, const Registration::ConnectionPar& parameter_) { // process only for shm and tcp layer switch (type_) { - case eCAL::pb::tl_ecal_shm: - break; - case eCAL::pb::tl_ecal_tcp: + case tl_ecal_shm: + case tl_ecal_tcp: break; default: return; @@ -615,50 +662,54 @@ namespace eCAL par.host_name = m_host_name; par.process_id = process_id_; par.topic_name = m_topic_name; - par.topic_id = m_topic_id; + par.topic_id = topic_id_; par.parameter = parameter_; switch (type_) { - case eCAL::pb::tl_ecal_shm: + case tl_ecal_shm: +#if ECAL_CORE_TRANSPORT_SHM CSHMReaderLayer::Get()->SetConnectionParameter(par); +#endif break; - case eCAL::pb::tl_ecal_tcp: + case tl_ecal_tcp: +#if ECAL_CORE_TRANSPORT_TCP CTCPReaderLayer::Get()->SetConnectionParameter(par); +#endif break; default: break; } } - void CDataReader::ApplyExtLayerParameter(const std::string& host_name_, eCAL::pb::eTLayerType type_, const std::string& parameter_) + void CDataReader::ApplyExtLayerParameter(const std::string& host_name_, eTLayerType type_, const Registration::ConnectionPar& parameter_) { // process only for tcp layer switch (type_) { - case eCAL::pb::tl_ecal_tcp: - break; - default: - return; - } - - SReaderLayerPar par; - par.host_name = host_name_; - par.topic_name = m_topic_name; - par.topic_id = m_topic_id; - par.parameter = parameter_; - - switch (type_) + case tl_ecal_tcp: +#if ECAL_CORE_TRANSPORT_TCP { - case eCAL::pb::tl_ecal_tcp: - CTCPReaderLayer::Get()->SetConnectionParameter(par); + SReaderLayerPar par; + par.host_name = host_name_; + par.topic_name = m_topic_name; + par.topic_id = m_topic_id; + par.parameter = parameter_; + + // process only for tcp layer + CTCPReaderLayer::Get()->SetConnectionParameter(par); + } +#else + (void)host_name_; + (void)parameter_; +#endif break; default: - break; + return; } } - void CDataReader::Connect(const std::string& tid_, const std::string& ttype_, const std::string& tdesc_) + void CDataReader::Connect(const std::string& tid_, const SDataTypeInformation& tinfo_) { SSubEventCallbackData data; data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); @@ -670,24 +721,29 @@ namespace eCAL // fire sub_event_connected { + const std::lock_guard lock(m_event_callback_map_sync); auto iter = m_event_callback_map.find(sub_event_connected); - if (iter != m_event_callback_map.end()) + if (iter != m_event_callback_map.end() && iter->second) { - data.type = sub_event_connected; + data.type = sub_event_connected; + data.tid = tid_; + data.tdatatype = tinfo_; (iter->second)(m_topic_name.c_str(), &data); } } } // fire sub_event_update_connection - auto iter = m_event_callback_map.find(sub_event_update_connection); - if (iter != m_event_callback_map.end()) { - data.type = sub_event_update_connection; - data.tid = tid_; - data.ttype = ttype_; - data.tdesc = tdesc_; - (iter->second)(m_topic_name.c_str(), &data); + const std::lock_guard lock(m_event_callback_map_sync); + 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); + } } } @@ -698,14 +754,17 @@ namespace eCAL m_connected = false; // fire sub_event_disconnected - auto iter = m_event_callback_map.find(sub_event_disconnected); - if (iter != m_event_callback_map.end()) { - 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); + const std::lock_guard lock(m_event_callback_map_sync); + 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); + } } } } @@ -724,8 +783,8 @@ namespace eCAL else { // calculate difference - long long last_clock = iter->second; - long long clock_difference = current_clock_ - last_clock; + const long long last_clock = iter->second; + const long long clock_difference = current_clock_ - last_clock; // this is perfect, the next message arrived if (clock_difference == 1) @@ -765,15 +824,19 @@ namespace eCAL Logging::Log(log_level_warning, msg); #endif // we fire the message drop event - auto citer = m_event_callback_map.find(sub_event_dropped); - if (citer != m_event_callback_map.end()) { - SSubEventCallbackData data; - 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); + const std::lock_guard lock(m_event_callback_map_sync); + auto citer = m_event_callback_map.find(sub_event_dropped); + if (citer != m_event_callback_map.end() && citer->second) + { + SSubEventCallbackData data; + 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); + } } + // increase the drop counter m_message_drops += clock_difference; @@ -791,7 +854,7 @@ namespace eCAL // ----------------------------------- // drop messages in the wrong order // ----------------------------------- - if (eCAL::Config::Experimental::GetDropOutOfOrderMessages()) + if (Config::Experimental::GetDropOutOfOrderMessages()) { // do not update the internal clock counter @@ -857,12 +920,12 @@ namespace eCAL } // register without send - DoRegister(false); + Register(false); // check connection timeouts - std::shared_ptr> loc_timeouts = std::make_shared>(); + const std::shared_ptr> loc_timeouts = std::make_shared>(); { - std::lock_guard lock(m_pub_map_sync); + const std::lock_guard lock(m_pub_map_sync); m_loc_pub_map.remove_deprecated(loc_timeouts.get()); m_ext_pub_map.remove_deprecated(); @@ -876,60 +939,28 @@ namespace eCAL } } - void CDataReader::CheckReceiveTimeout() - { - // check receive timeout - if(m_receive_timeout > 0) - { - m_receive_time += CMN_DATAREADER_TIMEOUT_DTIME; - if(m_receive_time > m_receive_timeout) - { - std::lock_guard lock(m_event_callback_map_sync); - auto iter = m_event_callback_map.find(sub_event_timeout); - if(iter != m_event_callback_map.end()) - { - SSubEventCallbackData data; - data.type = sub_event_timeout; - 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); - m_receive_time = 0; - } - } - } - } - - const std::string CDataReader::GetDescription() const - { - std::string topic_desc; - if(g_descgate() && g_descgate()->GetTopicDescription(m_topic_name, topic_desc)) - { - return(topic_desc); - } - return(""); - } - std::string CDataReader::Dump(const std::string& indent_ /* = "" */) { std::stringstream out; out << std::endl; - out << indent_ << "--------------------------------" << std::endl; - out << indent_ << " class CDataReader " << std::endl; - out << indent_ << "--------------------------------" << std::endl; - out << indent_ << "m_host_name: " << m_host_name << std::endl; - out << indent_ << "m_host_id: " << m_host_id << std::endl; - out << indent_ << "m_topic_name: " << m_topic_name << std::endl; - out << indent_ << "m_topic_id: " << m_topic_id << std::endl; - out << indent_ << "m_topic_type: " << m_topic_type << std::endl; - out << indent_ << "m_topic_desc: " << m_topic_desc << std::endl; - out << indent_ << "m_topic_size: " << m_topic_size << std::endl; - out << indent_ << "m_read_buf.size(): " << m_read_buf.size() << std::endl; - out << indent_ << "m_read_time: " << m_read_time << std::endl; - out << indent_ << "m_clock: " << m_clock << std::endl; - out << indent_ << "m_rec_time: " << std::chrono::duration_cast(m_rec_time.time_since_epoch()).count() << std::endl; - out << indent_ << "m_freq: " << m_freq << std::endl; - out << indent_ << "m_created: " << m_created << std::endl; + out << indent_ << "------------------------------------" << std::endl; + out << indent_ << " class CDataReader " << std::endl; + out << indent_ << "------------------------------------" << std::endl; + out << indent_ << "m_host_name: " << m_host_name << std::endl; + out << indent_ << "m_host_group_name: " << m_host_group_name << std::endl; + out << indent_ << "m_topic_name: " << m_topic_name << std::endl; + out << indent_ << "m_topic_id: " << m_topic_id << std::endl; + out << indent_ << "m_topic_info.encoding: " << m_topic_info.encoding << std::endl; + out << indent_ << "m_topic_info.name: " << m_topic_info.name << std::endl; + out << indent_ << "m_topic_info.desc: " << m_topic_info.descriptor << std::endl; + out << indent_ << "m_topic_size: " << m_topic_size << std::endl; + out << indent_ << "m_read_buf.size(): " << m_read_buf.size() << std::endl; + out << indent_ << "m_read_time: " << m_read_time << std::endl; + out << indent_ << "m_clock: " << m_clock << std::endl; + out << indent_ << "m_rec_time: " << std::chrono::duration_cast(m_rec_time.time_since_epoch()).count() << std::endl; + out << indent_ << "m_freq: " << m_freq << std::endl; + out << indent_ << "m_created: " << m_created << std::endl; out << std::endl; return(out.str()); diff --git a/ecal/core/src/readwrite/ecal_reader.h b/src/core/src/readwrite/ecal_reader.h similarity index 68% rename from ecal/core/src/readwrite/ecal_reader.h rename to src/core/src/readwrite/ecal_reader.h index 6a3ee57..3987e30 100644 --- a/ecal/core/src/readwrite/ecal_reader.h +++ b/src/core/src/readwrite/ecal_reader.h @@ -5,9 +5,9 @@ * 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. @@ -25,21 +25,17 @@ #include #include +#include -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ecal_expmap.h" +#include "serialization/ecal_serialize_sample_payload.h" +#include "serialization/ecal_serialize_sample_registration.h" +#include "util/ecal_expmap.h" #include #include #include #include +#include #include #include @@ -54,11 +50,9 @@ namespace eCAL static void InitializeLayers(); - bool Create(const std::string& topic_name_, const std::string& topic_type_, const std::string& topic_desc_); + bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_); bool Destroy(); - bool SetQOS(const QOS::SReaderQOS& qos_); - bool Receive(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ms_ = 0); bool AddReceiveCallback(ReceiveCallbackT callback_); @@ -67,63 +61,61 @@ namespace eCAL bool AddEventCallback(eCAL_Subscriber_Event type_, SubEventCallbackT callback_); bool RemEventCallback(eCAL_Subscriber_Event type_); - bool SetTimeout(int timeout_); - bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); bool ClearAttribute(const std::string& attr_name_); void SetID(const std::set& id_set_); - void ApplyLocPublication(const std::string& process_id_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_); - void ApplyExtPublication(const std::string& host_name_, const std::string& tid_, const std::string& ttype_, const std::string& tdesc_); + void ApplyLocPublication(const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_); + void RemoveLocPublication(const std::string& process_id_, const std::string& tid_); - void ApplyLocLayerParameter(const std::string& process_id_, eCAL::pb::eTLayerType type_, const std::string& parameter_); - void ApplyExtLayerParameter(const std::string& host_name_, eCAL::pb::eTLayerType type_, const std::string& parameter_); + void ApplyExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_, const SDataTypeInformation& tinfo_); + void RemoveExtPublication(const std::string& host_name_, const std::string& process_id_, const std::string& tid_); + + void ApplyLocLayerParameter(const std::string& process_id_, const std::string& topic_id_, eTLayerType type_, const Registration::ConnectionPar& parameter_); + void ApplyExtLayerParameter(const std::string& host_name_, eTLayerType type_, const Registration::ConnectionPar& parameter_); std::string Dump(const std::string& indent_ = ""); - bool IsCreated() const {return(m_created);} + bool IsCreated() const { return(m_created); } size_t GetPublisherCount() const { - std::lock_guard lock(m_pub_map_sync); + const std::lock_guard lock(m_pub_map_sync); return(m_loc_pub_map.size() + m_ext_pub_map.size()); } - const std::string GetTopicName() const {return(m_topic_name);} - const std::string GetTopicID() const {return(m_topic_id);} - const std::string GetTypeName() const {return(m_topic_type);} - const std::string GetDescription() const; + std::string GetTopicName() const { return(m_topic_name); } + std::string GetTopicID() const { return(m_topic_id); } + SDataTypeInformation GetDataTypeInformation() const { return(m_topic_info); } void RefreshRegistration(); - void CheckReceiveTimeout(); - size_t AddSample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eCAL::pb::eTLayerType layer_); + size_t AddSample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_); protected: void SubscribeToLayers(); void UnsubscribeFromLayers(); - bool DoRegister(const bool force_); - void Connect(const std::string& tid_, const std::string& ttype_, const std::string& tdesc_); + bool Register(bool force_); + bool Unregister(); + + void Connect(const std::string& tid_, const SDataTypeInformation& tinfo_); void Disconnect(); bool CheckMessageClock(const std::string& tid_, long long current_clock_); std::string m_host_name; - int m_host_id; + std::string m_host_group_name; int m_pid; std::string m_pname; std::string m_topic_name; std::string m_topic_id; - std::string m_topic_type; - std::string m_topic_desc; + SDataTypeInformation m_topic_info; std::map m_attr; std::atomic m_topic_size; - QOS::SReaderQOS m_qos; - std::atomic m_connected; - typedef Util::CExpMap ConnectedMapT; + using ConnectedMapT = Util::CExpMap; mutable std::mutex m_pub_map_sync; ConnectedMapT m_loc_pub_map; ConnectedMapT m_ext_pub_map; @@ -136,14 +128,12 @@ namespace eCAL std::mutex m_receive_callback_sync; ReceiveCallbackT m_receive_callback; - std::atomic m_receive_timeout; std::atomic m_receive_time; - typedef Util::CExpMap SampleHashMapT; - SampleHashMapT m_sample_hash; + std::deque m_sample_hash_queue; + using EventCallbackMapT = std::map; std::mutex m_event_callback_map_sync; - typedef std::map EventCallbackMapT; EventCallbackMapT m_event_callback_map; std::atomic m_clock; @@ -152,8 +142,8 @@ namespace eCAL long m_freq; std::set m_id_set; - - typedef std::unordered_map WriterCounterMapT; + + using WriterCounterMapT = std::unordered_map; WriterCounterMapT m_writer_counter_map; long long m_message_drops; @@ -166,7 +156,6 @@ namespace eCAL bool m_use_udp_mc_confirmed; bool m_use_shm_confirmed; bool m_use_tcp_confirmed; - bool m_use_inproc_confirmed; std::atomic m_created; }; diff --git a/ecal/core/src/readwrite/ecal_reader_layer.h b/src/core/src/readwrite/ecal_reader_layer.h similarity index 82% rename from ecal/core/src/readwrite/ecal_reader_layer.h rename to src/core/src/readwrite/ecal_reader_layer.h index 9db0e73..978b75f 100644 --- a/ecal/core/src/readwrite/ecal_reader_layer.h +++ b/src/core/src/readwrite/ecal_reader_layer.h @@ -23,7 +23,7 @@ #pragma once -#include +#include "serialization/ecal_struct_sample_registration.h" #include #include @@ -34,11 +34,11 @@ namespace eCAL // transmitted from a writer to a reader struct SReaderLayerPar { - std::string host_name; - std::string process_id; - std::string topic_name; - std::string topic_id; - std::string parameter; + std::string host_name; + std::string process_id; + std::string topic_name; + std::string topic_id; + Registration::ConnectionPar parameter; }; // ecal data layer base class @@ -55,12 +55,12 @@ namespace eCAL virtual void Initialize() = 0; // activate / create a specific subscription - virtual void AddSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, QOS::SReaderQOS qos_) = 0; + virtual void AddSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) = 0; // deactivate / destroy a specific subscription virtual void RemSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) = 0; - // connection paramter from writer side + // connection parameter from writer side virtual void SetConnectionParameter(SReaderLayerPar& par_) = 0; static std::shared_ptr Get() @@ -74,4 +74,4 @@ namespace eCAL } }; -}; +} diff --git a/src/core/src/readwrite/ecal_writer.cpp b/src/core/src/readwrite/ecal_writer.cpp new file mode 100644 index 0000000..d37257a --- /dev/null +++ b/src/core/src/readwrite/ecal_writer.cpp @@ -0,0 +1,1256 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief common eCAL data writer +**/ + +#include +#include +#include + +#include "ecal_def.h" +#include "config/ecal_config_reader_hlp.h" + +#if ECAL_CORE_REGISTRATION +#include "registration/ecal_registration_provider.h" +#endif + +#include "ecal_writer.h" +#include "ecal_writer_base.h" +#include "ecal_writer_buffer_payload.h" + +#include "pubsub/ecal_pubgate.h" + +#include +#include + +struct SSndHash +{ + SSndHash(std::string t, long long c) : topic_id(t), snd_clock(c) {} + std::string topic_id; + long long snd_clock; +}; + +namespace std +{ + template<> + class hash { + public: + size_t operator()(const SSndHash& h) const + { + const size_t h1 = std::hash()(h.topic_id); + const size_t h2 = std::hash()(h.snd_clock); + return h1 ^ (h2 << 1); + } + }; +} + +namespace eCAL +{ + CDataWriter::CDataWriter() : + m_host_name(Process::GetHostName()), + m_host_group_name(Process::GetHostGroupName()), + m_pid(Process::GetProcessID()), + m_pname(Process::GetProcessName()), + m_topic_size(0), + m_buffering_shm(PUB_MEMFILE_BUF_COUNT), + m_zero_copy(PUB_MEMFILE_ZERO_COPY), + m_acknowledge_timeout_ms(PUB_MEMFILE_ACK_TO), + m_connected(false), + m_id(0), + m_clock(0), + m_clock_old(0), + m_freq(0), + m_loc_subscribed(false), + m_ext_subscribed(false), + m_use_ttype(true), + m_use_tdesc(true), + m_share_ttype(-1), + m_share_tdesc(-1), + m_created(false) + { + // initialize layer modes with configuration settings +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc_mode.requested = Config::GetPublisherUdpMulticastMode(); +#endif +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm_mode.requested = Config::GetPublisherShmMode(); +#endif +#if ECAL_CORE_TRANSPORT_TCP + m_writer.tcp_mode.requested = Config::GetPublisherTcpMode(); +#endif + } + + CDataWriter::~CDataWriter() + { + Destroy(); + } + + bool CDataWriter::Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_) + { + if (m_created) return(false); + + // set defaults + m_topic_name = topic_name_; + m_topic_id.clear(); + m_topic_info = topic_info_; + m_id = 0; + m_clock = 0; + m_clock_old = 0; + m_snd_time = std::chrono::steady_clock::time_point(); + m_freq = 0; + m_buffering_shm = Config::GetMemfileBufferCount(); + m_zero_copy = Config::IsMemfileZerocopyEnabled(); + m_acknowledge_timeout_ms = Config::GetMemfileAckTimeoutMs(); + m_connected = false; + m_ext_subscribed = false; + m_created = false; + + // build topic id + std::stringstream counter; + 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_loc_sub_map.set_expiration(registration_timeout); + m_ext_sub_map.set_expiration(registration_timeout); + + // allow to share topic type + m_use_ttype = Config::IsTopicTypeSharingEnabled(); + + // allow to share topic description + m_use_tdesc = Config::IsTopicDescriptionSharingEnabled(); + + // register + Register(false); + + // mark as created + m_created = true; + + // create udp multicast layer + SetUseUdpMC(m_writer.udp_mc_mode.requested); + + // create shm layer + SetUseShm(m_writer.shm_mode.requested); + + // create tcp layer + SetUseTcp(m_writer.tcp_mode.requested); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Created"); +#endif + + // adapt number of used memory file + ShmSetBufferCount(m_buffering_shm); + + return(true); + } + + bool CDataWriter::Destroy() + { + if (!m_created) return(false); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Destroy"); +#endif + + // destroy udp multicast writer +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc.Destroy(); +#endif + + // destroy memory file writer +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm.Destroy(); +#endif + + // destroy tcp writer +#if ECAL_CORE_TRANSPORT_TCP + m_writer.tcp.Destroy(); +#endif + + // reset defaults + m_id = 0; + m_clock = 0; + m_clock_old = 0; + m_snd_time = std::chrono::steady_clock::time_point(); + m_freq = 0; + m_buffering_shm = Config::GetMemfileBufferCount(); + m_zero_copy = Config::IsMemfileZerocopyEnabled(); + m_acknowledge_timeout_ms = Config::GetMemfileAckTimeoutMs(); + m_connected = false; + + // reset subscriber maps + { + const std::lock_guard lock(m_sub_map_sync); + m_loc_sub_map.clear(); + m_ext_sub_map.clear(); + } + + // reset event callback map + { + const std::lock_guard lock(m_event_callback_map_sync); + m_event_callback_map.clear(); + } + + // unregister + Unregister(); + + m_created = false; + + return(true); + } + + bool CDataWriter::SetDataTypeInformation(const SDataTypeInformation& topic_info_) + { + // Does it even make sense to register if the info is the same??? + const bool force = m_topic_info != topic_info_; + m_topic_info = topic_info_; + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetDescription"); +#endif + + // register it + Register(force); + + return(true); + } + + bool CDataWriter::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) + { + auto current_val = m_attr.find(attr_name_); + + const bool force = current_val == m_attr.end() || current_val->second != attr_value_; + m_attr[attr_name_] = attr_value_; + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetAttribute"); +#endif + + // register it + Register(force); + + return(true); + } + + bool CDataWriter::ClearAttribute(const std::string& attr_name_) + { + auto force = m_attr.find(attr_name_) != m_attr.end(); + + m_attr.erase(attr_name_); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ClearAttribute"); +#endif + + // register it + Register(force); + + return(true); + } + + void CDataWriter::ShareType(bool state_) + { + if (state_) + { + m_share_ttype = 1; + } + else + { + m_share_ttype = 0; + } + } + + void CDataWriter::ShareDescription(bool state_) + { + if (state_) + { + m_share_tdesc = 1; + } + else + { + m_share_tdesc = 0; + } + } + + bool CDataWriter::SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_) + { + switch (layer_) + { + case TLayer::tlayer_udp_mc: + SetUseUdpMC(mode_); + break; + case TLayer::tlayer_shm: + SetUseShm(mode_); + break; + case TLayer::tlayer_tcp: + SetUseTcp(mode_); + break; + case TLayer::tlayer_all: + SetUseUdpMC(mode_); + SetUseShm(mode_); + SetUseTcp(mode_); + break; + default: + break; + } + return true; + } + + bool CDataWriter::ShmSetBufferCount(size_t buffering_) + { +#if ECAL_CORE_TRANSPORT_SHM + if (buffering_ < 1) + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::ShmSetBufferCount minimal number of memory files is 1 !"); + return false; + } + m_buffering_shm = static_cast(buffering_); + + // adapt number of used memory files + if (m_created) + { + m_writer.shm.SetBufferCount(buffering_); + } + + return true; +#else + return false; +#endif + } + + bool CDataWriter::ShmEnableZeroCopy(bool state_) + { +#if ECAL_CORE_TRANSPORT_SHM + m_zero_copy = state_; + return true; +#else + return false; +#endif + } + + bool CDataWriter::ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_) + { +#if ECAL_CORE_TRANSPORT_SHM + m_acknowledge_timeout_ms = acknowledge_timeout_ms_; + return true; +#else + return false; +#endif + } + + long long CDataWriter::ShmGetAcknowledgeTimeout() const + { + return m_acknowledge_timeout_ms; + } + + bool CDataWriter::AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_) + { + if (!m_created) return(false); + + // store event callback + { +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::AddEventCallback"); +#endif + const std::lock_guard lock(m_event_callback_map_sync); + m_event_callback_map[type_] = std::move(callback_); + } + + return(true); + } + + bool CDataWriter::RemEventCallback(eCAL_Publisher_Event type_) + { + if (!m_created) return(false); + + // reset event callback + { +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::RemEventCallback"); +#endif + const std::lock_guard lock(m_event_callback_map_sync); + m_event_callback_map[type_] = nullptr; + } + + return(true); + } + + size_t CDataWriter::Write(CPayloadWriter& payload_, long long time_, long long id_) + { + // check writer modes + if (!CheckWriterModes()) + { + // incompatible writer configurations + return 0; + } + + // get payload buffer size (one time, to avoid multiple computations) + const size_t payload_buf_size(payload_.GetSize()); + + // can we do a zero copy write ? + const bool allow_zero_copy = + m_zero_copy // zero copy mode activated by user + && m_writer.shm_mode.activated // shm layer active + && !m_writer.udp_mc_mode.activated + && !m_writer.tcp_mode.activated; + + // create a payload copy for all layer + if (!allow_zero_copy) + { + m_payload_buffer.resize(payload_buf_size); + payload_.WriteFull(m_payload_buffer.data(), m_payload_buffer.size()); + } + + // prepare counter and internal states + const size_t snd_hash = PrepareWrite(id_, payload_buf_size); + + // did we write anything + bool written(false); + + //////////////////////////////////////////////////////////////////////////// + // SHM + //////////////////////////////////////////////////////////////////////////// +#if ECAL_CORE_TRANSPORT_SHM + if (m_writer.shm_mode.activated) + { +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::SHM"); +#endif + + // send it + bool shm_sent(false); + { + // fill writer data + struct SWriterAttr wattr; + wattr.len = payload_buf_size; + wattr.id = m_id; + wattr.clock = m_clock; + wattr.hash = snd_hash; + wattr.time = time_; + wattr.buffering = m_buffering_shm; + wattr.zero_copy = m_zero_copy; + wattr.acknowledge_timeout_ms = m_acknowledge_timeout_ms; + + // prepare send + if (m_writer.shm.PrepareWrite(wattr)) + { + // register new to update listening subscribers and rematch + Register(true); + Process::SleepMS(5); + } + + // we are the only active layer, and we support zero copy -> we do a zero copy write via payload + if (allow_zero_copy) + { + // write to shm layer (write content into the opened memory file without additional copy) + shm_sent = m_writer.shm.Write(payload_, wattr); + } + // multiple layer are active -> we make a copy and use that one + else + { + // wrap the buffer into a payload object + CBufferPayloadWriter payload_buf(m_payload_buffer.data(), m_payload_buffer.size()); + // write to shm layer (write content into the opened memory file without additional copy) + shm_sent = m_writer.shm.Write(payload_buf, wattr); + } + + m_writer.shm_mode.confirmed = true; + } + written |= shm_sent; + +#ifndef NDEBUG + // log it + if (shm_sent) + { + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::SHM - SUCCESS"); + } + else + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::SHM - FAILED"); + } +#endif + } +#endif // ECAL_CORE_TRANSPORT_SHM + + //////////////////////////////////////////////////////////////////////////// + // UDP (MC) + //////////////////////////////////////////////////////////////////////////// +#if ECAL_CORE_TRANSPORT_UDP + if (m_writer.udp_mc_mode.activated) + { +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::UDP_MC"); +#endif + + // send it + bool udp_mc_sent(false); + { +#if ECAL_CORE_TRANSPORT_SHM + // if shared memory layer for local communication is switched off + // we activate udp message loopback to communicate with local processes too + const bool loopback = m_writer.shm_mode.requested == TLayer::smode_off; +#else + const bool loopback = true; +#endif + + // fill writer data + struct SWriterAttr wattr; + wattr.len = payload_buf_size; + wattr.id = m_id; + wattr.clock = m_clock; + wattr.hash = snd_hash; + wattr.time = time_; + wattr.loopback = loopback; + + // prepare send + if (m_writer.udp_mc.PrepareWrite(wattr)) + { + // register new to update listening subscribers and rematch + Register(true); + Process::SleepMS(5); + } + + // write to udp multicast layer + udp_mc_sent = m_writer.udp_mc.Write(m_payload_buffer.data(), wattr); + m_writer.udp_mc_mode.confirmed = true; + } + written |= udp_mc_sent; + +#ifndef NDEBUG + // log it + if (udp_mc_sent) + { + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::UDP_MC - SUCCESS"); + } + else + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::UDP_MC - FAILED"); + } +#endif + } +#endif // ECAL_CORE_TRANSPORT_UDP + + //////////////////////////////////////////////////////////////////////////// + // TCP + //////////////////////////////////////////////////////////////////////////// +#if ECAL_CORE_TRANSPORT_TCP + if (m_writer.tcp_mode.activated) + { +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::TCP"); +#endif + + // send it + bool tcp_sent(false); + { + // fill writer data + struct SWriterAttr wattr; + wattr.len = payload_buf_size; + wattr.id = m_id; + wattr.clock = m_clock; + wattr.hash = snd_hash; + wattr.time = time_; + + // write to tcp layer + tcp_sent = m_writer.tcp.Write(m_payload_buffer.data(), wattr); + m_writer.tcp_mode.confirmed = true; + } + written |= tcp_sent; + +#ifndef NDEBUG + // log it + if (tcp_sent) + { + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::TCP - SUCCESS"); + } + else + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::TCP - FAILED"); + } +#endif + } +#endif // ECAL_CORE_TRANSPORT_TCP + + // return success + if (written) return payload_buf_size; + else return 0; + } + + void CDataWriter::ApplyLocSubscription(const SLocalSubscriptionInfo& local_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_) + { + Connect(local_info_.topic_id, tinfo_); + + // add key to local subscriber map + { + const std::lock_guard lock(m_sub_map_sync); + m_loc_sub_map[local_info_] = true; + } + + m_loc_subscribed = true; + + // add a new local subscription +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc.AddLocConnection(local_info_.process_id, local_info_.topic_id, reader_par_); +#endif +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm.AddLocConnection(local_info_.process_id, local_info_.topic_id, reader_par_); +#endif +#if ECAL_CORE_TRANSPORT_TCP + m_writer.tcp.AddLocConnection(local_info_.process_id, local_info_.topic_id, reader_par_); +#endif + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplyLocSubscription"); +#endif + } + + void CDataWriter::RemoveLocSubscription(const SLocalSubscriptionInfo& local_info_) + { + // remove key from local subscriber map + { + const std::lock_guard lock(m_sub_map_sync); + m_loc_sub_map.erase(local_info_); + } + + // remove a local subscription +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc.RemLocConnection(local_info_.process_id, local_info_.topic_id); +#endif +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm.RemLocConnection(local_info_.process_id, local_info_.topic_id); +#endif +#if ECAL_CORE_TRANSPORT_TCP + m_writer.tcp.RemLocConnection(local_info_.process_id, local_info_.topic_id); +#endif + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::RemoveLocSubscription"); +#endif + } + + void CDataWriter::ApplyExtSubscription(const SExternalSubscriptionInfo& external_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_) + { + Connect(external_info_.topic_id, tinfo_); + + // add key to external subscriber map + { + const std::lock_guard lock(m_sub_map_sync); + m_ext_sub_map[external_info_] = true; + } + + m_ext_subscribed = true; + + // add a new external subscription +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc.AddExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id, reader_par_); +#endif +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm.AddExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id, reader_par_); +#endif +#if ECAL_CORE_TRANSPORT_TCP + m_writer.tcp.AddExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id, reader_par_); +#endif + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplyExtSubscription"); +#endif + } + + void CDataWriter::RemoveExtSubscription(const SExternalSubscriptionInfo& external_info_) + { + // remove key from external subscriber map + { + const std::lock_guard lock(m_sub_map_sync); + m_ext_sub_map.erase(external_info_); + } + + // remove external subscription +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc.RemExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id); +#endif +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm.RemExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id); +#endif +#if ECAL_CORE_TRANSPORT_TCP + m_writer.tcp.RemExtConnection(external_info_.host_name, external_info_.process_id, external_info_.topic_id); +#endif + } + + void CDataWriter::RefreshRegistration() + { + if (!m_created) return; + + // force to register every second to refresh data clock information + auto curr_time = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(curr_time - m_snd_time) > std::chrono::milliseconds(0)) + { + // reset clock and time on first call + if (m_clock_old == 0) + { + m_clock_old = m_clock; + m_snd_time = curr_time; + } + + // check for clock difference + if ((m_clock - m_clock_old) > 0) + { + // calculate frequency in mHz + m_freq = static_cast((1000 * 1000 * (m_clock - m_clock_old)) / std::chrono::duration_cast(curr_time - m_snd_time).count()); + // reset clock and time + m_clock_old = m_clock; + m_snd_time = curr_time; + } + else + { + m_freq = 0; + } + } + + // register without send + Register(false); + + // check connection timeouts + { + const std::lock_guard lock(m_sub_map_sync); + m_loc_sub_map.remove_deprecated(); + m_ext_sub_map.remove_deprecated(); + + m_loc_subscribed = !m_loc_sub_map.empty(); + m_ext_subscribed = !m_ext_sub_map.empty(); + } + + if (!m_loc_subscribed && !m_ext_subscribed) + { + Disconnect(); + } + } + + void CDataWriter::RefreshSendCounter() + { + // increase write clock + m_clock++; + + // statistics + g_process_wclock++; + } + + std::string CDataWriter::Dump(const std::string& indent_ /* = "" */) + { + std::stringstream out; + + out << std::endl; + out << indent_ << "--------------------------" << std::endl; + out << indent_ << " class CDataWriter " << std::endl; + out << indent_ << "--------------------------" << std::endl; + out << indent_ << "m_host_name: " << m_host_name << std::endl; + out << indent_ << "m_host_group_name: " << m_host_group_name << std::endl; + out << indent_ << "m_topic_name: " << m_topic_name << std::endl; + out << indent_ << "m_topic_id: " << m_topic_id << std::endl; + out << indent_ << "m_topic_info.encoding: " << m_topic_info.encoding << std::endl; + out << indent_ << "m_topic_info.name: " << m_topic_info.name << std::endl; + out << indent_ << "m_topic_info.desc: " << m_topic_info.descriptor << std::endl; + out << indent_ << "m_id: " << m_id << std::endl; + out << indent_ << "m_clock: " << m_clock << std::endl; + out << indent_ << "m_created: " << m_created << std::endl; + out << indent_ << "m_loc_subscribed: " << m_loc_subscribed << std::endl; + out << indent_ << "m_ext_subscribed: " << m_ext_subscribed << std::endl; + out << std::endl; + + return(out.str()); + } + + bool CDataWriter::Register(bool force_) + { +#if ECAL_CORE_REGISTRATION + if (m_topic_name.empty()) return(false); + + //@Rex: why is the logic different in CDataReader??? + // check share modes + bool share_ttype(m_use_ttype && (g_pubgate() != nullptr) && g_pubgate()->TypeShared()); + if (m_share_ttype != -1) + { + share_ttype = m_share_ttype == 1; + } + bool share_tdesc(m_use_tdesc && (g_pubgate() != nullptr) && g_pubgate()->DescriptionShared()); + if (m_share_tdesc != -1) + { + share_tdesc = m_share_tdesc == 1; + } + + // create command parameter + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_reg_publisher; + + auto& ecal_reg_sample_topic = ecal_reg_sample.topic; + ecal_reg_sample_topic.hname = m_host_name; + ecal_reg_sample_topic.hgname = m_host_group_name; + ecal_reg_sample_topic.tname = m_topic_name; + ecal_reg_sample_topic.tid = m_topic_id; + if (share_ttype) ecal_reg_sample_topic.ttype = Util::CombinedTopicEncodingAndType(m_topic_info.encoding, m_topic_info.name); + if (share_tdesc) ecal_reg_sample_topic.tdesc = m_topic_info.descriptor; + // topic_information + { + auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; + if (share_ttype) + { + ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; + ecal_reg_sample_tdatatype.name = m_topic_info.name; + } + if (share_tdesc) + { + ecal_reg_sample_tdatatype.desc = m_topic_info.descriptor; + } + } + ecal_reg_sample_topic.attr = m_attr; + ecal_reg_sample_topic.tsize = static_cast(m_topic_size); + +#if ECAL_CORE_TRANSPORT_UDP + // udp multicast layer + { + eCAL::Registration::TLayer udp_tlayer; + udp_tlayer.type = tl_ecal_udp_mc; + udp_tlayer.version = 1; + udp_tlayer.confirmed = m_writer.udp_mc_mode.confirmed; + udp_tlayer.par_layer.layer_par_udpmc = m_writer.udp_mc.GetConnectionParameter().layer_par_udpmc; + ecal_reg_sample_topic.tlayer.push_back(udp_tlayer); + } +#endif + +#if ECAL_CORE_TRANSPORT_SHM + // shm layer + { + eCAL::Registration::TLayer shm_tlayer; + shm_tlayer.type = tl_ecal_shm; + shm_tlayer.version = 1; + shm_tlayer.confirmed = m_writer.shm_mode.confirmed; + shm_tlayer.par_layer.layer_par_shm = m_writer.shm.GetConnectionParameter().layer_par_shm; + ecal_reg_sample_topic.tlayer.push_back(shm_tlayer); + } +#endif + +#if ECAL_CORE_TRANSPORT_TCP + // tcp layer + { + eCAL::Registration::TLayer tcp_tlayer; + tcp_tlayer.type = tl_ecal_tcp; + tcp_tlayer.version = 1; + tcp_tlayer.confirmed = m_writer.tcp_mode.confirmed; + tcp_tlayer.par_layer.layer_par_tcp = m_writer.tcp.GetConnectionParameter().layer_par_tcp; + ecal_reg_sample_topic.tlayer.push_back(tcp_tlayer); + } +#endif + + ecal_reg_sample_topic.pid = m_pid; + ecal_reg_sample_topic.pname = m_pname; + ecal_reg_sample_topic.uname = Process::GetUnitName(); + ecal_reg_sample_topic.did = m_id; + ecal_reg_sample_topic.dclock = m_clock; + ecal_reg_sample_topic.dfreq = m_freq; + + size_t loc_connections(0); + size_t ext_connections(0); + { + const std::lock_guard lock(m_sub_map_sync); + loc_connections = m_loc_sub_map.size(); + ext_connections = m_ext_sub_map.size(); + } + ecal_reg_sample_topic.connections_loc = static_cast(loc_connections); + ecal_reg_sample_topic.connections_ext = static_cast(ext_connections); + + // register publisher + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterTopic(m_topic_name, m_topic_id, ecal_reg_sample, force_); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Register"); +#endif + +#endif // ECAL_CORE_REGISTRATION + return(true); + } + + bool CDataWriter::Unregister() + { +#if ECAL_CORE_REGISTRATION + if (m_topic_name.empty()) return(false); + + // create command parameter + Registration::Sample ecal_unreg_sample; + ecal_unreg_sample.cmd_type = bct_unreg_publisher; + + auto& ecal_reg_sample_topic = ecal_unreg_sample.topic; + ecal_reg_sample_topic.hname = m_host_name; + ecal_reg_sample_topic.hgname = m_host_group_name; + ecal_reg_sample_topic.pname = m_pname; + ecal_reg_sample_topic.pid = m_pid; + ecal_reg_sample_topic.tname = m_topic_name; + ecal_reg_sample_topic.tid = m_topic_id; + ecal_reg_sample_topic.uname = Process::GetUnitName(); + + // unregister publisher + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterTopic(m_topic_name, m_topic_id, ecal_unreg_sample, true); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::UnRegister"); +#endif + +#endif // ECAL_CORE_REGISTRATION + return(true); + } + + void CDataWriter::Connect(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) + { + m_connected = true; + + // fire pub_event_connected + { + const std::lock_guard lock(m_event_callback_map_sync); + 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); + } + } + } + + // fire pub_event_update_connection + { + const std::lock_guard lock(m_event_callback_map_sync); + 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); + } + } + } + + void CDataWriter::Disconnect() + { + if (m_connected) + { + m_connected = false; + + // fire pub_event_disconnected + { + const std::lock_guard lock(m_event_callback_map_sync); + 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); + } + } + } + } + + void CDataWriter::SetUseUdpMC(TLayer::eSendMode mode_) + { +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc_mode.requested = mode_; + if (!m_created) return; + + // log send mode + LogSendMode(mode_, m_topic_name + "::CDataWriter::Create::UDP_MC_SENDMODE::"); + + switch (mode_) + { + case TLayer::eSendMode::smode_auto: + case TLayer::eSendMode::smode_on: + if (m_writer.udp_mc.Create(m_host_name, m_topic_name, m_topic_id)) + { +#ifndef NDEBUG + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::UDP_MC_WRITER - SUCCESS"); +#endif + } + else + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Create::UDP_MC_WRITER - FAILED"); + } + break; + case TLayer::eSendMode::smode_none: + case TLayer::eSendMode::smode_off: + m_writer.udp_mc.Destroy(); + break; + } +#endif // ECAL_CORE_TRANSPORT_UDP + } + + void CDataWriter::SetUseShm(TLayer::eSendMode mode_) + { +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm_mode.requested = mode_; + if (!m_created) return; + + // log send mode + LogSendMode(mode_, m_topic_name + "::CDataWriter::Create::SHM_SENDMODE::"); + + switch (mode_) + { + case TLayer::eSendMode::smode_auto: + case TLayer::eSendMode::smode_on: + if (m_writer.shm.Create(m_host_name, m_topic_name, m_topic_id)) + { +#ifndef NDEBUG + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::SHM_WRITER - SUCCESS"); +#endif + } + else + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Create::SHM_WRITER - FAILED"); + } + break; + case TLayer::eSendMode::smode_none: + case TLayer::eSendMode::smode_off: + m_writer.shm.Destroy(); + break; + } +#endif // ECAL_CORE_TRANSPORT_SHM + } + + void CDataWriter::SetUseTcp(TLayer::eSendMode mode_) + { +#if ECAL_CORE_TRANSPORT_TCP + m_writer.tcp_mode.requested = mode_; + if (!m_created) return; + + // log send mode + LogSendMode(mode_, m_topic_name + "::CDataWriter::Create::TCP_SENDMODE::"); + + switch (mode_) + { + case TLayer::eSendMode::smode_auto: + case TLayer::eSendMode::smode_on: + if (m_writer.tcp.Create(m_host_name, m_topic_name, m_topic_id)) + { +#ifndef NDEBUG + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Create::TCP_WRITER - SUCCESS"); +#endif + } + else + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Create::TCP_WRITER - FAILED"); + } + break; + case TLayer::eSendMode::smode_none: + case TLayer::eSendMode::smode_off: + m_writer.tcp.Destroy(); + break; + } +#else // ECAL_CORE_TRANSPORT_TCP + (void)mode_; +#endif // ECAL_CORE_TRANSPORT_TCP + } + + bool CDataWriter::CheckWriterModes() + { + // if nothing is activated, we use defaults shm = auto, udp = auto + if ((m_writer.udp_mc_mode.requested == TLayer::smode_off) + && (m_writer.shm_mode.requested == TLayer::smode_off) + && (m_writer.tcp_mode.requested == TLayer::smode_off) + ) + { +#if ECAL_CORE_TRANSPORT_UDP + m_writer.udp_mc_mode.requested = TLayer::smode_auto; +#endif +#if ECAL_CORE_TRANSPORT_SHM + m_writer.shm_mode.requested = TLayer::smode_auto; +#endif + } + + // if shm layer is off, we need a local transport layer switch to on + // prio 1: udp = on + // prio 2: tcp = on + if ( (m_writer.shm_mode.requested == TLayer::smode_off) + && (m_writer.shm_mode.requested != TLayer::smode_on) + && (m_writer.tcp_mode.requested != TLayer::smode_on) + ) + { + bool new_local_layer(false); +#if ECAL_CORE_TRANSPORT_UDP + if (m_writer.udp_mc_mode.requested != TLayer::smode_on) + { + m_writer.udp_mc_mode.requested = TLayer::smode_on; + new_local_layer = true; + } +#else + #if ECAL_CORE_TRANSPORT_TCP + if (m_writer.tcp_mode.requested != TLayer::smode_on) + { + m_writer.tcp_mode.requested = TLayer::smode_on; + new_local_layer = true; + } +#endif +#endif + if (new_local_layer) + { + if (m_writer.udp_mc_mode.requested == TLayer::smode_on) + { + Logging::Log(log_level_warning, m_topic_name + "::CDataWriter: Switched to udp for local communication."); + SetUseUdpMC(TLayer::smode_on); + } + if (m_writer.tcp_mode.requested == TLayer::smode_on) + { + Logging::Log(log_level_warning, m_topic_name + "::CDataWriter: Switched to tcp for local communication."); + SetUseTcp(TLayer::smode_on); + } + } + } + + if ( (m_writer.tcp_mode.requested == TLayer::smode_auto) + && (m_writer.udp_mc_mode.requested == TLayer::smode_auto) + ) + { + Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send: TCP layer and UDP layer are both set to auto mode - Publication failed !"); + return false; + } + +#if ECAL_CORE_TRANSPORT_UDP + //////////////////////////////////////////////////////////////////////////// + // UDP (MC) + //////////////////////////////////////////////////////////////////////////// + if (((m_writer.udp_mc_mode.requested == TLayer::smode_auto) && m_ext_subscribed) + || (m_writer.udp_mc_mode.requested == TLayer::smode_on) + ) + { + m_writer.udp_mc_mode.activated = true; + } +#endif + +#if ECAL_CORE_TRANSPORT_SHM + //////////////////////////////////////////////////////////////////////////// + // SHM + //////////////////////////////////////////////////////////////////////////// + if (((m_writer.shm_mode.requested == TLayer::smode_auto) && m_loc_subscribed) + || (m_writer.shm_mode.requested == TLayer::smode_on) + ) + { + m_writer.shm_mode.activated = true; + } +#endif + +#if ECAL_CORE_TRANSPORT_TCP + //////////////////////////////////////////////////////////////////////////// + // TCP + //////////////////////////////////////////////////////////////////////////// + if (((m_writer.tcp_mode.requested == TLayer::smode_auto) && m_ext_subscribed) + || (m_writer.tcp_mode.requested == TLayer::smode_on) + ) + { + m_writer.tcp_mode.activated = true; + } +#endif + + return true; + } + + size_t CDataWriter::PrepareWrite(long long id_, size_t len_) + { + // store id + m_id = id_; + + // handle write counters + RefreshSendCounter(); + + // calculate unique send hash + const std::hash hf; + const size_t snd_hash = hf(SSndHash(m_topic_id, m_clock)); + + // increase overall sum send + g_process_wbytes_sum += len_; + + // store size for monitoring + m_topic_size = len_; + + // return the hash for the write action + return snd_hash; + } + + bool CDataWriter::IsInternalSubscribedOnly() + { + const std::string process_id = Process::GetProcessIDAsString(); + bool is_internal_only(true); + const std::lock_guard lock(m_sub_map_sync); + for (auto sub : m_loc_sub_map) + { + if (sub.first.process_id != process_id) + { + is_internal_only = false; + break; + } + } + return is_internal_only; + } + + void CDataWriter::LogSendMode(TLayer::eSendMode smode_, const std::string& base_msg_) + { +#ifndef NDEBUG + switch (smode_) + { + case TLayer::eSendMode::smode_none: + Logging::Log(log_level_debug4, base_msg_ + "NONE"); + break; + case TLayer::eSendMode::smode_auto: + Logging::Log(log_level_debug4, base_msg_ + "AUTO"); + break; + case TLayer::eSendMode::smode_on: + Logging::Log(log_level_debug4, base_msg_ + "ON"); + break; + case TLayer::eSendMode::smode_off: + Logging::Log(log_level_debug4, base_msg_ + "OFF"); + break; + } +#else + (void)smode_; + (void)base_msg_; +#endif + } +} diff --git a/src/core/src/readwrite/ecal_writer.h b/src/core/src/readwrite/ecal_writer.h new file mode 100644 index 0000000..ecb4816 --- /dev/null +++ b/src/core/src/readwrite/ecal_writer.h @@ -0,0 +1,216 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief common eCAL data writer +**/ + +#pragma once + +#include +#include +#include +#include + +#include "ecal_def.h" +#include "util/ecal_expmap.h" + +#if ECAL_CORE_TRANSPORT_UDP +#include "udp/ecal_writer_udp_mc.h" +#endif + +#if ECAL_CORE_TRANSPORT_SHM +#include "shm/ecal_writer_shm.h" +#endif + +#if ECAL_CORE_TRANSPORT_TCP +#include "tcp/ecal_writer_tcp.h" +#endif + +#include +#include +#include +#include +#include + +namespace eCAL +{ + class CDataWriter + { + public: + struct SExternalSubscriptionInfo + { + std::string host_name; + std::string process_id; + std::string topic_id; + + friend bool operator<(const SExternalSubscriptionInfo& l, const SExternalSubscriptionInfo& r) + { + return std::tie(l.host_name, l.process_id, l.topic_id) + < std::tie(r.host_name, r.process_id, r.topic_id); + } + }; + + struct SLocalSubscriptionInfo + { + std::string process_id; + std::string topic_id; + + friend bool operator<(const SLocalSubscriptionInfo& l, const SLocalSubscriptionInfo& r) + { + return std::tie(l.process_id, l.topic_id) + < std::tie(r.process_id, r.topic_id); + } + }; + + CDataWriter(); + ~CDataWriter(); + + bool Create(const std::string& topic_name_, const SDataTypeInformation& topic_info_); + bool Destroy(); + + bool SetDataTypeInformation(const SDataTypeInformation& topic_info_); + + bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); + bool ClearAttribute(const std::string& attr_name_); + + void ShareType(bool state_); + void ShareDescription(bool state_); + + bool SetLayerMode(TLayer::eTransportLayer layer_, TLayer::eSendMode mode_); + + bool ShmSetBufferCount(size_t buffering_); + bool ShmEnableZeroCopy(bool state_); + + bool ShmSetAcknowledgeTimeout(long long acknowledge_timeout_ms_); + long long ShmGetAcknowledgeTimeout() const; + + bool AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_); + bool RemEventCallback(eCAL_Publisher_Event type_); + + size_t Write(CPayloadWriter& payload_, long long time_, long long id_); + + void ApplyLocSubscription(const SLocalSubscriptionInfo& local_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_); + void RemoveLocSubscription(const SLocalSubscriptionInfo& local_info_); + + void ApplyExtSubscription(const SExternalSubscriptionInfo& external_info_, const SDataTypeInformation& tinfo_, const std::string& reader_par_); + void RemoveExtSubscription(const SExternalSubscriptionInfo& external_info_); + + void RefreshRegistration(); + void RefreshSendCounter(); + + std::string Dump(const std::string& indent_ = ""); + + bool IsCreated() const { return(m_created); } + bool IsSubscribed() const { return(m_loc_subscribed || m_ext_subscribed); } + bool IsExtSubscribed() const { return(m_ext_subscribed); } + size_t GetSubscriberCount() const + { + std::lock_guard const lock(m_sub_map_sync); + return(m_loc_sub_map.size() + m_ext_sub_map.size()); + } + + const std::string& GetTopicName() const { return(m_topic_name); } + const SDataTypeInformation& GetDataTypeInformation() const { return m_topic_info; } + + protected: + bool Register(bool force_); + bool Unregister(); + + void Connect(const std::string& tid_, const SDataTypeInformation& tinfo_); + void Disconnect(); + + void SetUseUdpMC(TLayer::eSendMode mode_); + void SetUseShm(TLayer::eSendMode mode_); + void SetUseTcp(TLayer::eSendMode mode_); + + bool CheckWriterModes(); + size_t PrepareWrite(long long id_, size_t len_); + bool IsInternalSubscribedOnly(); + void LogSendMode(TLayer::eSendMode smode_, const std::string& base_msg_); + + 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; + + size_t m_buffering_shm; + bool m_zero_copy; + long long m_acknowledge_timeout_ms; + + std::vector m_payload_buffer; + + std::atomic m_connected; + + using LocalConnectedMapT = Util::CExpMap; + using ExternalConnectedMapT = Util::CExpMap; + mutable std::mutex m_sub_map_sync; + LocalConnectedMapT m_loc_sub_map; + ExternalConnectedMapT m_ext_sub_map; + + using EventCallbackMapT = std::map; + std::mutex m_event_callback_map_sync; + EventCallbackMapT m_event_callback_map; + + long long m_id; + long long m_clock; + long long m_clock_old; + std::chrono::steady_clock::time_point m_snd_time; + long m_freq; + + std::atomic m_loc_subscribed; + std::atomic m_ext_subscribed; + + struct SWriter + { + struct SWriterMode + { + TLayer::eSendMode requested = TLayer::smode_off; + bool activated = false; + bool confirmed = false; + }; + + SWriterMode udp_mc_mode; + SWriterMode tcp_mode; + SWriterMode shm_mode; + +#if ECAL_CORE_TRANSPORT_UDP + CDataWriterUdpMC udp_mc; +#endif +#if ECAL_CORE_TRANSPORT_SHM + CDataWriterSHM shm; +#endif +#if ECAL_CORE_TRANSPORT_TCP + CDataWriterTCP tcp; +#endif + }; + SWriter m_writer; + + bool m_use_ttype; + bool m_use_tdesc; + int m_share_ttype; + int m_share_tdesc; + bool m_created; + }; +} diff --git a/ecal/core/src/readwrite/ecal_writer_base.h b/src/core/src/readwrite/ecal_writer_base.h similarity index 57% rename from ecal/core/src/readwrite/ecal_writer_base.h rename to src/core/src/readwrite/ecal_writer_base.h index 67f4ded..40188b6 100644 --- a/ecal/core/src/readwrite/ecal_writer_base.h +++ b/src/core/src/readwrite/ecal_writer_base.h @@ -23,11 +23,13 @@ #pragma once -#include +#include #include "ecal_writer_data.h" #include "ecal_writer_info.h" +#include "serialization/ecal_struct_sample_registration.h" + #include #include @@ -37,32 +39,29 @@ namespace eCAL { public: CDataWriterBase() : m_created(false) {}; - virtual ~CDataWriterBase() {}; + virtual ~CDataWriterBase() = default; virtual SWriterInfo GetInfo() = 0; virtual bool Create(const std::string& host_name_, const std::string& topic_name_, const std::string & topic_id_) = 0; virtual bool Destroy() = 0; - virtual bool SetQOS(const QOS::SWriterQOS& qos_) { m_qos = qos_; return true; }; - QOS::SWriterQOS GetQOS() { return(m_qos); }; - - virtual bool AddLocConnection(const std::string& /*process_id_*/, const std::string& /*conn_par_*/) { return false; }; - virtual bool RemLocConnection(const std::string& /*process_id_*/) { return false; }; + virtual void AddLocConnection(const std::string& /*process_id_*/, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) {}; + virtual void RemLocConnection(const std::string& /*process_id_*/, const std::string& /*topic_id_*/) {}; - virtual bool AddExtConnection(const std::string& /*host_name_*/, const std::string& /*process_id_*/, const std::string& /*conn_par_*/) { return false; }; - virtual bool RemExtConnection(const std::string& /*host_name_*/, const std::string& /*process_id_*/) { return false; }; + virtual void AddExtConnection(const std::string& /*host_name_*/, const std::string& /*process_id_*/, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) {}; + virtual void RemExtConnection(const std::string& /*host_name_*/, const std::string& /*process_id_*/, const std::string& /*topic_id_*/) {}; - virtual std::string GetConnectionParameter() { return ""; }; + virtual Registration::ConnectionPar GetConnectionParameter() { return {}; }; - virtual bool PrepareWrite(const SWriterData& /*data_*/) { return false; }; - virtual bool Write(const SWriterData& data_) = 0; + 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; - QOS::SWriterQOS m_qos; std::atomic m_created; }; diff --git a/src/core/src/readwrite/ecal_writer_buffer_payload.h b/src/core/src/readwrite/ecal_writer_buffer_payload.h new file mode 100644 index 0000000..5a1c49c --- /dev/null +++ b/src/core/src/readwrite/ecal_writer_buffer_payload.h @@ -0,0 +1,89 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL payload writer class wrapping classic (buffer, size) payload +**/ + +#pragma once + +#include + +#include + +namespace eCAL +{ + /** + * @brief Payload writer class that wraps a classic (void*, size_t) interface. + * + * This class is a payload writer that wraps a classic interface using `void*` and `size_t` + * arguments. It inherits from the base class CPayloadWriter, allowing zero-copy memory + * operations. + */ + class CBufferPayloadWriter : public CPayloadWriter + { + public: + /** + * @brief Constructor for CBufferPayloadWriter. + * + * @param buffer_ Pointer to the buffer containing the data to be written. + * @param size_ Size of the data to be written. + */ + CBufferPayloadWriter(const void* const buffer_, size_t size_) : m_buffer(buffer_), m_size(size_) {}; + + /** + * @brief Make a dump memory copy of the stored buffer. + * + * This function performs a dump memory copy of the stored buffer to the provided + * memory location (buffer_) with the specified size (size_). The size of the provided + * memory buffer should be equal to or greater than the stored buffer size to avoid + * memory corruption. + * + * @param buffer_ Pointer to the target buffer where the data will be copied. + * @param size_ Size of the target buffer. + * + * @return True if the copy operation is successful, false otherwise. + */ + bool WriteFull(void* buffer_, size_t size_) override + { + if (buffer_ == nullptr) return false; + if (size_ < m_size) return false; + if (m_buffer == nullptr) return false; + if (m_size == 0) return false; + memcpy(buffer_, m_buffer, m_size); + return true; + } + + /** + * @brief Get the size of the memory that needs to be copied. + * + * This function returns the size of the memory buffer that needs to be copied during + * the write operation. It is used by the base class CPayloadWriter to allocate the + * required memory for eCAL. + * + * @return The size of the memory that needs to be copied. + */ + size_t GetSize() override { return m_size; }; + + private: + const void* m_buffer = nullptr; ///< Pointer to the buffer containing the data to be written. + size_t m_size = 0; ///< Size of the data to be written. + }; + +} // namespace eCAL diff --git a/ecal/core/src/readwrite/ecal_writer_data.h b/src/core/src/readwrite/ecal_writer_data.h similarity index 91% rename from ecal/core/src/readwrite/ecal_writer_data.h rename to src/core/src/readwrite/ecal_writer_data.h index 16d29f5..cae573f 100644 --- a/ecal/core/src/readwrite/ecal_writer_data.h +++ b/src/core/src/readwrite/ecal_writer_data.h @@ -27,16 +27,14 @@ namespace eCAL { - struct SWriterData + struct SWriterAttr { - const void* buf = nullptr; size_t len = 0; long long id = 0; long long clock = 0; size_t hash = 0; long long time = 0; size_t buffering = 1; - long bandwidth = 0; bool loopback = false; bool zero_copy = false; long long acknowledge_timeout_ms = 0; diff --git a/ecal/core/src/readwrite/ecal_writer_info.h b/src/core/src/readwrite/ecal_writer_info.h similarity index 83% rename from ecal/core/src/readwrite/ecal_writer_info.h rename to src/core/src/readwrite/ecal_writer_info.h index 5e620e2..dc1023a 100644 --- a/ecal/core/src/readwrite/ecal_writer_info.h +++ b/src/core/src/readwrite/ecal_writer_info.h @@ -32,12 +32,9 @@ namespace eCAL std::string name; std::string description; - bool has_mode_local = false; - bool has_mode_cloud = false; + bool has_mode_local = false; + bool has_mode_cloud = false; - bool has_qos_history_kind = false; - bool has_qos_reliability = false; - - int send_size_max = -1; + int send_size_max = -1; }; } diff --git a/src/core/src/readwrite/shm/ecal_reader_shm.cpp b/src/core/src/readwrite/shm/ecal_reader_shm.cpp new file mode 100644 index 0000000..84238fe --- /dev/null +++ b/src/core/src/readwrite/shm/ecal_reader_shm.cpp @@ -0,0 +1,70 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief shared memory layer +**/ + +#include + +#include "ecal_global_accessors.h" +#include "pubsub/ecal_subgate.h" +#include "io/shm/ecal_memfile_pool.h" +#include "ecal_reader_shm.h" + +namespace eCAL +{ + //////////////// + // LAYER + //////////////// + 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 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); + } + } + } + + 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_) + { + if (g_subgate() != nullptr) + { + if (g_subgate()->ApplySample(topic_name_, topic_id_, buf_, len_, id_, clock_, time_, hash_, tl_ecal_shm)) + { + return len_; + } + } + return 0; + } +} diff --git a/ecal/core/src/readwrite/ecal_reader_shm.h b/src/core/src/readwrite/shm/ecal_reader_shm.h similarity index 88% rename from ecal/core/src/readwrite/ecal_reader_shm.h rename to src/core/src/readwrite/shm/ecal_reader_shm.h index 3c907f0..36f4087 100644 --- a/ecal/core/src/readwrite/ecal_reader_shm.h +++ b/src/core/src/readwrite/shm/ecal_reader_shm.h @@ -36,14 +36,14 @@ namespace eCAL class CSHMReaderLayer : public CReaderLayer { public: - CSHMReaderLayer() {} - ~CSHMReaderLayer() {} + CSHMReaderLayer() = default; + ~CSHMReaderLayer() override = default; - void Initialize() {} - void AddSubscription(const std::string& /*host_name_*/, const std::string& /*topic_name_*/, const std::string& /*topic_id_*/, QOS::SReaderQOS /*qos_*/) {} - void RemSubscription(const std::string& /*host_name_*/, const std::string& /*topic_name_*/, const std::string& /*topic_id_*/) {} + void Initialize() 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_); + 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_); diff --git a/src/core/src/readwrite/shm/ecal_writer_shm.cpp b/src/core/src/readwrite/shm/ecal_writer_shm.cpp new file mode 100644 index 0000000..a289fde --- /dev/null +++ b/src/core/src/readwrite/shm/ecal_writer_shm.cpp @@ -0,0 +1,192 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief memory file data writer +**/ + +#include +#include +#include + +#include "ecal_def.h" +#include "ecal_writer_shm.h" + +namespace eCAL +{ + const std::string CDataWriterSHM::m_memfile_base_name = "ecal_"; + + CDataWriterSHM::~CDataWriterSHM() + { + Destroy(); + } + + SWriterInfo CDataWriterSHM::GetInfo() + { + SWriterInfo info_; + + info_.name = "shm"; + info_.description = "Local shared memory data writer"; + + info_.has_mode_local = true; + info_.has_mode_cloud = false; + + info_.send_size_max = -1; + + return info_; + } + + bool CDataWriterSHM::Create(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string & /*topic_id_*/) + { + if (m_created) return true; + m_topic_name = topic_name_; + + // init write index and create memory files + m_write_idx = 0; + + // set attributes + m_memory_file_attr.min_size = Config::GetMemfileMinsizeBytes(); + m_memory_file_attr.reserve = Config::GetMemfileOverprovisioningPercentage(); + m_memory_file_attr.timeout_open_ms = PUB_MEMFILE_OPEN_TO; + m_memory_file_attr.timeout_ack_ms = Config::GetMemfileAckTimeoutMs(); + + // initialize memory file buffer + m_created = SetBufferCount(m_buffer_count); + + return m_created; + } + + bool CDataWriterSHM::Destroy() + { + if (!m_created) return true; + m_created = false; + + m_memory_file_vec.clear(); + + return true; + } + + bool CDataWriterSHM::SetBufferCount(size_t buffer_count_) + { + // no need to adapt anything + if (m_memory_file_vec.size() == buffer_count_) return true; + + // 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 !"); + return false; + } + + // retrieve the memory file size of existing files + size_t memory_file_size(0); + if (!m_memory_file_vec.empty()) + { + memory_file_size = m_memory_file_vec[0]->GetSize(); + } + else + { + memory_file_size = m_memory_file_attr.min_size; + } + + // create memory file vector + m_memory_file_vec.clear(); + while (m_memory_file_vec.size() < buffer_count_) + { + auto sync_memfile = std::make_shared(m_memfile_base_name, memory_file_size, m_memory_file_attr); + if (sync_memfile->IsCreated()) + { + m_memory_file_vec.push_back(sync_memfile); + } + else + { + m_memory_file_vec.clear(); + Logging::Log(log_level_error, "CDataWriterSHM::SetBufferCount - FAILED"); + return false; + } + } + + return true; + } + + bool CDataWriterSHM::PrepareWrite(const SWriterAttr& attr_) + { + if (!m_created) return false; + + // false signals no rematching / exchanging of + // connection parameters needed + bool ret_state(false); + + // adapt number of used memory files if needed + if (attr_.buffering != m_buffer_count) + { + SetBufferCount(attr_.buffering); + + // store new buffer count and flag change + m_buffer_count = attr_.buffering; + ret_state |= true; + } + + // adapt write index if needed + m_write_idx %= m_memory_file_vec.size(); + + // check size and reserve new if needed + ret_state |= m_memory_file_vec[m_write_idx]->CheckSize(attr_.len); + + return ret_state; + } + + bool CDataWriterSHM::Write(CPayloadWriter& payload_, const SWriterAttr& attr_) + { + if (!m_created) return false; + + // write content + const bool force_full_write(m_memory_file_vec.size() > 1); + const bool sent = m_memory_file_vec[m_write_idx]->Write(payload_, attr_, force_full_write); + + // and increment file index + m_write_idx++; + m_write_idx %= m_memory_file_vec.size(); + + return sent; + } + + void CDataWriterSHM::AddLocConnection(const std::string& process_id_, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) + { + if (!m_created) return; + + for (auto& memory_file : m_memory_file_vec) + { + memory_file->Connect(process_id_); +#ifndef NDEBUG + Logging::Log(log_level_debug1, std::string("CDataWriterSHM::AddLocConnection - Memory FileName: ") + memory_file->GetName() + " to ProcessId " + process_id_); +#endif + } + } + + Registration::ConnectionPar CDataWriterSHM::GetConnectionParameter() + { + Registration::ConnectionPar connection_par; + for (auto& memory_file : m_memory_file_vec) + { + connection_par.layer_par_shm.memory_file_list.push_back(memory_file->GetName()); + } + return connection_par; + } +} diff --git a/ecal/core/src/readwrite/ecal_writer_shm.h b/src/core/src/readwrite/shm/ecal_writer_shm.h similarity index 75% rename from ecal/core/src/readwrite/ecal_writer_shm.h rename to src/core/src/readwrite/shm/ecal_writer_shm.h index 9a47af4..deb4d70 100644 --- a/ecal/core/src/readwrite/ecal_writer_shm.h +++ b/src/core/src/readwrite/shm/ecal_writer_shm.h @@ -24,7 +24,7 @@ #pragma once #include "readwrite/ecal_writer_base.h" -#include "io/ecal_memfile_sync.h" +#include "io/shm/ecal_memfile_sync.h" #include #include @@ -34,30 +34,30 @@ namespace eCAL class CDataWriterSHM : public CDataWriterBase { public: - CDataWriterSHM(); - ~CDataWriterSHM(); + CDataWriterSHM() = default; + ~CDataWriterSHM() override; SWriterInfo GetInfo() override; bool Create(const std::string& host_name_, const std::string& topic_name_, const std::string & topic_id_) override; // this virtual function is called during construction/destruction, // so, mark it as final to ensure that no derived classes override it. - bool Destroy() final override; + bool Destroy() final; - bool SetQOS(const QOS::SWriterQOS& qos_) override; + bool SetBufferCount(size_t buffer_count_); - bool PrepareWrite(const SWriterData& data_) override; - bool Write(const SWriterData& data_) override; + bool PrepareWrite(const SWriterAttr& attr_) override; - bool AddLocConnection(const std::string& process_id_, const std::string& conn_par_) override; - bool RemLocConnection(const std::string& process_id_) override; + bool Write(CPayloadWriter& payload_, const SWriterAttr& attr_) override; - std::string GetConnectionParameter() override; + void AddLocConnection(const std::string& process_id_, const std::string& topic_id_, const std::string& conn_par_) override; - protected: + Registration::ConnectionPar GetConnectionParameter() override; + + protected: size_t m_write_idx = 0; size_t m_buffer_count = 1; - SSyncMemoryFileAttr m_memory_file_attr; + SSyncMemoryFileAttr m_memory_file_attr = {}; std::vector> m_memory_file_vec; static const std::string m_memfile_base_name; }; diff --git a/ecal/core/src/readwrite/ecal_reader_tcp.cpp b/src/core/src/readwrite/tcp/ecal_reader_tcp.cpp similarity index 61% rename from ecal/core/src/readwrite/ecal_reader_tcp.cpp rename to src/core/src/readwrite/tcp/ecal_reader_tcp.cpp index 17d8469..34a1577 100644 --- a/ecal/core/src/readwrite/ecal_reader_tcp.cpp +++ b/src/core/src/readwrite/tcp/ecal_reader_tcp.cpp @@ -21,8 +21,6 @@ * @brief tcp reader and layer **/ -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" #include "ecal_global_accessors.h" #include @@ -30,8 +28,8 @@ #include "pubsub/ecal_subgate.h" #include "readwrite/ecal_writer_base.h" -#include "readwrite/ecal_reader_tcp.h" -#include "readwrite/ecal_tcp_pubsub_logger.h" +#include "ecal_reader_tcp.h" +#include "ecal_tcp_pubsub_logger.h" #include "ecal_utils/portable_endian.h" @@ -65,7 +63,7 @@ namespace eCAL // check for new session bool new_session(true); auto sessions = m_subscriber->getSessions(); - for (auto session : sessions) + for (const auto& session : sessions) { auto address = session->getAddress(); auto port = session->getPort(); @@ -94,9 +92,9 @@ namespace eCAL { // 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); - uint16_t header_size = le16toh(*reinterpret_cast(data_.buffer_->data() + ecal_magic)); + // 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)); // extract header const char* header_payload = data_.buffer_->data() + header_length; @@ -104,49 +102,47 @@ namespace eCAL const char* data_payload = header_payload + header_size; // parse header - if (m_ecal_header.ParseFromArray(header_payload, static_cast(header_size))) + if (DeserializeFromBuffer(header_payload, header_size, m_ecal_header)) { - if (g_subgate()) + if (g_subgate() != nullptr) { // use this intermediate variables as optimization - auto& ecal_header_topic = m_ecal_header.topic(); - auto& ecal_header_content = m_ecal_header.content(); + const auto& ecal_header_topic = m_ecal_header.topic; + const auto& ecal_header_content = m_ecal_header.content; // apply sample g_subgate()->ApplySample( - ecal_header_topic.tname(), - ecal_header_topic.tid(), + ecal_header_topic.tname, + ecal_header_topic.tid, data_payload, - static_cast(ecal_header_content.size()), - ecal_header_content.id(), - ecal_header_content.clock(), - ecal_header_content.time(), - ecal_header_content.hash(), - eCAL::pb::tl_ecal_tcp); + static_cast(ecal_header_content.size), + ecal_header_content.id, + ecal_header_content.clock, + ecal_header_content.time, + ecal_header_content.hash, + tl_ecal_tcp); } } - }; + } //////////////// // LAYER //////////////// - CTCPReaderLayer::CTCPReaderLayer() - { - } + CTCPReaderLayer::CTCPReaderLayer() = default; void CTCPReaderLayer::Initialize() { - tcp_pubsub::logger::logger_t tcp_pubsub_logger = std::bind(TcpPubsubLogger, std::placeholders::_1, std::placeholders::_2); + 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); } - void CTCPReaderLayer::AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/, QOS::SReaderQOS /*qos_*/) + void CTCPReaderLayer::AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) { - std::string map_key(topic_name_); + const std::string& map_key(topic_name_); - std::lock_guard lock(m_datareadertcp_sync); + const std::lock_guard lock(m_datareadertcp_sync); if (m_datareadertcp_map.find(map_key) != m_datareadertcp_map.end()) return; - std::shared_ptr reader = std::make_shared(); + const std::shared_ptr reader = std::make_shared(); reader->Create(m_executor); m_datareadertcp_map.insert(std::pair>(map_key, reader)); @@ -154,10 +150,10 @@ namespace eCAL void CTCPReaderLayer::RemSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) { - std::string map_key(topic_name_); + const std::string& map_key(topic_name_); - std::lock_guard lock(m_datareadertcp_sync); - DataReaderTCPMapT::iterator iter = m_datareadertcp_map.find(map_key); + const std::lock_guard lock(m_datareadertcp_sync); + const DataReaderTCPMapT::iterator iter = m_datareadertcp_map.find(map_key); if (iter == m_datareadertcp_map.end()) return; auto reader = iter->second; @@ -168,29 +164,19 @@ namespace eCAL void CTCPReaderLayer::SetConnectionParameter(SReaderLayerPar& par_) { - eCAL::pb::ConnnectionPar connection_par; - if (connection_par.ParseFromString(par_.parameter)) - { - ////////////////////////////////// - // get parameter from a new writer - ////////////////////////////////// - - const auto& remote_hostname = par_.host_name; - auto remote_port = connection_par.layer_par_tcp().port(); + ////////////////////////////////// + // get parameter from a new writer + ////////////////////////////////// + const auto& remote_hostname = par_.host_name; + auto remote_port = par_.parameter.layer_par_tcp.port; - std::string map_key(par_.topic_name); + const std::string map_key(par_.topic_name); - std::lock_guard lock(m_datareadertcp_sync); - DataReaderTCPMapT::iterator iter = m_datareadertcp_map.find(map_key); - if (iter == m_datareadertcp_map.end()) return; + const std::lock_guard lock(m_datareadertcp_sync); + const DataReaderTCPMapT::iterator iter = m_datareadertcp_map.find(map_key); + if (iter == m_datareadertcp_map.end()) return; - auto& reader = iter->second; - reader->AddConnectionIfNecessary(remote_hostname, static_cast(remote_port)); - } - else - { - std::cout << "FATAL ERROR: Could not parse layer connection parameter ! Did you mix up different eCAL versions on the same host ?" << std::endl; - return; - } + auto& reader = iter->second; + reader->AddConnectionIfNecessary(remote_hostname, static_cast(remote_port)); } } diff --git a/ecal/core/src/readwrite/ecal_reader_tcp.h b/src/core/src/readwrite/tcp/ecal_reader_tcp.h similarity index 78% rename from ecal/core/src/readwrite/ecal_reader_tcp.h rename to src/core/src/readwrite/tcp/ecal_reader_tcp.h index 28932ff..cc1a74c 100644 --- a/ecal/core/src/readwrite/ecal_reader_tcp.h +++ b/src/core/src/readwrite/tcp/ecal_reader_tcp.h @@ -28,13 +28,7 @@ #include #include -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif +#include "serialization/ecal_struct_sample_payload.h" namespace eCAL { @@ -53,9 +47,11 @@ namespace eCAL private: void OnTcpMessage(const tcp_pubsub::CallbackData& callback_data); + + Payload::Sample m_ecal_header; + std::shared_ptr m_subscriber; bool m_callback_active; - eCAL::pb::Sample m_ecal_header; }; //////////////// @@ -66,17 +62,17 @@ namespace eCAL public: CTCPReaderLayer(); - void Initialize(); + void Initialize() override; - void AddSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, QOS::SReaderQOS qos_); - void RemSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_); + 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_*/); + void SetConnectionParameter(SReaderLayerPar& /*par_*/) override; private: std::shared_ptr m_executor; - typedef std::unordered_map> DataReaderTCPMapT; + using DataReaderTCPMapT = std::unordered_map>; std::mutex m_datareadertcp_sync; DataReaderTCPMapT m_datareadertcp_map; }; diff --git a/ecal/core/src/readwrite/ecal_tcp_pubsub_logger.h b/src/core/src/readwrite/tcp/ecal_tcp_pubsub_logger.h similarity index 100% rename from ecal/core/src/readwrite/ecal_tcp_pubsub_logger.h rename to src/core/src/readwrite/tcp/ecal_tcp_pubsub_logger.h diff --git a/ecal/core/src/readwrite/ecal_writer_tcp.cpp b/src/core/src/readwrite/tcp/ecal_writer_tcp.cpp similarity index 50% rename from ecal/core/src/readwrite/ecal_writer_tcp.cpp rename to src/core/src/readwrite/tcp/ecal_writer_tcp.cpp index 40f499e..9fda547 100644 --- a/ecal/core/src/readwrite/ecal_writer_tcp.cpp +++ b/src/core/src/readwrite/tcp/ecal_writer_tcp.cpp @@ -21,27 +21,21 @@ * @brief tcp writer **/ -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "ecal_def.h" -#include "ecal_config_reader_hlp.h" +#include "config/ecal_config_reader_hlp.h" +#include "serialization/ecal_serialize_sample_payload.h" #include -#include "readwrite/ecal_writer_tcp.h" -#include "readwrite/ecal_tcp_pubsub_logger.h" +#include "ecal_writer_tcp.h" +#include "ecal_tcp_pubsub_logger.h" #include "ecal_utils/portable_endian.h" +#include + namespace eCAL { - std::mutex CDataWriterTCP::g_tcp_writer_executor_mtx; + std::mutex CDataWriterTCP::g_tcp_writer_executor_mtx; std::shared_ptr CDataWriterTCP::g_tcp_writer_executor; CDataWriterTCP::CDataWriterTCP() : m_port(0) @@ -63,9 +57,6 @@ namespace eCAL info_.has_mode_local = true; info_.has_mode_cloud = true; - info_.has_qos_history_kind = false; - info_.has_qos_reliability = false; - info_.send_size_max = -1; return info_; @@ -74,7 +65,7 @@ namespace eCAL bool CDataWriterTCP::Create(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) { { - std::lock_guard lock(g_tcp_writer_executor_mtx); + 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); @@ -95,7 +86,7 @@ namespace eCAL bool CDataWriterTCP::Destroy() { - if(!m_publisher) return false; + if(!m_publisher) return true; // destroy publisher m_publisher = nullptr; @@ -104,39 +95,39 @@ namespace eCAL return true; } - bool CDataWriterTCP::Write(const SWriterData& data_) + bool CDataWriterTCP::Write(const void* const buf_, const SWriterAttr& attr_) { if (!m_publisher) return false; - // create new sample (header information only, no payload) - m_ecal_header.Clear(); - auto ecal_sample_mutable_topic = m_ecal_header.mutable_topic(); - ecal_sample_mutable_topic->set_tname(m_topic_name); - ecal_sample_mutable_topic->set_tid(m_topic_id); - - // append payload header (without payload) - auto ecal_sample_mutable_content = m_ecal_header.mutable_content(); - ecal_sample_mutable_content->set_id(data_.id); - ecal_sample_mutable_content->set_clock(data_.clock); - ecal_sample_mutable_content->set_time(data_.time); - ecal_sample_mutable_content->set_hash(data_.hash); - ecal_sample_mutable_content->set_size((google::protobuf::int32)data_.len); - - uint16_t header_size(0); -#if GOOGLE_PROTOBUF_VERSION >= 3001000 - header_size = (uint16_t)m_ecal_header.ByteSizeLong(); -#else - header_size = (uint16_t)m_ecal_header.ByteSize(); -#endif - - // create header - const size_t ecal_magic(4 * sizeof(char)); - // ECAL + header size field + proto header - m_header_buffer.resize(ecal_magic + sizeof(uint16_t) + header_size); - // add size - *reinterpret_cast(&m_header_buffer[ecal_magic]) = htole16(header_size); - // serialize header message right after size field - m_ecal_header.SerializeToArray((void*)(m_header_buffer.data() + ecal_magic + sizeof(uint16_t)), (int)header_size); + // 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; + + // set payload content (without payload) + auto& proto_header_content = proto_header.content; + proto_header_content.id = attr_.id; + proto_header_content.clock = attr_.clock; + proto_header_content.time = attr_.time; + proto_header_content.hash = attr_.hash; + proto_header_content.size = static_cast(attr_.len); // we use this size attribute for "header only" + + // Compute size of "ECAL" pre-header + constexpr size_t ecal_magic_size(4 * sizeof(char)); + + // Serialize payload sample + std::vector serialized_proto_header; + SerializeToBuffer(proto_header, serialized_proto_header); + + // Get size of ecal payload sample + uint16_t proto_header_size = static_cast(serialized_proto_header.size()); + + // Compute needed padding for aligning the payload + //const size_t minimal_header_size = ecal_magic_size + sizeof(uint16_t) + ecal_sample_size; + + // 'ECAL' + proto header size field + proto header + m_header_buffer.resize(ecal_magic_size + sizeof(uint16_t) + proto_header_size); // add magic ecal header :-) m_header_buffer[0] = 'E'; @@ -144,27 +135,32 @@ namespace eCAL m_header_buffer[2] = 'A'; m_header_buffer[3] = 'L'; + // set proto header size right after magic ecal header + *reinterpret_cast(&m_header_buffer[ecal_magic_size]) = htole16(proto_header_size); + + // copy serialized proto header right after sample size field + memcpy((void*)(m_header_buffer.data() + ecal_magic_size + sizeof(uint16_t)), serialized_proto_header.data(), serialized_proto_header.size()); + // create tcp send buffer std::vector> send_vec; send_vec.reserve(2); // push header data - send_vec.emplace_back(std::pair(m_header_buffer.data(), m_header_buffer.size())); + send_vec.emplace_back(m_header_buffer.data(), m_header_buffer.size()); // push payload data - send_vec.emplace_back(std::pair(static_cast(data_.buf), data_.len)); + send_vec.emplace_back(static_cast(buf_), attr_.len); // send it - bool success = m_publisher->send(send_vec); + const bool success = m_publisher->send(send_vec); // return success return success; } - std::string CDataWriterTCP::GetConnectionParameter() + Registration::ConnectionPar CDataWriterTCP::GetConnectionParameter() { - // set tcp port - eCAL::pb::ConnnectionPar connection_par; - connection_par.mutable_layer_par_tcp()->set_port(m_port); - return connection_par.SerializeAsString(); + Registration::ConnectionPar connection_par; + connection_par.layer_par_tcp.port = m_port; + return connection_par; } } diff --git a/ecal/core/src/readwrite/ecal_writer_tcp.h b/src/core/src/readwrite/tcp/ecal_writer_tcp.h similarity index 73% rename from ecal/core/src/readwrite/ecal_writer_tcp.h rename to src/core/src/readwrite/tcp/ecal_writer_tcp.h index c09fd95..ee90ff6 100644 --- a/ecal/core/src/readwrite/ecal_writer_tcp.h +++ b/src/core/src/readwrite/tcp/ecal_writer_tcp.h @@ -32,14 +32,6 @@ #include #include -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - namespace eCAL { // ecal tcp writer @@ -47,27 +39,26 @@ namespace eCAL { public: CDataWriterTCP(); - ~CDataWriterTCP(); + ~CDataWriterTCP() override; SWriterInfo GetInfo() override; bool Create(const std::string& host_name_, const std::string& topic_name_, const std::string & topic_id_) override; // this virtual function is called during construction/destruction, // so, mark it as final to ensure that no derived classes override it. - bool Destroy() final override; + bool Destroy() final; - bool Write(const SWriterData& data_) override; + bool Write(const void* buf_, const SWriterAttr& attr_) override; - std::string GetConnectionParameter() override; + Registration::ConnectionPar GetConnectionParameter() override; private: - static std::mutex g_tcp_writer_executor_mtx; + std::vector m_header_buffer; + + static std::mutex g_tcp_writer_executor_mtx; static std::shared_ptr g_tcp_writer_executor; std::shared_ptr m_publisher; - uint16_t m_port; - - eCAL::pb::Sample m_ecal_header; - std::vector m_header_buffer; + uint16_t m_port; }; } diff --git a/src/core/src/readwrite/udp/ecal_reader_udp_mc.cpp b/src/core/src/readwrite/udp/ecal_reader_udp_mc.cpp new file mode 100644 index 0000000..0261754 --- /dev/null +++ b/src/core/src/readwrite/udp/ecal_reader_udp_mc.cpp @@ -0,0 +1,112 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief udp multicast reader and layer +**/ + +#include "ecal_reader_udp_mc.h" + +#include "ecal_global_accessors.h" +#include "pubsub/ecal_subgate.h" + +#include "io/udp/ecal_udp_configurations.h" + +namespace eCAL +{ + //////////////// + // LAYER + //////////////// + CUDPReaderLayer::CUDPReaderLayer() : + m_started(false), + m_local_mode(false) + {} + + CUDPReaderLayer::~CUDPReaderLayer() = default; + + void CUDPReaderLayer::Initialize() + { + } + + 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 + IO::UDP::SReceiverAttr attr; + attr.address = UDP::GetPayloadAddress(); + attr.port = UDP::GetPayloadPort(); + attr.broadcast = UDP::IsBroadcast(); + attr.loopback = true; + attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); + + // 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_started = true; + } + + // we use udp broadcast in local mode + if (m_local_mode) return; + + // add topic name based multicast address + const std::string mcast_address = UDP::GetTopicPayloadAddress(topic_name_); + if (m_topic_name_mcast_map.find(mcast_address) == m_topic_name_mcast_map.end()) + { + m_topic_name_mcast_map.emplace(std::pair(mcast_address, 0)); + m_payload_receiver->AddMultiCastGroup(mcast_address.c_str()); + } + m_topic_name_mcast_map[mcast_address]++; + } + + 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; + + const std::string mcast_address = UDP::GetTopicPayloadAddress(topic_name_); + if (m_topic_name_mcast_map.find(mcast_address) == m_topic_name_mcast_map.end()) + { + // this should never happen + } + else + { + m_topic_name_mcast_map[mcast_address]--; + if (m_topic_name_mcast_map[mcast_address] == 0) + { + m_payload_receiver->RemMultiCastGroup(mcast_address.c_str()); + m_topic_name_mcast_map.erase(mcast_address); + } + } + } + bool CUDPReaderLayer::HasSample(const std::string& sample_name_) + { + if (g_subgate() == nullptr) return(false); + return(g_subgate()->HasSample(sample_name_)); + } + + bool CUDPReaderLayer::ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_) + { + if (g_subgate() == nullptr) return false; + return g_subgate()->ApplySample(serialized_sample_data_, serialized_sample_size_, tl_ecal_udp_mc); + } +} diff --git a/ecal/core/src/readwrite/ecal_reader_udp_mc.h b/src/core/src/readwrite/udp/ecal_reader_udp_mc.h similarity index 64% rename from ecal/core/src/readwrite/ecal_reader_udp_mc.h rename to src/core/src/readwrite/udp/ecal_reader_udp_mc.h index 8c5d2fe..cbfb16e 100644 --- a/ecal/core/src/readwrite/ecal_reader_udp_mc.h +++ b/src/core/src/readwrite/udp/ecal_reader_udp_mc.h @@ -23,26 +23,15 @@ #pragma once +#include "io/udp/ecal_udp_sample_receiver.h" #include "readwrite/ecal_reader_layer.h" -#include "io/rcv_sample.h" - #include #include #include namespace eCAL { - //////////////// - // READER - //////////////// - class CDataReaderUDP : public CSampleReceiver - { - public: - bool HasSample(const std::string& sample_name_); - size_t ApplySample(const eCAL::pb::Sample& ecal_sample_, eCAL::pb::eTLayerType layer_); - }; - //////////////// // LAYER //////////////// @@ -50,20 +39,22 @@ namespace eCAL { public: CUDPReaderLayer(); - ~CUDPReaderLayer(); + ~CUDPReaderLayer() override; - void Initialize(); + void Initialize() override; - void AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/, QOS::SReaderQOS /*qos_*/); - void RemSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/); + 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_*/) {} + void SetConnectionParameter(SReaderLayerPar& /*par_*/) override {} private: - bool started; - CUDPReceiver rcv; - CThread thread; - CDataReaderUDP reader; - std::map topic_name_mcast_map; + bool HasSample(const std::string& sample_name_); + 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; }; -}; +} diff --git a/src/core/src/readwrite/udp/ecal_writer_udp_mc.cpp b/src/core/src/readwrite/udp/ecal_writer_udp_mc.cpp new file mode 100644 index 0000000..6882cfc --- /dev/null +++ b/src/core/src/readwrite/udp/ecal_writer_udp_mc.cpp @@ -0,0 +1,143 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief udp data writer +**/ + +#include +#include + +#include "ecal_writer_udp_mc.h" +#include "io/udp/ecal_udp_configurations.h" +#include "serialization/ecal_serialize_sample_payload.h" + +namespace eCAL +{ + CDataWriterUdpMC::~CDataWriterUdpMC() + { + Destroy(); + } + + SWriterInfo CDataWriterUdpMC::GetInfo() + { + SWriterInfo info_; + + info_.name = "udp"; + info_.description = "udp multicast data writer"; + + info_.has_mode_local = true; + info_.has_mode_cloud = true; + + info_.send_size_max = -1; + + return info_; + } + + bool CDataWriterUdpMC::Create(const std::string & host_name_, const std::string & topic_name_, const std::string & topic_id_) + { + if (m_created) return true; + + m_host_name = host_name_; + m_topic_name = topic_name_; + m_topic_id = topic_id_; + + // set network attributes + IO::UDP::SSenderAttr attr; + attr.address = UDP::GetTopicPayloadAddress(topic_name_); + attr.port = UDP::GetPayloadPort(); + attr.ttl = UDP::GetMulticastTtl(); + attr.broadcast = UDP::IsBroadcast(); + attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); + + // create udp/sample sender with activated loop-back + attr.loopback = true; + m_sample_sender_loopback = std::make_shared(attr); + + // create udp/sample sender without activated loop-back + attr.loopback = false; + m_sample_sender_no_loopback = std::make_shared(attr); + + m_created = true; + return true; + } + + bool CDataWriterUdpMC::Destroy() + { + if (!m_created) return true; + + m_sample_sender_loopback.reset(); + m_sample_sender_no_loopback.reset(); + + m_created = false; + return true; + } + + bool CDataWriterUdpMC::Write(const void* const buf_, const SWriterAttr& attr_) + { + if (!m_created) return false; + + // create new sample + 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; + + // append content + auto& ecal_sample_content = ecal_sample.content; + ecal_sample_content.id = attr_.id; + ecal_sample_content.clock = attr_.clock; + ecal_sample_content.time = attr_.time; + ecal_sample_content.hash = attr_.hash; + ecal_sample_content.payload.type = Payload::pl_raw; + ecal_sample_content.payload.raw_addr = static_cast(buf_); + ecal_sample_content.payload.raw_size = attr_.len; + + // send it + size_t sent = 0; + if (SerializeToBuffer(ecal_sample, m_sample_buffer)) + { + if (attr_.loopback) + { + if (m_sample_sender_loopback) + { + sent = m_sample_sender_loopback->Send(ecal_sample.topic.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); + } + } + } + + // log it + if (sent == 0) + { + Logging::Log(log_level_fatal, "CDataWriterUDP::Send failed to send message !"); + } + + return(sent > 0); + } +} diff --git a/ecal/core/src/readwrite/ecal_writer_inproc.h b/src/core/src/readwrite/udp/ecal_writer_udp_mc.h similarity index 73% rename from ecal/core/src/readwrite/ecal_writer_inproc.h rename to src/core/src/readwrite/udp/ecal_writer_udp_mc.h index d8713be..d1bc40b 100644 --- a/ecal/core/src/readwrite/ecal_writer_inproc.h +++ b/src/core/src/readwrite/udp/ecal_writer_udp_mc.h @@ -18,22 +18,12 @@ */ /** - * @brief inproc data writer + * @brief udp data writer **/ #pragma once -#include "ecal_def.h" - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -//#include "io/ecal_inproc.h" +#include "io/udp/ecal_udp_sample_sender.h" #include "readwrite/ecal_writer_base.h" #include @@ -41,20 +31,23 @@ namespace eCAL { - class CDataWriterInProc : public CDataWriterBase + class CDataWriterUdpMC : public CDataWriterBase { public: - ~CDataWriterInProc(); + ~CDataWriterUdpMC() override; SWriterInfo GetInfo() override; bool Create(const std::string& host_name_, const std::string& topic_name_, const std::string & topic_id_) override; // this virtual function is called during construction/destruction, // so, mark it as final to ensure that no derived classes override it. - bool Destroy() final override; + bool Destroy() final; - bool Write(const SWriterData& data_) override; + bool Write(const void* buf_, const SWriterAttr& attr_) override; protected: + std::vector m_sample_buffer; + std::shared_ptr m_sample_sender_loopback; + std::shared_ptr m_sample_sender_no_loopback; }; } diff --git a/src/core/src/registration/ecal_registration_provider.cpp b/src/core/src/registration/ecal_registration_provider.cpp new file mode 100644 index 0000000..f3d2e59 --- /dev/null +++ b/src/core/src/registration/ecal_registration_provider.cpp @@ -0,0 +1,569 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL registration provider + * + * All process internal publisher/subscriber, server/clients register here with all their attributes. + * + * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. + * +**/ + +#include + +#include "ecal_def.h" +#include "ecal_globals.h" +#include "ecal_registration_provider.h" + +#include "io/udp/ecal_udp_configurations.h" +#include "io/udp/ecal_udp_sample_sender.h" + +#include + +namespace +{ + bool ApplyTopicToDescGate(const std::string& topic_name_, const eCAL::SDataTypeInformation& topic_info_, bool topic_is_a_publisher_) + { + if (eCAL::g_descgate() != nullptr) + { + // calculate the quality of the current info + eCAL::CDescGate::QualityFlags quality = eCAL::CDescGate::QualityFlags::NO_QUALITY; + if (!topic_info_.encoding.empty() || !topic_info_.name.empty()) + quality |= eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; + if (!topic_info_.descriptor.empty()) + quality |= eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; + if (topic_is_a_publisher_) + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PRODUCER; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_ENTITY; + // update description + return eCAL::g_descgate()->ApplyTopicDescription(topic_name_, topic_info_, quality); + } + return false; + } + + bool ApplyServiceToDescGate(const std::string& service_name_ ,const std::string& method_name_, const eCAL::SDataTypeInformation& request_type_information_, const eCAL::SDataTypeInformation& response_type_information_) + { + if (eCAL::g_descgate() != nullptr) + { + // Calculate the quality of the current info + eCAL::CDescGate::QualityFlags quality = eCAL::CDescGate::QualityFlags::NO_QUALITY; + if (!(request_type_information_.name.empty() && response_type_information_.name.empty())) + quality |= eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; + if (!(request_type_information_.descriptor.empty() && response_type_information_.descriptor.empty())) + quality |= eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; + quality |= eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_THIS_PROCESS; + + return eCAL::g_descgate()->ApplyServiceDescription(service_name_, method_name_, request_type_information_, response_type_information_, quality); + } + return false; + } +} + +namespace eCAL +{ + std::atomic CRegistrationProvider::m_created; + + CRegistrationProvider::CRegistrationProvider() : + m_reg_refresh(CMN_REGISTRATION_REFRESH), + m_reg_topics(false), + m_reg_services(false), + m_reg_process(false), + m_use_registration_udp(false), + m_use_registration_shm(false) + { + } + + CRegistrationProvider::~CRegistrationProvider() + { + Destroy(); + } + + void CRegistrationProvider::Create(bool topics_, bool services_, bool process_) + { + if(m_created) return; + + m_reg_refresh = Config::GetRegistrationRefreshMs(); + + m_reg_topics = topics_; + m_reg_services = services_; + m_reg_process = process_; + + // send registration to shared memory and to udp + m_use_registration_udp = !Config::Experimental::IsNetworkMonitoringDisabled(); + m_use_registration_shm = Config::Experimental::IsShmMonitoringEnabled(); + + if (m_use_registration_udp) + { + // set network attributes + IO::UDP::SSenderAttr attr; + attr.address = UDP::GetRegistrationAddress(); + attr.port = UDP::GetRegistrationPort(); + attr.ttl = UDP::GetMulticastTtl(); + attr.broadcast = UDP::IsBroadcast(); + attr.loopback = true; + attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); + + // create udp registration sender + m_reg_sample_snd = std::make_shared(attr); + } + +#if ECAL_CORE_REGISTRATION_SHM + if (m_use_registration_shm) + { + std::cout << "Shared memory monitoring is enabled (domain: " << Config::Experimental::GetShmMonitoringDomain() << " - queue size: " << Config::Experimental::GetShmMonitoringQueueSize() << ")" << std::endl; + m_memfile_broadcast.Create(Config::Experimental::GetShmMonitoringDomain(), Config::Experimental::GetShmMonitoringQueueSize()); + m_memfile_broadcast_writer.Bind(&m_memfile_broadcast); + } +#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_created = true; + } + + void CRegistrationProvider::Destroy() + { + if(!m_created) return; + + // stop cyclic registration thread + m_reg_sample_snd_thread->stop(); + + // send one last (un)registration message to the world + // thank you and goodbye :-) + UnregisterProcess(); + + // destroy registration sample sender + m_reg_sample_snd.reset(); + +#if ECAL_CORE_REGISTRATION_SHM + if (m_use_registration_shm) + { + m_memfile_broadcast_writer.Unbind(); + m_memfile_broadcast.Destroy(); + } +#endif + + m_created = false; + } + + bool CRegistrationProvider::RegisterTopic(const std::string& topic_name_, const std::string& topic_id_, const Registration::Sample& ecal_sample_, const bool force_) + { + if (!m_created) return(false); + if (!m_reg_topics) return(false); + + const std::lock_guard lock(m_topics_map_sync); + m_topics_map[topic_name_ + topic_id_] = ecal_sample_; + if(force_) + { + RegisterProcess(); + // apply registration sample + ApplySample(topic_name_, ecal_sample_); + // apply registration sample to shm registration + SendSampleList(false); + } + + return(true); + } + + bool CRegistrationProvider::UnregisterTopic(const std::string& topic_name_, const std::string& topic_id_, const Registration::Sample& ecal_sample_, const bool force_) + { + if(!m_created) return(false); + + if (force_) + { + // apply unregistration sample + ApplySample(topic_name_, ecal_sample_); + // apply registration sample to shm registration + SendSampleList(false); + } + + SampleMapT::iterator iter; + const std::lock_guard lock(m_topics_map_sync); + iter = m_topics_map.find(topic_name_ + topic_id_); + if(iter != m_topics_map.end()) + { + m_topics_map.erase(iter); + return(true); + } + + return(false); + } + + bool CRegistrationProvider::RegisterServer(const std::string& service_name_, const std::string& service_id_, const Registration::Sample& ecal_sample_, bool force_) + { + if (!m_created) return(false); + if (!m_reg_services) return(false); + + const std::lock_guard lock(m_server_map_sync); + m_server_map[service_name_ + service_id_] = ecal_sample_; + if (force_) + { + RegisterProcess(); + // apply registration sample + ApplySample(service_name_, ecal_sample_); + // apply registration sample to shm registration + SendSampleList(false); + } + + return(true); + } + + bool CRegistrationProvider::UnregisterServer(const std::string& service_name_, const std::string& service_id_, const Registration::Sample& ecal_sample_, bool force_) + { + if (!m_created) return(false); + + if (force_) + { + // apply unregistration sample + ApplySample(service_name_, ecal_sample_); + // apply registration sample to shm registration + SendSampleList(false); + } + + SampleMapT::iterator iter; + const std::lock_guard lock(m_server_map_sync); + iter = m_server_map.find(service_name_ + service_id_); + if (iter != m_server_map.end()) + { + m_server_map.erase(iter); + return(true); + } + + return(false); + } + + bool CRegistrationProvider::RegisterClient(const std::string& client_name_, const std::string& client_id_, const Registration::Sample& ecal_sample_, bool force_) + { + if (!m_created) return(false); + if (!m_reg_services) return(false); + + const std::lock_guard lock(m_client_map_sync); + m_client_map[client_name_ + client_id_] = ecal_sample_; + if (force_) + { + RegisterProcess(); + // apply registration sample + ApplySample(client_name_, ecal_sample_); + // apply registration sample to shm registration + SendSampleList(false); + } + + return(true); + } + + bool CRegistrationProvider::UnregisterClient(const std::string& client_name_, const std::string& client_id_, const Registration::Sample& ecal_sample_, bool force_) + { + if (!m_created) return(false); + + if (force_) + { + // apply unregistration sample + ApplySample(client_name_, ecal_sample_); + // apply registration sample to shm registration + SendSampleList(false); + } + + SampleMapT::iterator iter; + const std::lock_guard lock(m_client_map_sync); + iter = m_client_map.find(client_name_ + client_id_); + if (iter != m_client_map.end()) + { + m_client_map.erase(iter); + return(true); + } + + return(false); + } + + bool CRegistrationProvider::RegisterProcess() + { + if (!m_created) return(false); + if (!m_reg_process) return(false); + + Registration::Sample process_sample; + process_sample.cmd_type = bct_reg_process; + auto& process_sample_process = process_sample.process; + process_sample_process.hname = Process::GetHostName(); + process_sample_process.hgname = Process::GetHostGroupName(); + process_sample_process.pid = Process::GetProcessID(); + process_sample_process.pname = Process::GetProcessName(); + process_sample_process.uname = Process::GetUnitName(); + process_sample_process.pparam = Process::GetProcessParameter(); + process_sample_process.datawrite = g_process_wbytes; + process_sample_process.dataread = g_process_rbytes; + process_sample_process.state.severity = static_cast(g_process_severity); + process_sample_process.state.severity_level = static_cast(g_process_severity_level); + process_sample_process.state.info = g_process_info; +#if ECAL_CORE_TIMEPLUGIN + if (g_timegate() == nullptr) + { + process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; + } + else + { + if (!g_timegate()->IsSynchronized()) + { + process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; + } + else + { + switch (g_timegate()->GetSyncMode()) + { + case CTimeGate::eTimeSyncMode::realtime: + process_sample_process.tsync_state = Registration::eTSyncState::tsync_realtime; + break; + case CTimeGate::eTimeSyncMode::replay: + process_sample_process.tsync_state = Registration::eTSyncState::tsync_replay; + break; + default: + process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; + break; + } + } + process_sample_process.tsync_mod_name = g_timegate()->GetName(); + } +#endif + + // eCAL initialization state + const unsigned int comp_state(g_globals()->GetComponents()); + process_sample_process.component_init_state = comp_state; + std::string component_info; + if ((comp_state & Init::Publisher) != 0u) component_info += "|pub"; + if ((comp_state & Init::Subscriber) != 0u) component_info += "|sub"; + if ((comp_state & Init::Logging) != 0u) component_info += "|log"; + if ((comp_state & Init::TimeSync) != 0u) component_info += "|time"; + if (!component_info.empty()) component_info = component_info.substr(1); + process_sample_process.component_init_info = component_info; + + process_sample_process.ecal_runtime_version = GetVersionString(); + + // apply registration sample + const bool return_value = ApplySample(Process::GetHostName(), process_sample); + + return return_value; + } + + bool CRegistrationProvider::UnregisterProcess() + { + if (!m_created) return(false); + if (!m_reg_process) return(false); + + Registration::Sample process_sample; + process_sample.cmd_type = bct_unreg_process; + auto& process_sample_process = process_sample.process; + process_sample_process.hname = Process::GetHostName(); + process_sample_process.pid = Process::GetProcessID(); + process_sample_process.pname = Process::GetProcessName(); + process_sample_process.uname = Process::GetUnitName(); + + // apply unregistration sample + const bool return_value = ApplySample(Process::GetHostName(), process_sample); + + return return_value; + } + + bool CRegistrationProvider::RegisterTopics() + { + if (!m_created) return(false); + if (!m_reg_topics) return(false); + + bool return_value {true}; + const std::lock_guard lock(m_topics_map_sync); + for(SampleMapT::const_iterator iter = m_topics_map.begin(); iter != m_topics_map.end(); ++iter) + { + ////////////////////////////////////////////// + // update description + ////////////////////////////////////////////// + // read attributes + const std::string topic_name(iter->second.topic.tname); + const bool topic_is_a_publisher(iter->second.cmd_type == eCAL::bct_reg_publisher); + + SDataTypeInformation topic_info; + const auto& topic_datatype = iter->second.topic.tdatatype; + topic_info.encoding = topic_datatype.encoding; + topic_info.name = topic_datatype.name; + topic_info.descriptor = topic_datatype.desc; + + ApplyTopicToDescGate(topic_name, topic_info, topic_is_a_publisher); + + ////////////////////////////////////////////// + // send sample to registration layer + ////////////////////////////////////////////// + return_value &= ApplySample(iter->second.topic.tname, iter->second); + } + + return return_value; + } + + bool CRegistrationProvider::RegisterServer() + { + if (!m_created) return(false); + if (!m_reg_services) return(false); + + bool return_value {true}; + const std::lock_guard lock(m_server_map_sync); + for (SampleMapT::const_iterator iter = m_server_map.begin(); iter != m_server_map.end(); ++iter) + { + ////////////////////////////////////////////// + // update description + ////////////////////////////////////////////// + const auto& ecal_sample_service = iter->second.service; + for (const auto& method : ecal_sample_service.methods) + { + SDataTypeInformation request_type; + request_type.name = method.req_type; + request_type.descriptor = method.req_desc; + + SDataTypeInformation response_type; + response_type.name = method.resp_type; + response_type.descriptor = method.resp_desc; + + ApplyServiceToDescGate(ecal_sample_service.sname, method.mname, request_type, response_type); + } + + ////////////////////////////////////////////// + // send sample to registration layer + ////////////////////////////////////////////// + return_value &= ApplySample(iter->second.service.sname, iter->second); + } + + return return_value; + } + + bool CRegistrationProvider::RegisterClient() + { + if (!m_created) return(false); + if (!m_reg_services) return(false); + + bool return_value {true}; + const std::lock_guard lock(m_client_map_sync); + for (SampleMapT::const_iterator iter = m_client_map.begin(); iter != m_client_map.end(); ++iter) + { + // apply registration sample + return_value &= ApplySample(iter->second.client.sname, iter->second); + } + + return return_value; + } + + bool CRegistrationProvider::ApplySample(const std::string& sample_name_, const Registration::Sample& sample_) + { + if(!m_created) return(false); + + bool return_value {true}; + + if (m_use_registration_udp && m_reg_sample_snd) + { + const std::lock_guard lock(m_sample_buffer_sync); + if (SerializeToBuffer(sample_, m_sample_buffer)) + { + return_value &= (m_reg_sample_snd->Send(sample_name_, m_sample_buffer) != 0); + } + } + +#if ECAL_CORE_REGISTRATION_SHM + if (m_use_registration_shm) + { + const std::lock_guard lock(m_sample_list_sync); + m_sample_list.samples.push_back(sample_); + } +#endif + + return return_value; + } + + bool CRegistrationProvider::SendSampleList(bool reset_sample_list_) + { + if (!m_created) return(false); + bool return_value{ true }; + +#if ECAL_CORE_REGISTRATION_SHM + if (m_use_registration_shm) + { + { + const std::lock_guard lock(m_sample_list_sync); + if (SerializeToBuffer(m_sample_list, m_sample_list_buffer)) + { + if (reset_sample_list_) + { + m_sample_list.samples.clear(); + } + } + } + + if (!m_sample_list_buffer.empty()) + { + return_value &= m_memfile_broadcast_writer.Write(m_sample_list_buffer.data(), m_sample_list_buffer.size()); + } + } +#endif + + return return_value; + } + + void CRegistrationProvider::RegisterSendThread() + { + // calculate average receive bytes + g_process_rbytes = static_cast(((double)g_process_rbytes_sum / m_reg_refresh) * 1000.0); + g_process_rbytes_sum = 0; + + // calculate average write bytes + g_process_wbytes = static_cast(((double)g_process_wbytes_sum / m_reg_refresh) * 1000.0); + g_process_wbytes_sum = 0; + +#if ECAL_CORE_SUBSCRIBER + // refresh subscriber registration + if (g_subgate() != nullptr) g_subgate()->RefreshRegistrations(); +#endif + +#if ECAL_CORE_PUBLISHER + // refresh publisher registration + if (g_pubgate() != nullptr) g_pubgate()->RefreshRegistrations(); +#endif + +#if ECAL_CORE_SERVICE + // refresh server registration + if (g_servicegate() != nullptr) g_servicegate()->RefreshRegistrations(); + + // refresh client registration + if (g_clientgate() != nullptr) g_clientgate()->RefreshRegistrations(); +#endif + + // register process + RegisterProcess(); + +#if ECAL_CORE_SERVICE + // register server + RegisterServer(); + + // register clients + RegisterClient(); +#endif + + // register topics + RegisterTopics(); + + // write sample list to shared memory + SendSampleList(); + } +} diff --git a/src/core/src/registration/ecal_registration_provider.h b/src/core/src/registration/ecal_registration_provider.h new file mode 100644 index 0000000..bb190e4 --- /dev/null +++ b/src/core/src/registration/ecal_registration_provider.h @@ -0,0 +1,118 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL registration provider + * + * All process internal publisher/subscriber, server/clients register here with all their attributes. + * + * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. + * +**/ + +#pragma once + +#include "io/udp/ecal_udp_sample_sender.h" + +#include "util/ecal_thread.h" + +#if ECAL_CORE_REGISTRATION_SHM +#include "shm/ecal_memfile_broadcast.h" +#include "shm/ecal_memfile_broadcast_writer.h" +#endif + +#include "serialization/ecal_serialize_sample_registration.h" + +#include +#include +#include +#include +#include +#include + +namespace eCAL +{ + class CRegistrationProvider + { + public: + CRegistrationProvider(); + ~CRegistrationProvider(); + + void Create(bool topics_, bool services_, bool process_); + void Destroy(); + + bool RegisterTopic(const std::string& topic_name_, const std::string& topic_id_, const Registration::Sample& ecal_sample_, bool force_); + bool UnregisterTopic(const std::string& topic_name_, const std::string& topic_id_, const Registration::Sample& ecal_sample_, bool force_); + + bool RegisterServer(const std::string& service_name_, const std::string& service_id_, const Registration::Sample& ecal_sample_, bool force_); + bool UnregisterServer(const std::string& service_name_, const std::string& service_id_, const Registration::Sample& ecal_sample_, bool force_); + + bool RegisterClient(const std::string& client_name_, const std::string& client_id_, const Registration::Sample& ecal_sample_, bool force_); + bool UnregisterClient(const std::string& client_name_, const std::string& client_id_, const Registration::Sample& ecal_sample_, bool force_); + + protected: + bool RegisterProcess(); + bool UnregisterProcess(); + + bool RegisterTopics(); + + bool RegisterServer(); + bool RegisterClient(); + + bool ApplySample(const std::string& sample_name_, const eCAL::Registration::Sample& sample_); + + void RegisterSendThread(); + + bool SendSampleList(bool reset_sample_list_ = true); + + static std::atomic m_created; + int m_reg_refresh; + bool m_reg_topics; + bool m_reg_services; + bool m_reg_process; + + std::shared_ptr m_reg_sample_snd; + std::shared_ptr m_reg_sample_snd_thread; + + std::mutex m_sample_buffer_sync; + std::vector m_sample_buffer; + + using SampleMapT = std::unordered_map; + std::mutex m_topics_map_sync; + SampleMapT m_topics_map; + + std::mutex m_server_map_sync; + SampleMapT m_server_map; + + std::mutex m_client_map_sync; + SampleMapT m_client_map; + +#if ECAL_CORE_REGISTRATION_SHM + std::mutex m_sample_list_sync; + Registration::SampleList m_sample_list; + std::vector m_sample_list_buffer; + + CMemoryFileBroadcast m_memfile_broadcast; + CMemoryFileBroadcastWriter m_memfile_broadcast_writer; +#endif + + bool m_use_registration_udp; + bool m_use_registration_shm; + }; +} diff --git a/src/core/src/registration/ecal_registration_receiver.cpp b/src/core/src/registration/ecal_registration_receiver.cpp new file mode 100644 index 0000000..1e857f1 --- /dev/null +++ b/src/core/src/registration/ecal_registration_receiver.cpp @@ -0,0 +1,392 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL registration receiver + * + * Receives registration information from external eCAL processes and forwards them to + * the internal publisher/subscriber, server/clients. + * +**/ + +#include "ecal_registration_receiver.h" +#include "ecal_global_accessors.h" + +#include "pubsub/ecal_subgate.h" +#include "pubsub/ecal_pubgate.h" +#include "service/ecal_clientgate.h" + +#include "io/udp/ecal_udp_configurations.h" +#include "ecal_sample_to_topicinfo.h" + +namespace eCAL +{ + ////////////////////////////////////////////////////////////////// + // CRegistrationReceiver + ////////////////////////////////////////////////////////////////// + std::atomic CRegistrationReceiver::m_created; + + CRegistrationReceiver::CRegistrationReceiver() : + m_network(NET_ENABLED), + m_loopback(false), + m_callback_pub(nullptr), + m_callback_sub(nullptr), + m_callback_service(nullptr), + m_callback_client(nullptr), + m_callback_process(nullptr), + m_use_registration_udp(false), + m_use_registration_shm(false), + m_callback_custom_apply_sample([](const auto&) {}), + m_host_group_name(Process::GetHostGroupName()) + { + } + + CRegistrationReceiver::~CRegistrationReceiver() + { + Destroy(); + } + + void CRegistrationReceiver::Create() + { + if(m_created) return; + + // network mode + m_network = Config::IsNetworkEnabled(); + + // receive registration from shared memory and or udp + m_use_registration_udp = !Config::Experimental::IsNetworkMonitoringDisabled(); + m_use_registration_shm = Config::Experimental::IsShmMonitoringEnabled(); + + if (m_use_registration_udp) + { + // set network attributes + IO::UDP::SReceiverAttr attr; + attr.address = UDP::GetRegistrationAddress(); + attr.port = UDP::GetRegistrationPort(); + attr.broadcast = UDP::IsBroadcast(); + attr.loopback = true; + attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); + + // start registration sample receiver + m_registration_receiver = std::make_shared(attr, std::bind(&CRegistrationReceiver::HasSample, this, std::placeholders::_1), std::bind(&CRegistrationReceiver::ApplySerializedSample, this, std::placeholders::_1, std::placeholders::_2)); + } + +#if ECAL_CORE_REGISTRATION_SHM + if (m_use_registration_shm) + { + m_memfile_broadcast.Create(Config::Experimental::GetShmMonitoringDomain(), Config::Experimental::GetShmMonitoringQueueSize()); + m_memfile_broadcast.FlushLocalEventQueue(); + m_memfile_broadcast_reader.Bind(&m_memfile_broadcast); + + m_memfile_reg_rcv.Create(&m_memfile_broadcast_reader); + } +#endif + + m_created = true; + } + + void CRegistrationReceiver::Destroy() + { + if(!m_created) return; + + // stop network registration receive thread + m_registration_receiver = nullptr; + + // stop network registration receive thread + if (m_use_registration_udp) + { + m_registration_receiver = nullptr; + } + +#if ECAL_CORE_REGISTRATION_SHM + if (m_use_registration_shm) + { + // stop memfile registration receive thread and unbind reader + m_memfile_broadcast_reader.Unbind(); + m_memfile_broadcast.Destroy(); + } +#endif + + // reset callbacks + m_callback_pub = nullptr; + m_callback_sub = nullptr; + m_callback_service = nullptr; + m_callback_client = nullptr; + m_callback_process = nullptr; + + // finished + m_created = false; + } + + void CRegistrationReceiver::EnableLoopback(bool state_) + { + m_loopback = state_; + } + + bool CRegistrationReceiver::ApplySerializedSample(const char* serialized_sample_data_, size_t serialized_sample_size_) + { + if(!m_created) return false; + + Registration::Sample ecal_sample; + if (!DeserializeFromBuffer(serialized_sample_data_, serialized_sample_size_, ecal_sample)) return false; + + return ApplySample(ecal_sample); + } + + bool CRegistrationReceiver::ApplySample(const Registration::Sample& ecal_sample_) + { + if (!m_created) return false; + + //Remove in eCAL6 + // for the time being we need to copy the incoming sample and set the incompatible fields + Registration::Sample modified_ttype_sample; + ModifyIncomingSampleForBackwardsCompatibility(ecal_sample_, modified_ttype_sample); + + // forward all registration samples to outside "customer" (e.g. Monitoring) + { + const std::lock_guard lock(m_callback_custom_apply_sample_mtx); + m_callback_custom_apply_sample(modified_ttype_sample); + } + + std::string reg_sample; + if (m_callback_pub + || m_callback_sub + || m_callback_service + || m_callback_client + || m_callback_process + ) + { + SerializeToBuffer(modified_ttype_sample, reg_sample); + } + + switch (modified_ttype_sample.cmd_type) + { + case bct_none: + case bct_set_sample: + break; + case bct_reg_process: + case bct_unreg_process: + // unregistration event not implemented currently + if (m_callback_process) m_callback_process(reg_sample.c_str(), static_cast(reg_sample.size())); + break; +#if ECAL_CORE_SERVICE + case bct_reg_service: + if (g_clientgate() != nullptr) g_clientgate()->ApplyServiceRegistration(modified_ttype_sample); + if (m_callback_service) m_callback_service(reg_sample.c_str(), static_cast(reg_sample.size())); + break; + case bct_unreg_service: + // current client implementation doesn't need that information + if (m_callback_service) m_callback_service(reg_sample.c_str(), static_cast(reg_sample.size())); + break; +#endif + case bct_reg_client: + case bct_unreg_client: + // current service implementation doesn't need that information + if (m_callback_client) m_callback_client(reg_sample.c_str(), static_cast(reg_sample.size())); + break; + case bct_reg_subscriber: + case bct_unreg_subscriber: + ApplySubscriberRegistration(modified_ttype_sample); + if (m_callback_sub) m_callback_sub(reg_sample.c_str(), static_cast(reg_sample.size())); + break; + case bct_reg_publisher: + case bct_unreg_publisher: + ApplyPublisherRegistration(modified_ttype_sample); + if (m_callback_pub) m_callback_pub(reg_sample.c_str(), static_cast(reg_sample.size())); + break; + default: + Logging::Log(log_level_debug1, "CRegistrationReceiver::ApplySample : unknown sample type"); + break; + } + + return true; + } + + bool CRegistrationReceiver::AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_) + { + if (!m_created) return false; + switch (event_) + { + case reg_event_publisher: + m_callback_pub = callback_; + return true; + case reg_event_subscriber: + m_callback_sub = callback_; + return true; + case reg_event_service: + m_callback_service = callback_; + return true; + case reg_event_client: + m_callback_client = callback_; + return true; + case reg_event_process: + m_callback_process = callback_; + return true; + default: + return false; + } + } + + bool CRegistrationReceiver::RemRegistrationCallback(enum eCAL_Registration_Event event_) + { + if (!m_created) return false; + switch (event_) + { + case reg_event_publisher: + m_callback_pub = nullptr; + return true; + case reg_event_subscriber: + m_callback_sub = nullptr; + return true; + case reg_event_service: + m_callback_service = nullptr; + return true; + case reg_event_client: + m_callback_client = nullptr; + return true; + case reg_event_process: + m_callback_process = nullptr; + return true; + default: + return false; + } + } + + void CRegistrationReceiver::ApplySubscriberRegistration(const Registration::Sample& ecal_sample_) + { +#if ECAL_CORE_PUBLISHER + // process registrations from same host group + if (IsHostGroupMember(ecal_sample_)) + { + // do not register local entities, only if loop back flag is set true + if (m_loopback || (ecal_sample_.topic.pid != Process::GetProcessID())) + { + if (g_pubgate() != nullptr) + { + switch (ecal_sample_.cmd_type) + { + case bct_reg_subscriber: + g_pubgate()->ApplyLocSubRegistration(ecal_sample_); + break; + case bct_unreg_subscriber: + g_pubgate()->ApplyLocSubUnregistration(ecal_sample_); + break; + default: + break; + } + } + } + } + // process external registrations + else + { + if (m_network) + { + if (g_pubgate() != nullptr) + { + switch (ecal_sample_.cmd_type) + { + case bct_reg_subscriber: + g_pubgate()->ApplyExtSubRegistration(ecal_sample_); + break; + case bct_unreg_subscriber: + g_pubgate()->ApplyExtSubUnregistration(ecal_sample_); + break; + default: + break; + } + } + } + } +#endif + } + + void CRegistrationReceiver::ApplyPublisherRegistration(const Registration::Sample& ecal_sample_) + { +#if ECAL_CORE_SUBSCRIBER + // process registrations from same host group + if (IsHostGroupMember(ecal_sample_)) + { + // do not register local entities, only if loop back flag is set true + if (m_loopback || (ecal_sample_.topic.pid != Process::GetProcessID())) + { + if (g_subgate() != nullptr) + { + switch (ecal_sample_.cmd_type) + { + case bct_reg_publisher: + g_subgate()->ApplyLocPubRegistration(ecal_sample_); + break; + case bct_unreg_publisher: + g_subgate()->ApplyLocPubUnregistration(ecal_sample_); + break; + default: + break; + } + } + } + } + // process external registrations + else + { + if (m_network) + { + if (g_subgate() != nullptr) + { + switch (ecal_sample_.cmd_type) + { + case bct_reg_publisher: + g_subgate()->ApplyExtPubRegistration(ecal_sample_); + break; + case bct_unreg_publisher: + g_subgate()->ApplyExtPubUnregistration(ecal_sample_); + break; + default: + break; + } + } + } + } +#endif + } + + bool CRegistrationReceiver::IsHostGroupMember(const Registration::Sample& ecal_sample_) + { + const std::string& sample_host_group_name = ecal_sample_.topic.hgname.empty() ? ecal_sample_.topic.hname : ecal_sample_.topic.hgname; + + if (sample_host_group_name.empty() || m_host_group_name.empty()) + return false; + if (sample_host_group_name != m_host_group_name) + return false; + + return true; + } + + void CRegistrationReceiver::SetCustomApplySampleCallback(const ApplySampleCallbackT& callback_) + { + const std::lock_guard lock(m_callback_custom_apply_sample_mtx); + m_callback_custom_apply_sample = callback_; + } + + void CRegistrationReceiver::RemCustomApplySampleCallback() + { + const std::lock_guard lock(m_callback_custom_apply_sample_mtx); + m_callback_custom_apply_sample = [](const auto&) {}; + } +} diff --git a/src/core/src/registration/ecal_registration_receiver.h b/src/core/src/registration/ecal_registration_receiver.h new file mode 100644 index 0000000..a6ababc --- /dev/null +++ b/src/core/src/registration/ecal_registration_receiver.h @@ -0,0 +1,106 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL registration receiver + * + * Receives registration information from external eCAL processes and forwards them to + * the internal publisher/subscriber, server/clients. + * +**/ + +#pragma once + +#include + +#include "ecal_def.h" + +#include "io/udp/ecal_udp_sample_receiver.h" +#include "serialization/ecal_struct_sample_registration.h" + +#if ECAL_CORE_REGISTRATION_SHM +#include "ecal_registration_receiver_shm.h" +#endif + +#include +#include +#include +#include +#include +#include + +namespace eCAL +{ + class CRegistrationReceiver + { + public: + CRegistrationReceiver(); + ~CRegistrationReceiver(); + + void Create(); + void Destroy(); + + void EnableLoopback(bool state_); + + bool HasSample(const std::string& /*sample_name_*/) { return(true); }; + bool ApplySerializedSample(const char* serialized_sample_data_, size_t serialized_sample_size_); + + bool ApplySample(const Registration::Sample& ecal_sample_); + + bool AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_); + bool RemRegistrationCallback(enum eCAL_Registration_Event event_); + + using ApplySampleCallbackT = std::function; + void SetCustomApplySampleCallback(const ApplySampleCallbackT& callback_); + void RemCustomApplySampleCallback(); + + protected: + void ApplySubscriberRegistration(const eCAL::Registration::Sample& ecal_sample_); + void ApplyPublisherRegistration(const eCAL::Registration::Sample& ecal_sample_); + + bool IsHostGroupMember(const eCAL::Registration::Sample& ecal_sample_); + + static std::atomic m_created; + bool m_network; + bool m_loopback; + + RegistrationCallbackT m_callback_pub; + RegistrationCallbackT m_callback_sub; + RegistrationCallbackT m_callback_service; + RegistrationCallbackT m_callback_client; + RegistrationCallbackT m_callback_process; + + std::shared_ptr m_registration_receiver; + +#if ECAL_CORE_REGISTRATION_SHM + CMemoryFileBroadcast m_memfile_broadcast; + CMemoryFileBroadcastReader m_memfile_broadcast_reader; + + CMemfileRegistrationReceiver m_memfile_reg_rcv; +#endif + + bool m_use_registration_udp; + bool m_use_registration_shm; + + std::mutex m_callback_custom_apply_sample_mtx; + ApplySampleCallbackT m_callback_custom_apply_sample; + + std::string m_host_group_name; + }; +} diff --git a/src/core/src/registration/ecal_registration_receiver_shm.cpp b/src/core/src/registration/ecal_registration_receiver_shm.cpp new file mode 100644 index 0000000..b1e89ff --- /dev/null +++ b/src/core/src/registration/ecal_registration_receiver_shm.cpp @@ -0,0 +1,85 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL registration receiver + * + * Receives registration information from external eCAL processes and forwards them to + * the internal publisher/subscriber, server/clients. + * +**/ + +#include "ecal_globals.h" + +#include "ecal_registration_receiver_shm.h" +#include "serialization/ecal_serialize_sample_registration.h" + +namespace eCAL +{ + ////////////////////////////////////////////////////////////////// + // CMemfileRegistrationReceiver + ////////////////////////////////////////////////////////////////// + + CMemfileRegistrationReceiver::~CMemfileRegistrationReceiver() + { + Destroy(); + } + + void CMemfileRegistrationReceiver::Create(eCAL::CMemoryFileBroadcastReader* memfile_broadcast_reader_) + { + if (m_created) return; + + // start memfile broadcast receive thread + m_memfile_broadcast_reader = memfile_broadcast_reader_; + m_memfile_broadcast_reader_thread = std::make_shared(std::bind(&CMemfileRegistrationReceiver::Receive, this)); + m_memfile_broadcast_reader_thread->start(std::chrono::milliseconds(Config::GetRegistrationRefreshMs()/2)); + + m_created = true; + } + + void CMemfileRegistrationReceiver::Destroy() + { + if (!m_created) return; + + // stop memfile broadcast receive thread + m_memfile_broadcast_reader_thread->stop(); + m_memfile_broadcast_reader = nullptr; + + m_created = false; + } + + void CMemfileRegistrationReceiver::Receive() + { + MemfileBroadcastMessageListT message_list; + if (m_memfile_broadcast_reader->Read(message_list, 0)) + { + eCAL::Registration::SampleList sample_list; + for (const auto& message : message_list) + { + if (DeserializeFromBuffer(static_cast(message.data), message.size, sample_list)) + { + for (const auto& sample : sample_list.samples) + { + if (g_registration_receiver()) g_registration_receiver()->ApplySample(sample); + } + } + } + } + } +} diff --git a/src/core/src/registration/ecal_registration_receiver_shm.h b/src/core/src/registration/ecal_registration_receiver_shm.h new file mode 100644 index 0000000..45a554a --- /dev/null +++ b/src/core/src/registration/ecal_registration_receiver_shm.h @@ -0,0 +1,63 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL registration receiver + * + * Receives registration information from external eCAL processes and forwards them to + * the internal publisher/subscriber, server/clients. + * +**/ + +#pragma once + +#include "shm/ecal_memfile_broadcast.h" +#include "shm/ecal_memfile_broadcast_reader.h" + +#include "util/ecal_thread.h" + +namespace eCAL +{ + class CMemfileRegistrationReceiver + { + public: + CMemfileRegistrationReceiver() = default; + ~CMemfileRegistrationReceiver(); + + // default copy constructor + CMemfileRegistrationReceiver(const CMemfileRegistrationReceiver& other) = delete; + // default copy assignment operator + CMemfileRegistrationReceiver& operator=(const CMemfileRegistrationReceiver& other) = delete; + // default move constructor + CMemfileRegistrationReceiver(CMemfileRegistrationReceiver&& other) noexcept = delete; + // default move assignment operator + CMemfileRegistrationReceiver& operator=(CMemfileRegistrationReceiver&& other) noexcept = delete; + + void Create(CMemoryFileBroadcastReader* memfile_broadcast_reader_); + void Destroy(); + + private: + void Receive(); + + CMemoryFileBroadcastReader* m_memfile_broadcast_reader = nullptr; + std::shared_ptr m_memfile_broadcast_reader_thread; + + bool m_created = false; + }; +} diff --git a/ecal/core/src/io/ecal_memfile_broadcast.cpp b/src/core/src/registration/shm/ecal_memfile_broadcast.cpp similarity index 99% rename from ecal/core/src/io/ecal_memfile_broadcast.cpp rename to src/core/src/registration/shm/ecal_memfile_broadcast.cpp index ae9f8bc..f342f8e 100644 --- a/ecal/core/src/io/ecal_memfile_broadcast.cpp +++ b/src/core/src/registration/shm/ecal_memfile_broadcast.cpp @@ -24,7 +24,7 @@ #include "ecal_def.h" #include "ecal_memfile_broadcast.h" -#include "ecal_memfile.h" +#include "io/shm/ecal_memfile.h" #include "ecal_global_accessors.h" #include diff --git a/ecal/core/src/io/ecal_memfile_broadcast.h b/src/core/src/registration/shm/ecal_memfile_broadcast.h similarity index 98% rename from ecal/core/src/io/ecal_memfile_broadcast.h rename to src/core/src/registration/shm/ecal_memfile_broadcast.h index e622f43..9c500ee 100644 --- a/ecal/core/src/io/ecal_memfile_broadcast.h +++ b/src/core/src/registration/shm/ecal_memfile_broadcast.h @@ -30,13 +30,12 @@ #include #include "relocatable_circular_queue.h" +#include "io/shm/ecal_memfile.h" #include namespace eCAL { - class CMemoryFile; - static inline std::int64_t CreateTimestamp() { const auto time_point = std::chrono::steady_clock::now(); diff --git a/ecal/core/src/io/ecal_memfile_broadcast_reader.cpp b/src/core/src/registration/shm/ecal_memfile_broadcast_reader.cpp similarity index 99% rename from ecal/core/src/io/ecal_memfile_broadcast_reader.cpp rename to src/core/src/registration/shm/ecal_memfile_broadcast_reader.cpp index 1fb08ec..cf1ed8b 100644 --- a/ecal/core/src/io/ecal_memfile_broadcast_reader.cpp +++ b/src/core/src/registration/shm/ecal_memfile_broadcast_reader.cpp @@ -22,7 +22,7 @@ **/ #include "ecal_memfile_broadcast_reader.h" -#include "ecal_memfile.h" +#include "io/shm/ecal_memfile.h" #include "ecal_def.h" namespace eCAL diff --git a/ecal/core/src/io/ecal_memfile_broadcast_reader.h b/src/core/src/registration/shm/ecal_memfile_broadcast_reader.h similarity index 74% rename from ecal/core/src/io/ecal_memfile_broadcast_reader.h rename to src/core/src/registration/shm/ecal_memfile_broadcast_reader.h index 8dda745..2e16a4c 100644 --- a/ecal/core/src/io/ecal_memfile_broadcast_reader.h +++ b/src/core/src/registration/shm/ecal_memfile_broadcast_reader.h @@ -34,20 +34,20 @@ namespace eCAL { struct SMemfileBroadcastMessage { - const void *data; - std::size_t size; - std::int64_t timestamp; + const void *data; + std::size_t size = 0; + std::int64_t timestamp = 0; }; - typedef std::vector MemfileBroadcastMessageListT; + using MemfileBroadcastMessageListT = std::vector; class CMemoryFileBroadcastReader { struct SPayloadMemfile { std::shared_ptr payload_memfile; - std::vector payload_memfile_buffer; - std::int64_t timestamp; + std::vector payload_memfile_buffer; + std::int64_t timestamp; }; public: bool Bind(CMemoryFileBroadcast *memfile_broadcast); @@ -55,9 +55,9 @@ namespace eCAL bool Read(MemfileBroadcastMessageListT &memfile_broadcast_message_list, std::int64_t timeout); private: - CMemoryFileBroadcast *m_memfile_broadcast = nullptr; + CMemoryFileBroadcast *m_memfile_broadcast = nullptr; std::unordered_map m_payload_memfiles; - MemfileBroadcastEventListT m_broadcast_event_list; - bool m_bound = false; + MemfileBroadcastEventListT m_broadcast_event_list; + bool m_bound = false; }; } \ No newline at end of file diff --git a/ecal/core/src/io/ecal_memfile_broadcast_writer.cpp b/src/core/src/registration/shm/ecal_memfile_broadcast_writer.cpp similarity index 97% rename from ecal/core/src/io/ecal_memfile_broadcast_writer.cpp rename to src/core/src/registration/shm/ecal_memfile_broadcast_writer.cpp index 3604dca..2e9e911 100644 --- a/ecal/core/src/io/ecal_memfile_broadcast_writer.cpp +++ b/src/core/src/registration/shm/ecal_memfile_broadcast_writer.cpp @@ -22,7 +22,7 @@ **/ #include "ecal_memfile_broadcast_writer.h" -#include "ecal_memfile.h" +#include "io/shm/ecal_memfile.h" #include "ecal_def.h" namespace eCAL @@ -72,7 +72,7 @@ namespace eCAL if (m_payload_memfile->GetWriteAccess(EXP_MEMFILE_ACCESS_TIMEOUT)) { - m_payload_memfile->Write(data, size, 0); + m_payload_memfile->WriteBuffer(data, size, 0); m_payload_memfile->ReleaseWriteAccess(); m_memfile_broadcast->SendEvent(m_event_id, eMemfileBroadcastEventType::EVENT_UPDATED); diff --git a/ecal/core/src/io/ecal_memfile_broadcast_writer.h b/src/core/src/registration/shm/ecal_memfile_broadcast_writer.h similarity index 100% rename from ecal/core/src/io/ecal_memfile_broadcast_writer.h rename to src/core/src/registration/shm/ecal_memfile_broadcast_writer.h diff --git a/ecal/core/src/io/relocatable_circular_queue.h b/src/core/src/registration/shm/relocatable_circular_queue.h similarity index 100% rename from ecal/core/src/io/relocatable_circular_queue.h rename to src/core/src/registration/shm/relocatable_circular_queue.h diff --git a/src/core/src/serialization/ecal_serialize_common.cpp b/src/core/src/serialization/ecal_serialize_common.cpp new file mode 100644 index 0000000..aaff165 --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_common.cpp @@ -0,0 +1,397 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_common.cpp + * @brief eCAL common (de)serialization +**/ + +#include "nanopb/pb_encode.h" +#include "nanopb/pb_decode.h" +#include "nanopb/ecal.pb.h" + +#include "ecal_serialize_common.h" + +namespace eCAL +{ + namespace nanopb + { + /////////////////////////////////////////////// + // string + /////////////////////////////////////////////// + bool encode_string_field(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + auto* str = (std::string*)(*arg); + return pb_encode_string(stream, (pb_byte_t*)(str->c_str()), str->size()); + } + + void encode_string(pb_callback_t& pb_callback, const std::string& str) + { + if (str.empty()) return; + + pb_callback.funcs.encode = &encode_string_field; + pb_callback.arg = (void*)(&str); + } + + bool decode_string_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + size_t len = stream->bytes_left; + auto tgt_string = (std::string*)(*arg); + tgt_string->resize(len); + + if (!pb_read(stream, (pb_byte_t*)(tgt_string->data()), tgt_string->size())) + return false; + + return true; + } + + void decode_string(pb_callback_t& pb_callback, std::string& str) + { + pb_callback.funcs.decode = &decode_string_field; + pb_callback.arg = &str; + } + + /////////////////////////////////////////////// + // bytes + /////////////////////////////////////////////// + bool encode_bytes_field(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + auto* bytes = (SNanoBytes*)(*arg); + return pb_encode_string(stream, (pb_byte_t*)bytes->content, bytes->length); + } + + void encode_bytes(pb_callback_t& pb_callback, const SNanoBytes& nano_bytes) + { + if (nano_bytes.length == 0) return; + + pb_callback.funcs.encode = &encode_bytes_field; + pb_callback.arg = (void*)(&nano_bytes); + } + + bool decode_bytes_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + size_t len = stream->bytes_left; + auto tgt_vector = (std::vector*)(*arg); + tgt_vector->resize(len); + + if (!pb_read(stream, (pb_byte_t*)(tgt_vector->data()), tgt_vector->size())) + return false; + + return true; + } + + void decode_bytes(pb_callback_t& pb_callback, std::vector& vec) + { + pb_callback.funcs.decode = &decode_bytes_field; + pb_callback.arg = &vec; + } + + /////////////////////////////////////////////// + // map + /////////////////////////////////////////////// + bool encode_map_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* attr_map = (std::map*)(*arg); + for (const auto& iter : *attr_map) + { + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + eCAL_pb_Topic_AttrEntry pb_attr_map = eCAL_pb_Topic_AttrEntry_init_default; + encode_string(pb_attr_map.key, iter.first); + encode_string(pb_attr_map.value, iter.second); + + if (!pb_encode_submessage(stream, eCAL_pb_Topic_AttrEntry_fields, &pb_attr_map)) + { + return false; + } + } + + return true; + } + + void encode_map(pb_callback_t& pb_callback, const std::map& str_map) + { + pb_callback.funcs.encode = &encode_map_field; + pb_callback.arg = (void*)(&str_map); + } + + bool decode_map_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Topic_AttrEntry attr_entry = eCAL_pb_Topic_AttrEntry_init_default; + std::string key; + std::string value; + decode_string(attr_entry.key, key); + decode_string(attr_entry.value, value); + + if (!pb_decode(stream, eCAL_pb_Topic_AttrEntry_fields, &attr_entry)) + { + return false; + } + + auto tgt_map = (std::map*)(*arg); + (*tgt_map)[key] = value; + + return true; + } + + void decode_map(pb_callback_t& pb_callback, std::map& str_map) + { + pb_callback.funcs.decode = &decode_map_field; + pb_callback.arg = &str_map; + } + + /////////////////////////////////////////////// + // list + /////////////////////////////////////////////// + bool encode_string_list_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 = (std::list*)(*arg); + + for (const auto& str : *str_list) + { + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + if (!pb_encode_string(stream, (pb_byte_t*)str.c_str(), str.size())) + { + return false; + } + } + + return true; + } + + bool decode_string_list_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + 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())) + return false; + + auto tgt_list = (std::list*)(*arg); + tgt_list->push_back(tgt_string); + + return true; + } + + /////////////////////////////////////////////// + // registration_layer + /////////////////////////////////////////////// + bool encode_registration_layer_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* layer_vec = (std::vector*)(*arg); + + for (auto layer : *layer_vec) + { + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; + pb_layer.type = static_cast(layer.type); + pb_layer.version = layer.version; + pb_layer.confirmed = layer.confirmed; + + // layer + pb_layer.has_par_layer = true; + + // udp layer parameter + pb_layer.par_layer.has_layer_par_udpmc = false; + + // tcp layer parameter + pb_layer.par_layer.has_layer_par_tcp = true; + pb_layer.par_layer.layer_par_tcp.port = layer.par_layer.layer_par_tcp.port; + + // 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; + 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)) + { + return false; + } + } + + return true; + } + + void encode_registration_layer(pb_callback_t& pb_callback, const std::vector& layer_vec) + { + pb_callback.funcs.encode = &encode_registration_layer_field; + pb_callback.arg = (void*)(&layer_vec); + } + + bool decode_registration_layer_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; + eCAL::Registration::TLayer layer{}; + + // decode shm layer parameter + pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.decode = &decode_string_list_field; + 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)) + { + return false; + } + + // apply layer values + layer.type = static_cast(pb_layer.type); + layer.version = pb_layer.version; + layer.confirmed = pb_layer.confirmed; + + // apply tcp layer parameter + layer.par_layer.layer_par_tcp.port = pb_layer.par_layer.layer_par_tcp.port; + + // add layer + auto tgt_vector = (std::vector*)(*arg); + tgt_vector->push_back(layer); + + return true; + } + + void decode_registration_layer(pb_callback_t& pb_callback, std::vector& layer_vec) + { + pb_callback.funcs.decode = &decode_registration_layer_field; + pb_callback.arg = &layer_vec; + } + + /////////////////////////////////////////////// + // service_methods + /////////////////////////////////////////////// + bool encode_service_methods_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* method_vec = (std::vector*)(*arg); + + for (const auto& method : *method_vec) + { + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + eCAL_pb_Method pb_method = eCAL_pb_Method_init_default; + encode_string(pb_method.mname, method.mname); + encode_string(pb_method.req_type, method.req_type); + encode_string(pb_method.resp_type, method.resp_type); + encode_string(pb_method.req_desc, method.req_desc); + encode_string(pb_method.resp_desc, method.resp_desc); + pb_method.call_count = method.call_count; + + if (!pb_encode_submessage(stream, eCAL_pb_Method_fields, &pb_method)) + { + return false; + } + } + + return true; + } + + void encode_service_methods(pb_callback_t& pb_callback, const std::vector& method_vec) + { + pb_callback.funcs.encode = &encode_service_methods_field; + pb_callback.arg = (void*)(&method_vec); + } + + bool decode_service_methods_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Method pb_method = eCAL_pb_Method_init_default; + eCAL::Service::Method method{}; + + // decode method parameter + decode_string(pb_method.mname, method.mname); + decode_string(pb_method.req_type, method.req_type); + decode_string(pb_method.req_desc, method.req_desc); + decode_string(pb_method.resp_type, method.resp_type); + decode_string(pb_method.resp_desc, method.resp_desc); + + // decode it + if (!pb_decode(stream, eCAL_pb_Method_fields, &pb_method)) + { + return false; + } + + // apply method values + method.call_count = pb_method.call_count; + + // add method to vector + auto* method_vec = (std::vector*)(*arg); + method_vec->emplace_back(method); + + return true; + } + + void decode_service_methods(pb_callback_t& pb_callback, std::vector& method_vec) + { + pb_callback.funcs.decode = &decode_service_methods_field; + pb_callback.arg = &method_vec; + } + } +} diff --git a/src/core/src/serialization/ecal_serialize_common.h b/src/core/src/serialization/ecal_serialize_common.h new file mode 100644 index 0000000..d6d977f --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_common.h @@ -0,0 +1,62 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_common.h + * @brief eCAL common (de)serialization +**/ + +#pragma once + +#include "nanopb/pb_encode.h" +#include "nanopb/pb_decode.h" + +#include "ecal_struct_sample_payload.h" +#include "ecal_struct_sample_registration.h" +#include "ecal_struct_service.h" + +#include +#include + +namespace eCAL +{ + namespace nanopb + { + struct SNanoBytes + { + pb_byte_t* content = nullptr; + size_t length = 0; + }; + + void encode_string(pb_callback_t& pb_callback, const std::string& str); + void decode_string(pb_callback_t& pb_callback, std::string& str); + + void encode_bytes(pb_callback_t& pb_callback, const SNanoBytes& nano_bytes); + void decode_bytes(pb_callback_t& pb_callback, std::vector& vec); + + 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_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); + } +} diff --git a/src/core/src/serialization/ecal_serialize_logging.cpp b/src/core/src/serialization/ecal_serialize_logging.cpp new file mode 100644 index 0000000..3a7f05a --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_logging.cpp @@ -0,0 +1,332 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_logging.cpp + * @brief eCAL logging (de)serialization +**/ + +#include "nanopb/pb_encode.h" +#include "nanopb/pb_decode.h" +#include "nanopb/logging.pb.h" + +#include "ecal_serialize_common.h" +#include "ecal_serialize_logging.h" + +#include + +namespace +{ + ///////////////////////////////////////////////////////////////////////////////// + // eCAL::Logging::LogMessage + ///////////////////////////////////////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Logging::LogMessage& log_message_, eCAL_pb_LogMessage& pb_log_message_) + { + // time + pb_log_message_.time = log_message_.time; + // hname + eCAL::nanopb::encode_string(pb_log_message_.hname, log_message_.hname); + // pid + pb_log_message_.pid = log_message_.pid; + // pname + eCAL::nanopb::encode_string(pb_log_message_.pname, log_message_.pname); + // uname + eCAL::nanopb::encode_string(pb_log_message_.uname, log_message_.uname); + // level + pb_log_message_.level = log_message_.level; + // content + eCAL::nanopb::encode_string(pb_log_message_.content, log_message_.content); + } + + size_t LogMessageStruct2PbLogMessage(const eCAL::Logging::LogMessage& log_message_, eCAL_pb_LogMessage& pb_log_message_) + { + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + PrepareEncoding(log_message_, pb_log_message_); + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr}; + pb_encode(&pb_sizestream, eCAL_pb_LogMessage_fields, &pb_log_message_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool LogMessageStruct2Buffer(const eCAL::Logging::LogMessage& log_message_, T& target_buffer_) + { + target_buffer_.clear(); + + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + eCAL_pb_LogMessage pb_log_message = eCAL_pb_LogMessage_init_default; + size_t target_size = LogMessageStruct2PbLogMessage(log_message_, pb_log_message); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_LogMessage_fields, &pb_log_message)) + { + std::cerr << "NanoPb eCAL::Logging::LogMessage encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + void PrepareDecoding(eCAL_pb_LogMessage& pb_log_message_, eCAL::Logging::LogMessage& log_message_) + { + // initialize + pb_log_message_ = eCAL_pb_LogMessage_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + + // hname + eCAL::nanopb::decode_string(pb_log_message_.hname, log_message_.hname); + // pname + eCAL::nanopb::decode_string(pb_log_message_.pname, log_message_.pname); + // uname + eCAL::nanopb::decode_string(pb_log_message_.uname, log_message_.uname); + // content + eCAL::nanopb::decode_string(pb_log_message_.content, log_message_.content); + } + + void AssignValues(const eCAL_pb_LogMessage& pb_log_message_, eCAL::Logging::LogMessage& log_message_) + { + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + // time + log_message_.time = pb_log_message_.time; + // pid + log_message_.pid = pb_log_message_.pid; + // level + log_message_.level = static_cast(pb_log_message_.level); + } + + bool Buffer2LogMessageStruct(const char* data_, size_t size_, eCAL::Logging::LogMessage& log_message_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_LogMessage pb_log_message = eCAL_pb_LogMessage_init_default; + + /////////////////////////////////////////////// + // prepare sample for decoding + /////////////////////////////////////////////// + PrepareDecoding(pb_log_message, log_message_); + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_LogMessage_fields, &pb_log_message)) + { + std::cerr << "NanoPb eCAL::Logging::LogMessage decode failed: " << pb_istream.errmsg << std::endl; + } + + /////////////////////////////////////////////// + // assign sample values + /////////////////////////////////////////////// + AssignValues(pb_log_message, log_message_); + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // eCAL::Logging::LogMessageList + ///////////////////////////////////////////////////////////////////////////////// + bool encode_log_message_list_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* sample_list = (std::list*)(*arg); + + for (const auto& sample : *sample_list) + { + // encode sample tag + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + // encode single sample + eCAL_pb_LogMessage pb_log_message = eCAL_pb_LogMessage_init_default; + PrepareEncoding(sample, pb_log_message); + + // encode submessage + if (!pb_encode_submessage(stream, eCAL_pb_LogMessage_fields, &pb_log_message)) + { + return false; + } + } + + return true; + } + + size_t LogMessageListStruct2PbLogMessageList(const eCAL::Logging::LogMessageList& log_message_list_, eCAL_pb_LogMessageList& pb_log_message_list_) + { + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + pb_log_message_list_.log_messages.funcs.encode = &encode_log_message_list_field; + pb_log_message_list_.log_messages.arg = (void*)(&log_message_list_.log_messages); + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr}; + pb_encode(&pb_sizestream, eCAL_pb_LogMessageList_fields, &pb_log_message_list_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool LogMessageListStruct2Buffer(const eCAL::Logging::LogMessageList& log_message_list_, T& target_buffer_) + { + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + eCAL_pb_LogMessageList pb_log_message_list = eCAL_pb_LogMessageList_init_default; + size_t target_size = LogMessageListStruct2PbLogMessageList(log_message_list_, pb_log_message_list); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_LogMessageList_fields, &pb_log_message_list)) + { + std::cerr << "NanoPb eCAL::Logging::LogMessageList encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + bool decode_log_message_list_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_LogMessage pb_log_message = eCAL_pb_LogMessage_init_default; + eCAL::Logging::LogMessage sample{}; + + // prepare sample for decoding + PrepareDecoding(pb_log_message, sample); + + // decode it + if (!pb_decode(stream, eCAL_pb_LogMessage_fields, &pb_log_message)) + { + return false; + } + + // apply sample values + AssignValues(pb_log_message, sample); + + // add sample to list + auto* sample_list = (std::list*)(*arg); + sample_list->push_back(sample); + + return true; + } + + bool Buffer2LogMessageListStruct(const char* data_, size_t size_, eCAL::Logging::LogMessageList& log_message_list_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_LogMessageList pb_log_message_list = eCAL_pb_LogMessageList_init_default; + + /////////////////////////////////////////////// + // prepare sample for decoding + /////////////////////////////////////////////// + pb_log_message_list.log_messages.funcs.decode = &decode_log_message_list_field; + pb_log_message_list.log_messages.arg = &log_message_list_.log_messages; + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_LogMessageList_fields, &pb_log_message_list)) + { + std::cerr << "NanoPb eCAL::Logging::LogMessageList decode failed: " << pb_istream.errmsg << std::endl; + } + + return true; + } +} + +namespace eCAL +{ + // log message - serialize/deserialize + bool SerializeToBuffer(const Logging::LogMessage& source_sample_, std::vector& target_buffer_) + { + return LogMessageStruct2Buffer(source_sample_, target_buffer_); + } + + bool SerializeToBuffer(const Logging::LogMessage& source_sample_, std::string& target_buffer_) + { + return LogMessageStruct2Buffer(source_sample_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Logging::LogMessage& target_sample_) + { + return Buffer2LogMessageStruct(data_, size_, target_sample_); + } + + bool SerializeToBuffer(const Logging::LogMessageList& source_sample_, std::vector& target_buffer_) + { + target_buffer_.clear(); + return LogMessageListStruct2Buffer(source_sample_, target_buffer_); + } + + bool SerializeToBuffer(const Logging::LogMessageList& source_sample_, std::string& target_buffer_) + { + target_buffer_.clear(); + return LogMessageListStruct2Buffer(source_sample_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Logging::LogMessageList& target_sample_) + { + return Buffer2LogMessageListStruct(data_, size_, target_sample_); + } +} diff --git a/src/core/src/serialization/ecal_serialize_logging.h b/src/core/src/serialization/ecal_serialize_logging.h new file mode 100644 index 0000000..ab755f9 --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_logging.h @@ -0,0 +1,43 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_logging.h + * @brief eCAL logging serialization / deserialization +**/ + +#pragma once + +#include "ecal_struct_logging.h" + +#include +#include + +namespace eCAL +{ + // logmessage - serialize/deserialize + bool SerializeToBuffer (const Logging::LogMessage& source_sample_, std::vector& target_buffer_); + bool SerializeToBuffer (const Logging::LogMessage& source_sample_, std::string& target_buffer_); + bool DeserializeFromBuffer (const char* data_, size_t size_, Logging::LogMessage& target_sample_); + + // logmessage list - serialize/deserialize + bool SerializeToBuffer (const Logging::LogMessageList& source_sample_, std::vector& target_buffer_); + bool SerializeToBuffer (const Logging::LogMessageList& source_sample_, std::string& target_buffer_); + bool DeserializeFromBuffer (const char* data_, size_t size_, Logging::LogMessageList& target_sample_); +} diff --git a/src/core/src/serialization/ecal_serialize_monitoring.cpp b/src/core/src/serialization/ecal_serialize_monitoring.cpp new file mode 100644 index 0000000..3578f26 --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_monitoring.cpp @@ -0,0 +1,922 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_monitoring.cpp + * @brief eCAL monitoring (de)serialization +**/ + +#include "nanopb/pb_encode.h" +#include "nanopb/pb_decode.h" +#include "nanopb/monitoring.pb.h" + +#include "ecal_serialize_common.h" +#include "ecal_serialize_monitoring.h" + +#include + +namespace +{ + ///////////////////////////////////////////////////////////////////////////////// + // Encode: eCAL::Monitoring::SProcessMon + ///////////////////////////////////////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Monitoring::SProcessMon& process_, eCAL_pb_Process& pb_process_) + { + /////////////////////////////////////////////// + // process information + /////////////////////////////////////////////// + // rclock + pb_process_.rclock = process_.rclock; + // hname + eCAL::nanopb::encode_string(pb_process_.hname, process_.hname); + // hgname + eCAL::nanopb::encode_string(pb_process_.hgname, process_.hgname); + // pid + pb_process_.pid = process_.pid; + // pname + eCAL::nanopb::encode_string(pb_process_.pname, process_.pname); + // uname + eCAL::nanopb::encode_string(pb_process_.uname, process_.uname); + // pparam + eCAL::nanopb::encode_string(pb_process_.pparam, process_.pparam); + // datawrite + pb_process_.datawrite = process_.datawrite; + // dataread + pb_process_.dataread = process_.dataread; + // state + pb_process_.has_state = true; + // state.severity + pb_process_.state.severity = static_cast(process_.state_severity); + // state.severity_level + pb_process_.state.severity_level = static_cast(process_.state_severity_level); + // state.info + eCAL::nanopb::encode_string(pb_process_.state.info, process_.state_info); + // process.tsync_state + pb_process_.tsync_state = static_cast(process_.tsync_state); + // tsync_mod_name + eCAL::nanopb::encode_string(pb_process_.tsync_mod_name, process_.tsync_mod_name); + // component_init_state + pb_process_.component_init_state = process_.component_init_state; + // component_init_info + eCAL::nanopb::encode_string(pb_process_.component_init_info, process_.component_init_info); + // ecal_runtime_version + eCAL::nanopb::encode_string(pb_process_.ecal_runtime_version, process_.ecal_runtime_version); + } + + bool encode_mon_message_processes_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* process_vec = (std::vector*)(*arg); + + for (const auto& process : *process_vec) + { + // encode process tag + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + // encode single process + eCAL_pb_Process pb_process = eCAL_pb_Process_init_default; + PrepareEncoding(process, pb_process); + + // encode submessage + if (!pb_encode_submessage(stream, eCAL_pb_Process_fields, &pb_process)) + { + return false; + } + } + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Encode: eCAL::Monitoring::STopicMon + ///////////////////////////////////////////////////////////////////////////////// + bool encode_mon_registration_layer_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* layer_vec = (std::vector*)(*arg); + + for (auto layer : *layer_vec) + { + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; + pb_layer.type = static_cast(layer.type); + pb_layer.version = layer.version; + pb_layer.confirmed = layer.confirmed; + + if (!pb_encode_submessage(stream, eCAL_pb_TLayer_fields, &pb_layer)) + { + return false; + } + } + + return true; + } + + void encode_mon_registration_layer(pb_callback_t& pb_callback, const std::vector& layer_vec) + { + pb_callback.funcs.encode = &encode_mon_registration_layer_field; + pb_callback.arg = (void*)(&layer_vec); + } + + void PrepareEncoding(const eCAL::Monitoring::STopicMon& topic_, eCAL_pb_Topic& pb_topic_) + { + // rclock + pb_topic_.rclock = topic_.rclock; + // hname + eCAL::nanopb::encode_string(pb_topic_.hname, topic_.hname); + // hgname + eCAL::nanopb::encode_string(pb_topic_.hgname, topic_.hgname); + // pid + pb_topic_.pid = topic_.pid; + // pname + eCAL::nanopb::encode_string(pb_topic_.pname, topic_.pname); + // uname + eCAL::nanopb::encode_string(pb_topic_.uname, topic_.uname); + // tid + eCAL::nanopb::encode_string(pb_topic_.tid, topic_.tid); + // tname + eCAL::nanopb::encode_string(pb_topic_.tname, topic_.tname); + // direction + eCAL::nanopb::encode_string(pb_topic_.direction, topic_.direction); + // tdatatype + pb_topic_.has_tdatatype = true; + // tdatatype.name + eCAL::nanopb::encode_string(pb_topic_.tdatatype.name, topic_.tdatatype.name); + // tdatatype.encoding + eCAL::nanopb::encode_string(pb_topic_.tdatatype.encoding, topic_.tdatatype.encoding); + // tdatatype.desc + eCAL::nanopb::encode_string(pb_topic_.tdatatype.desc, topic_.tdatatype.descriptor); + // tsize + pb_topic_.tsize = topic_.tsize; + // connections_loc + pb_topic_.connections_loc = topic_.connections_loc; + // connections_ext + pb_topic_.connections_ext = topic_.connections_ext; + // message_drops + pb_topic_.message_drops = topic_.message_drops; + // did + pb_topic_.did = topic_.did; + // dclock + pb_topic_.dclock = topic_.dclock; + // dfreq + pb_topic_.dfreq = topic_.dfreq; + // tlayer + encode_mon_registration_layer(pb_topic_.tlayer, topic_.tlayer); + // attr + eCAL::nanopb::encode_map(pb_topic_.attr, topic_.attr); + } + + bool encode_mon_message_topics_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* monitoring = (eCAL::Monitoring::SMonitoring*)(*arg); + + ////////////////// + // publisher + ////////////////// + for (const auto& topic : monitoring->publisher) + { + // encode topic tag + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + // encode single topic + eCAL_pb_Topic pb_topic = eCAL_pb_Topic_init_default; + PrepareEncoding(topic, pb_topic); + + // encode submessage + if (!pb_encode_submessage(stream, eCAL_pb_Topic_fields, &pb_topic)) + { + return false; + } + } + + ////////////////// + // subscriber + ////////////////// + for (const auto& topic : monitoring->subscriber) + { + // encode topic tag + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + // encode single topic + eCAL_pb_Topic pb_topic = eCAL_pb_Topic_init_default; + PrepareEncoding(topic, pb_topic); + + // encode submessage + if (!pb_encode_submessage(stream, eCAL_pb_Topic_fields, &pb_topic)) + { + return false; + } + } + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Encode: eCAL::Monitoring::SServerMon + ///////////////////////////////////////////////////////////////////////////////// + bool encode_mon_service_methods_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* method_vec = (std::vector*)(*arg); + + for (const auto& method : *method_vec) + { + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + eCAL_pb_Method pb_method = eCAL_pb_Method_init_default; + eCAL::nanopb::encode_string(pb_method.mname, method.mname); + eCAL::nanopb::encode_string(pb_method.req_type, method.req_type); + eCAL::nanopb::encode_string(pb_method.resp_type, method.resp_type); + eCAL::nanopb::encode_string(pb_method.req_desc, method.req_desc); + eCAL::nanopb::encode_string(pb_method.resp_desc, method.resp_desc); + pb_method.call_count = method.call_count; + + if (!pb_encode_submessage(stream, eCAL_pb_Method_fields, &pb_method)) + { + return false; + } + } + + return true; + } + + void encode_mon_service_methods(pb_callback_t& pb_callback, const std::vector& method_vec) + { + pb_callback.funcs.encode = &encode_mon_service_methods_field; + pb_callback.arg = (void*)(&method_vec); + } + + void PrepareEncoding(const eCAL::Monitoring::SServerMon& service_, eCAL_pb_Service& pb_service_) + { + /////////////////////////////////////////////// + // service information + /////////////////////////////////////////////// + // rclock + pb_service_.rclock = service_.rclock; + // hname + eCAL::nanopb::encode_string(pb_service_.hname, service_.hname); + // pname + eCAL::nanopb::encode_string(pb_service_.pname, service_.pname); + // uname + eCAL::nanopb::encode_string(pb_service_.uname, service_.uname); + // pid + pb_service_.pid = service_.pid; + // sname + eCAL::nanopb::encode_string(pb_service_.sname, service_.sname); + // sid + eCAL::nanopb::encode_string(pb_service_.sid, service_.sid); + // methods + encode_mon_service_methods(pb_service_.methods, service_.methods); + // version + pb_service_.version = service_.version; + // tcp_port_v0 + pb_service_.tcp_port_v0 = service_.tcp_port_v0; + // tcp_port_v1 + pb_service_.tcp_port_v1 = service_.tcp_port_v1; + } + + bool encode_mon_message_services_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* service_vec = (std::vector*)(*arg); + + for (const auto& service : *service_vec) + { + // encode process tag + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + // encode single service + eCAL_pb_Service pb_service = eCAL_pb_Service_init_default; + PrepareEncoding(service, pb_service); + + // encode submessage + if (!pb_encode_submessage(stream, eCAL_pb_Service_fields, &pb_service)) + { + return false; + } + } + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Encode: eCAL::Monitoring::SClientMon + ///////////////////////////////////////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Monitoring::SClientMon& client_, eCAL_pb_Client& pb_client_) + { + /////////////////////////////////////////////// + // client information + /////////////////////////////////////////////// + // rclock + pb_client_.rclock = client_.rclock; + // hname + eCAL::nanopb::encode_string(pb_client_.hname, client_.hname); + // pname + eCAL::nanopb::encode_string(pb_client_.pname, client_.pname); + // uname + eCAL::nanopb::encode_string(pb_client_.uname, client_.uname); + // pid + pb_client_.pid = client_.pid; + // sname + eCAL::nanopb::encode_string(pb_client_.sname, client_.sname); + // sid + eCAL::nanopb::encode_string(pb_client_.sid, client_.sid); + // version + pb_client_.version = client_.version; + } + + bool encode_mon_message_clients_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* client_vec = (std::vector*)(*arg); + + for (const auto& client : *client_vec) + { + // encode process tag + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + // encode single client + eCAL_pb_Client pb_client = eCAL_pb_Client_init_default; + PrepareEncoding(client, pb_client); + + // encode submessage + if (!pb_encode_submessage(stream, eCAL_pb_Client_fields, &pb_client)) + { + return false; + } + } + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Encode: eCAL::Monitoring::Monitoring + ///////////////////////////////////////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Monitoring::SMonitoring& mon_message_, eCAL_pb_Monitoring& pb_mon_message_) + { + /////////////////////////////////////////////// + // processes + /////////////////////////////////////////////// + pb_mon_message_.processes.funcs.encode = &encode_mon_message_processes_field; + pb_mon_message_.processes.arg = (void*)(&mon_message_.processes); + + /////////////////////////////////////////////// + // topics (publisher/subscriber) + /////////////////////////////////////////////// + pb_mon_message_.topics.funcs.encode = &encode_mon_message_topics_field; + pb_mon_message_.topics.arg = (void*)(&mon_message_); + + /////////////////////////////////////////////// + // services + /////////////////////////////////////////////// + pb_mon_message_.services.funcs.encode = &encode_mon_message_services_field; + pb_mon_message_.services.arg = (void*)(&mon_message_.server); + + /////////////////////////////////////////////// + // clients + /////////////////////////////////////////////// + pb_mon_message_.clients.funcs.encode = &encode_mon_message_clients_field; + pb_mon_message_.clients.arg = (void*)(&mon_message_.clients); + } + + size_t MonitoringStruct2PbMonitoring(const eCAL::Monitoring::SMonitoring& mon_message_, eCAL_pb_Monitoring& pb_mon_message_) + { + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + PrepareEncoding(mon_message_, pb_mon_message_); + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr }; + pb_encode(&pb_sizestream, eCAL_pb_Monitoring_fields, &pb_mon_message_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool MonitoringStruct2Buffer(const eCAL::Monitoring::SMonitoring& mon_message_, T& target_buffer_) + { + target_buffer_.clear(); + + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + eCAL_pb_Monitoring pb_mon_message = eCAL_pb_Monitoring_init_default; + size_t target_size = MonitoringStruct2PbMonitoring(mon_message_, pb_mon_message); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_Monitoring_fields, &pb_mon_message)) + { + std::cerr << "NanoPb eCAL::Monitoring::SMonitoring encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Decode: eCAL::Monitoring::SProcessMon + ///////////////////////////////////////////////////////////////////////////////// + void PrepareDecoding(eCAL_pb_Process& pb_process_, eCAL::Monitoring::SProcessMon& process_) + { + // initialize + pb_process_ = eCAL_pb_Process_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_process_.hname, process_.hname); + // hgname + eCAL::nanopb::decode_string(pb_process_.hgname, process_.hgname); + // pname + eCAL::nanopb::decode_string(pb_process_.pname, process_.pname); + // uname + eCAL::nanopb::decode_string(pb_process_.uname, process_.uname); + // pparam + eCAL::nanopb::decode_string(pb_process_.pparam, process_.pparam); + // state.info + eCAL::nanopb::decode_string(pb_process_.state.info, process_.state_info); + // tsync_mod_name + eCAL::nanopb::decode_string(pb_process_.tsync_mod_name, process_.tsync_mod_name); + // component_init_info + eCAL::nanopb::decode_string(pb_process_.component_init_info, process_.component_init_info); + // ecal_runtime_version + eCAL::nanopb::decode_string(pb_process_.ecal_runtime_version, process_.ecal_runtime_version); + } + + void AssignValues(const eCAL_pb_Process& pb_process_, eCAL::Monitoring::SProcessMon& process_) + { + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + // rclock + process_.rclock = pb_process_.rclock; + // pid + process_.pid = pb_process_.pid; + // datawrite + process_.datawrite = pb_process_.datawrite; + // dataread + process_.dataread = pb_process_.dataread; + // state.severity + process_.state_severity = pb_process_.state.severity; + // state.severity_level + process_.state_severity_level = pb_process_.state.severity_level; + // tsync_state + process_.tsync_state = pb_process_.tsync_state; + // component_init_state + process_.component_init_state = pb_process_.component_init_state; + } + + bool decode_processes_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Process pb_process = eCAL_pb_Process_init_default; + eCAL::Monitoring::SProcessMon process{}; + + // prepare sample for decoding + PrepareDecoding(pb_process, process); + + // decode it + if (!pb_decode(stream, eCAL_pb_Process_fields, &pb_process)) + { + return false; + } + + // apply sample values + AssignValues(pb_process, process); + + // add sample to vector + auto* processes_vec = (std::vector*)(*arg); + processes_vec->push_back(process); + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Decode: eCAL::Monitoring::STopicMon + ///////////////////////////////////////////////////////////////////////////////// + bool decode_mon_registration_layer_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; + eCAL::Monitoring::TLayer layer{}; + + if (!pb_decode(stream, eCAL_pb_TLayer_fields, &pb_layer)) + { + return false; + } + + // apply layer values + layer.type = static_cast(pb_layer.type); + layer.version = pb_layer.version; + layer.confirmed = pb_layer.confirmed; + + // add layer + auto tgt_vector = (std::vector*)(*arg); + tgt_vector->push_back(layer); + + return true; + } + + void decode_mon_registration_layer(pb_callback_t& pb_callback, std::vector& layer_vec) + { + pb_callback.funcs.decode = &decode_mon_registration_layer_field; + pb_callback.arg = &layer_vec; + } + + void PrepareDecoding(eCAL_pb_Topic& pb_topic_, eCAL::Monitoring::STopicMon& topic_) + { + // initialize + pb_topic_ = eCAL_pb_Topic_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_topic_.hname, topic_.hname); + // hgname + eCAL::nanopb::decode_string(pb_topic_.hgname, topic_.hgname); + // pname + eCAL::nanopb::decode_string(pb_topic_.pname, topic_.pname); + // uname + eCAL::nanopb::decode_string(pb_topic_.uname, topic_.uname); + // tid + eCAL::nanopb::decode_string(pb_topic_.tid, topic_.tid); + // tname + eCAL::nanopb::decode_string(pb_topic_.tname, topic_.tname); + // direction + eCAL::nanopb::decode_string(pb_topic_.direction, topic_.direction); + // tdatatype.name + eCAL::nanopb::decode_string(pb_topic_.tdatatype.name, topic_.tdatatype.name); + // tdatatype.encoding + eCAL::nanopb::decode_string(pb_topic_.tdatatype.encoding, topic_.tdatatype.encoding); + // tdatatype.desc + eCAL::nanopb::decode_string(pb_topic_.tdatatype.desc, topic_.tdatatype.descriptor); + // tlayer + decode_mon_registration_layer(pb_topic_.tlayer, topic_.tlayer); + // attr + eCAL::nanopb::decode_map(pb_topic_.attr, topic_.attr); + } + + void AssignValues(const eCAL_pb_Topic& pb_topic_, eCAL::Monitoring::STopicMon& topic_) + { + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + // rclock + topic_.rclock = pb_topic_.rclock; + // pid + topic_.pid = pb_topic_.pid; + // tsize + topic_.tsize = pb_topic_.tsize; + // connections_loc + topic_.connections_loc = pb_topic_.connections_loc; + // connections_ext + topic_.connections_ext = pb_topic_.connections_ext; + // message_drops + topic_.message_drops = pb_topic_.message_drops; + // did + topic_.did = pb_topic_.did; + // dclock + topic_.dclock = pb_topic_.dclock; + // dfreq + topic_.dfreq = pb_topic_.dfreq; + } + + bool decode_topics_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Topic pb_topic = eCAL_pb_Topic_init_default; + eCAL::Monitoring::STopicMon topic{}; + + // prepare sample for decoding + PrepareDecoding(pb_topic, topic); + + // decode it + if (!pb_decode(stream, eCAL_pb_Topic_fields, &pb_topic)) + { + return false; + } + + // apply sample values + AssignValues(pb_topic, topic); + + // add sample to vector + auto* monitoring = (eCAL::Monitoring::SMonitoring*)(*arg); + if (topic.direction == "publisher") + { + monitoring->publisher.push_back(topic); + } + if (topic.direction == "subscriber") + { + monitoring->subscriber.push_back(topic); + } + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Decode: eCAL::Monitoring::SServerMon + ///////////////////////////////////////////////////////////////////////////////// + bool decode_mon_service_methods_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Method pb_method = eCAL_pb_Method_init_default; + eCAL::Monitoring::SMethodMon method{}; + + // decode method parameter + eCAL::nanopb::decode_string(pb_method.mname, method.mname); + eCAL::nanopb::decode_string(pb_method.req_type, method.req_type); + eCAL::nanopb::decode_string(pb_method.req_desc, method.req_desc); + eCAL::nanopb::decode_string(pb_method.resp_type, method.resp_type); + eCAL::nanopb::decode_string(pb_method.resp_desc, method.resp_desc); + + // decode it + if (!pb_decode(stream, eCAL_pb_Method_fields, &pb_method)) + { + return false; + } + + // apply method values + method.call_count = pb_method.call_count; + + // add method to vector + auto* method_vec = (std::vector*)(*arg); + method_vec->emplace_back(method); + + return true; + } + + void decode_mon_service_methods(pb_callback_t& pb_callback, std::vector& method_vec) + { + pb_callback.funcs.decode = &decode_mon_service_methods_field; + pb_callback.arg = &method_vec; + } + + void PrepareDecoding(eCAL_pb_Service& pb_service_, eCAL::Monitoring::SServerMon& service_) + { + // initialize + pb_service_ = eCAL_pb_Service_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_service_.hname, service_.hname); + // pname + eCAL::nanopb::decode_string(pb_service_.pname, service_.pname); + // uname + eCAL::nanopb::decode_string(pb_service_.uname, service_.uname); + // sname + eCAL::nanopb::decode_string(pb_service_.sname, service_.sname); + // sid + eCAL::nanopb::decode_string(pb_service_.sid, service_.sid); + // methods + decode_mon_service_methods(pb_service_.methods, service_.methods); + } + + void AssignValues(const eCAL_pb_Service& pb_service_, eCAL::Monitoring::SServerMon& service_) + { + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + // rclock + service_.rclock = pb_service_.rclock; + // pid + service_.pid = pb_service_.pid; + // version + service_.version = pb_service_.version; + // tcp_port_v0 + service_.tcp_port_v0 = pb_service_.tcp_port_v0; + // tcp_port_v1 + service_.tcp_port_v1 = pb_service_.tcp_port_v1; + } + + bool decode_services_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Service pb_service = eCAL_pb_Service_init_default; + eCAL::Monitoring::SServerMon service{}; + + // prepare sample for decoding + PrepareDecoding(pb_service, service); + + // decode it + if (!pb_decode(stream, eCAL_pb_Service_fields, &pb_service)) + { + return false; + } + + // apply sample values + AssignValues(pb_service, service); + + // add sample to vector + auto* services_vec = (std::vector*)(*arg); + services_vec->push_back(service); + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Decode: eCAL::Monitoring::SClientMon + ///////////////////////////////////////////////////////////////////////////////// + void PrepareDecoding(eCAL_pb_Client& pb_client_, eCAL::Monitoring::SClientMon& client_) + { + // initialize + pb_client_ = eCAL_pb_Client_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_client_.hname, client_.hname); + // pname + eCAL::nanopb::decode_string(pb_client_.pname, client_.pname); + // uname + eCAL::nanopb::decode_string(pb_client_.uname, client_.uname); + // sname + eCAL::nanopb::decode_string(pb_client_.sname, client_.sname); + // sid + eCAL::nanopb::decode_string(pb_client_.sid, client_.sid); + } + + void AssignValues(const eCAL_pb_Client& pb_client_, eCAL::Monitoring::SClientMon& client_) + { + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + // rclock + client_.rclock = pb_client_.rclock; + // pid + client_.pid = pb_client_.pid; + // version + client_.version = pb_client_.version; + } + + bool decode_clients_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Client pb_client = eCAL_pb_Client_init_default; + eCAL::Monitoring::SClientMon client{}; + + // prepare sample for decoding + PrepareDecoding(pb_client, client); + + // decode it + if (!pb_decode(stream, eCAL_pb_Client_fields, &pb_client)) + { + return false; + } + + // apply sample values + AssignValues(pb_client, client); + + // add sample to vector + auto* client_vec = (std::vector*)(*arg); + client_vec->push_back(client); + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // Decode: eCAL::Monitoring::Monitoring + ///////////////////////////////////////////////////////////////////////////////// + bool Buffer2MonitoringStruct(const char* data_, size_t size_, eCAL::Monitoring::SMonitoring& mon_message_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_Monitoring pb_mon_message = eCAL_pb_Monitoring_init_default; + + /////////////////////////////////////////////// + // prepare processes for decoding + /////////////////////////////////////////////// + pb_mon_message.processes.funcs.decode = &decode_processes_field; + pb_mon_message.processes.arg = &mon_message_.processes; + + /////////////////////////////////////////////// + // prepare topics for decoding + /////////////////////////////////////////////// + pb_mon_message.topics.funcs.decode = &decode_topics_field; + pb_mon_message.topics.arg = &mon_message_; + + /////////////////////////////////////////////// + // prepare services for decoding + /////////////////////////////////////////////// + pb_mon_message.services.funcs.decode = &decode_services_field; + pb_mon_message.services.arg = &mon_message_.server; + + /////////////////////////////////////////////// + // prepare clients for decoding + /////////////////////////////////////////////// + pb_mon_message.clients.funcs.decode = &decode_clients_field; + pb_mon_message.clients.arg = &mon_message_.clients; + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_Monitoring_fields, &pb_mon_message)) + { + std::cerr << "NanoPb eCAL::Monitoring::SMonitoring decode failed: " << pb_istream.errmsg << std::endl; + } + + return true; + } +} + +namespace eCAL +{ + // monitoring - serialize/deserialize + bool SerializeToBuffer(const Monitoring::SMonitoring& source_sample_, std::vector& target_buffer_) + { + return MonitoringStruct2Buffer(source_sample_, target_buffer_); + } + + bool SerializeToBuffer(const Monitoring::SMonitoring& source_sample_, std::string& target_buffer_) + { + return MonitoringStruct2Buffer(source_sample_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Monitoring::SMonitoring& target_sample_) + { + return Buffer2MonitoringStruct(data_, size_, target_sample_); + } +} diff --git a/ecal/core/src/io/snd_raw_buffer.h b/src/core/src/serialization/ecal_serialize_monitoring.h similarity index 60% rename from ecal/core/src/io/snd_raw_buffer.h rename to src/core/src/serialization/ecal_serialize_monitoring.h index bfcdf16..937238b 100644 --- a/ecal/core/src/io/snd_raw_buffer.h +++ b/src/core/src/serialization/ecal_serialize_monitoring.h @@ -18,24 +18,21 @@ */ /** - * @brief raw message buffer handling + * @file ecal_serialize_monitoring.h + * @brief eCAL monitoring serialization / deserialization **/ -#include -#include +#pragma once + +#include -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif +#include +#include namespace eCAL { - size_t CreateSampleBuffer(const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, std::vector& payload_); - - typedef std::function TransmitCallbackT; - size_t SendSampleBuffer(char* buf_, size_t buf_len_, long bandwidth_, TransmitCallbackT transmit_cb_); + // monitoring - serialize/deserialize + bool SerializeToBuffer (const Monitoring::SMonitoring& source_sample_, std::vector& target_buffer_); + bool SerializeToBuffer (const Monitoring::SMonitoring& source_sample_, std::string& target_buffer_); + bool DeserializeFromBuffer (const char* data_, size_t size_, Monitoring::SMonitoring& target_sample_); } diff --git a/src/core/src/serialization/ecal_serialize_sample_payload.cpp b/src/core/src/serialization/ecal_serialize_sample_payload.cpp new file mode 100644 index 0000000..ad003e6 --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_sample_payload.cpp @@ -0,0 +1,197 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_sample_payload.cpp + * @brief eCAL payload sample (de)serialization +**/ + +#include "nanopb/pb_encode.h" +#include "nanopb/pb_decode.h" +#include "nanopb/ecal.pb.h" + +#include "ecal_serialize_common.h" +#include "ecal_struct_sample_payload.h" + +#include + +namespace +{ + void CreatePayloadStruct(const eCAL::Payload::Sample& payload_, eCAL::nanopb::SNanoBytes& nano_bytes_) + { + // extract payload + // payload may be stored as std::vector or raw pointer + size + const char* payload_addr = nullptr; + size_t payload_size = 0; + switch (payload_.content.payload.type) + { + case eCAL::Payload::pl_raw: + payload_addr = payload_.content.payload.raw_addr; + payload_size = payload_.content.payload.raw_size; + break; + case eCAL::Payload::pl_vec: + payload_addr = payload_.content.payload.vec.data(); + payload_size = payload_.content.payload.vec.size(); + break; + default: + break; + } + + // topic content payload + if ((payload_addr != nullptr) && (payload_size > 0)) + { + nano_bytes_.content = (pb_byte_t*)(payload_addr); + nano_bytes_.length = payload_size; + } + } + + // TODO: The size must be a multiple of 8. + size_t PayloadStruct2PbSample(const eCAL::Payload::Sample& payload_, const eCAL::nanopb::SNanoBytes& nano_bytes_, eCAL_pb_Sample& pb_sample_) + { + // command type + pb_sample_.cmd_type = static_cast(payload_.cmd_type); + + // topic information + pb_sample_.has_topic = true; + // hname + eCAL::nanopb::encode_string(pb_sample_.topic.hname, payload_.topic.hname); + // tid + eCAL::nanopb::encode_string(pb_sample_.topic.tid, payload_.topic.tid); + // tname + eCAL::nanopb::encode_string(pb_sample_.topic.tname, payload_.topic.tname); + + // topic content + pb_sample_.has_content = true; + pb_sample_.content.id = payload_.content.id; + pb_sample_.content.clock = payload_.content.clock; + pb_sample_.content.time = payload_.content.time; + pb_sample_.content.hash = payload_.content.hash; + pb_sample_.content.size = payload_.content.size; + + // topic content payload + eCAL::nanopb::encode_bytes(pb_sample_.content.payload, nano_bytes_); + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr}; + pb_encode(&pb_sizestream, eCAL_pb_Sample_fields, &pb_sample_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool PayloadStruct2Buffer(const eCAL::Payload::Sample& payload_, T& target_buffer_) + { + target_buffer_.clear(); + + // create payload helper struct + eCAL::nanopb::SNanoBytes nano_bytes; + CreatePayloadStruct(payload_, nano_bytes); + + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; + size_t target_size = PayloadStruct2PbSample(payload_, nano_bytes, pb_sample); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_Sample_fields, &pb_sample)) + { + std::cerr << "NanoPb eCAL::Payload::Sample encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + bool Buffer2PayloadStruct(const char* data_, size_t size_, eCAL::Payload::Sample& payload_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_sample.topic.hname, payload_.topic.hname); + // tid + eCAL::nanopb::decode_string(pb_sample.topic.tid, payload_.topic.tid); + // tname + eCAL::nanopb::decode_string(pb_sample.topic.tname, payload_.topic.tname); + // topic content payload + payload_.content.payload.type = eCAL::Payload::pl_vec; + eCAL::nanopb::decode_bytes(pb_sample.content.payload, payload_.content.payload.vec); + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_Sample_fields, &pb_sample)) + { + std::cerr << "NanoPb eCAL::Payload::Sample decode failed: " << pb_istream.errmsg << std::endl; + } + + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + // command type + payload_.cmd_type = static_cast(pb_sample.cmd_type); + + // topic content + payload_.content.id = pb_sample.content.id; + payload_.content.clock = pb_sample.content.clock; + payload_.content.time = pb_sample.content.time; + payload_.content.hash = pb_sample.content.hash; + payload_.content.size = pb_sample.content.size; + + return true; + } +} + +namespace eCAL +{ + bool SerializeToBuffer(const Payload::Sample& source_sample_, std::vector& target_buffer_) + { + return PayloadStruct2Buffer(source_sample_, target_buffer_); + } + + bool SerializeToBuffer(const Payload::Sample& source_sample_, std::string& target_buffer_) + { + return PayloadStruct2Buffer(source_sample_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Payload::Sample& target_sample_) + { + return Buffer2PayloadStruct(data_, size_, target_sample_); + } +} diff --git a/ecal/core/src/io/snd_sample.h b/src/core/src/serialization/ecal_serialize_sample_payload.h similarity index 60% rename from ecal/core/src/io/snd_sample.h rename to src/core/src/serialization/ecal_serialize_sample_payload.h index 8020e91..e39a990 100644 --- a/ecal/core/src/io/snd_sample.h +++ b/src/core/src/serialization/ecal_serialize_sample_payload.h @@ -18,22 +18,21 @@ */ /** - * @brief Sender thread for ecal samples + * @file ecal_serialize_sample_payload.h + * @brief eCAL sample payload serialization / deserialization **/ #pragma once -#include "udp_sender.h" +#include "ecal_struct_sample_payload.h" -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif +#include +#include namespace eCAL { - size_t SendSample(eCAL::CUDPSender* udp_sender_, const std::string& sample_name_, const eCAL::pb::Sample& ecal_sample_, const std::string& ipaddr_, long bandwidth_); + // payload sample - serialize/deserialize + bool SerializeToBuffer (const Payload::Sample& source_sample_, std::vector& target_buffer_); + bool SerializeToBuffer (const Payload::Sample& source_sample_, std::string& target_buffer_); + bool DeserializeFromBuffer (const char* data_, size_t size_, Payload::Sample& target_sample_); } diff --git a/src/core/src/serialization/ecal_serialize_sample_registration.cpp b/src/core/src/serialization/ecal_serialize_sample_registration.cpp new file mode 100644 index 0000000..def7596 --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_sample_registration.cpp @@ -0,0 +1,614 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_sample_registration.cpp + * @brief eCAL registration sample (de)serialization +**/ + +#include "nanopb/pb_encode.h" +#include "nanopb/pb_decode.h" +#include "nanopb/ecal.pb.h" + +#include "ecal_serialize_common.h" +#include "ecal_serialize_sample_registration.h" + +#include + +namespace +{ + ///////////////////////////////////////////////////////////////////////////////// + // eCAL::Registration::Sample + ///////////////////////////////////////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Registration::Sample& registration_, eCAL_pb_Sample& pb_sample_) + { + // command type + pb_sample_.cmd_type = static_cast(registration_.cmd_type); + + /////////////////////////////////////////////// + // host information + /////////////////////////////////////////////// + pb_sample_.has_host = true; + + // hname + eCAL::nanopb::encode_string(pb_sample_.host.hname, registration_.host.hname); + + /////////////////////////////////////////////// + // process information + /////////////////////////////////////////////// + pb_sample_.has_process = true; + + // rclock + pb_sample_.process.rclock = registration_.process.rclock; + // hname + eCAL::nanopb::encode_string(pb_sample_.process.hname, registration_.process.hname); + // hgname + eCAL::nanopb::encode_string(pb_sample_.process.hgname, registration_.process.hgname); + // pid + pb_sample_.process.pid = registration_.process.pid; + // pname + eCAL::nanopb::encode_string(pb_sample_.process.pname, registration_.process.pname); + // uname + eCAL::nanopb::encode_string(pb_sample_.process.uname, registration_.process.uname); + // pparam + eCAL::nanopb::encode_string(pb_sample_.process.pparam, registration_.process.pparam); + // datawrite + pb_sample_.process.datawrite = registration_.process.datawrite; + // dataread + pb_sample_.process.dataread = registration_.process.dataread; + // state.severity + pb_sample_.process.state.severity = static_cast(registration_.process.state.severity); + // state.severity_level + pb_sample_.process.state.severity_level = static_cast(registration_.process.state.severity_level); + // state.info + eCAL::nanopb::encode_string(pb_sample_.process.state.info, registration_.process.state.info); + // process.tsync_state + pb_sample_.process.tsync_state = static_cast(registration_.process.tsync_state); + // tsync_mod_name + eCAL::nanopb::encode_string(pb_sample_.process.tsync_mod_name, registration_.process.tsync_mod_name); + // component_init_state + pb_sample_.process.component_init_state = registration_.process.component_init_state; + // component_init_info + eCAL::nanopb::encode_string(pb_sample_.process.component_init_info, registration_.process.component_init_info); + // ecal_runtime_version + eCAL::nanopb::encode_string(pb_sample_.process.ecal_runtime_version, registration_.process.ecal_runtime_version); + + /////////////////////////////////////////////// + // service information + /////////////////////////////////////////////// + pb_sample_.has_service = true; + + // rclock + pb_sample_.service.rclock = registration_.service.rclock; + // hname + eCAL::nanopb::encode_string(pb_sample_.service.hname, registration_.service.hname); + // pname + eCAL::nanopb::encode_string(pb_sample_.service.pname, registration_.service.pname); + // uname + eCAL::nanopb::encode_string(pb_sample_.service.uname, registration_.service.uname); + // pid + pb_sample_.service.pid = registration_.service.pid; + // sname + eCAL::nanopb::encode_string(pb_sample_.service.sname, registration_.service.sname); + // sid + eCAL::nanopb::encode_string(pb_sample_.service.sid, registration_.service.sid); + // methods + eCAL::nanopb::encode_service_methods(pb_sample_.service.methods, registration_.service.methods); + // version + pb_sample_.service.version = registration_.service.version; + // tcp_port_v0 + pb_sample_.service.tcp_port_v0 = registration_.service.tcp_port_v0; + // tcp_port_v1 + pb_sample_.service.tcp_port_v1 = registration_.service.tcp_port_v1; + + /////////////////////////////////////////////// + // client information + /////////////////////////////////////////////// + pb_sample_.has_client = true; + + // rclock + pb_sample_.client.rclock = registration_.client.rclock; + // hname + eCAL::nanopb::encode_string(pb_sample_.client.hname, registration_.client.hname); + // pname + eCAL::nanopb::encode_string(pb_sample_.client.pname, registration_.client.pname); + // uname + eCAL::nanopb::encode_string(pb_sample_.client.uname, registration_.client.uname); + // pid + pb_sample_.client.pid = registration_.client.pid; + // sname + eCAL::nanopb::encode_string(pb_sample_.client.sname, registration_.client.sname); + // sid + eCAL::nanopb::encode_string(pb_sample_.client.sid, registration_.client.sid); + // version + pb_sample_.client.version = registration_.client.version; + + /////////////////////////////////////////////// + // topic information + /////////////////////////////////////////////// + pb_sample_.has_topic = true; + + // rclock + pb_sample_.topic.rclock = registration_.topic.rclock; + // hname + eCAL::nanopb::encode_string(pb_sample_.topic.hname, registration_.topic.hname); + // hgname + eCAL::nanopb::encode_string(pb_sample_.topic.hgname, registration_.topic.hgname); + // pid + pb_sample_.topic.pid = registration_.topic.pid; + // pname + eCAL::nanopb::encode_string(pb_sample_.topic.pname, registration_.topic.pname); + // uname + eCAL::nanopb::encode_string(pb_sample_.topic.uname, registration_.topic.uname); + // tid + eCAL::nanopb::encode_string(pb_sample_.topic.tid, registration_.topic.tid); + // tname + eCAL::nanopb::encode_string(pb_sample_.topic.tname, registration_.topic.tname); + // direction + eCAL::nanopb::encode_string(pb_sample_.topic.direction, registration_.topic.direction); + // ttype + eCAL::nanopb::encode_string(pb_sample_.topic.ttype, registration_.topic.ttype); + // tdesc + eCAL::nanopb::encode_string(pb_sample_.topic.tdesc, registration_.topic.tdesc); + // tdatatype + pb_sample_.topic.has_tdatatype = true; + // tdatatype.name + eCAL::nanopb::encode_string(pb_sample_.topic.tdatatype.name, registration_.topic.tdatatype.name); + // tdatatype.encoding + eCAL::nanopb::encode_string(pb_sample_.topic.tdatatype.encoding, registration_.topic.tdatatype.encoding); + // tdatatype.desc + eCAL::nanopb::encode_string(pb_sample_.topic.tdatatype.desc, registration_.topic.tdatatype.desc); + // tsize + pb_sample_.topic.tsize = registration_.topic.tsize; + // connections_loc + pb_sample_.topic.connections_loc = registration_.topic.connections_loc; + // connections_ext + pb_sample_.topic.connections_ext = registration_.topic.connections_ext; + // message_drops + pb_sample_.topic.message_drops = registration_.topic.message_drops; + // did + pb_sample_.topic.did = registration_.topic.did; + // dclock + pb_sample_.topic.dclock = registration_.topic.dclock; + // dfreq + pb_sample_.topic.dfreq = registration_.topic.dfreq; + // tlayer + eCAL::nanopb::encode_registration_layer(pb_sample_.topic.tlayer, registration_.topic.tlayer); + // attr + eCAL::nanopb::encode_map(pb_sample_.topic.attr, registration_.topic.attr); + } + + size_t RegistrationStruct2PbSample(const eCAL::Registration::Sample& registration_, eCAL_pb_Sample& pb_sample_) + { + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + PrepareEncoding(registration_, pb_sample_); + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr}; + pb_encode(&pb_sizestream, eCAL_pb_Sample_fields, &pb_sample_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool RegistrationStruct2Buffer(const eCAL::Registration::Sample& registration_, T& target_buffer_) + { + target_buffer_.clear(); + + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; + size_t target_size = RegistrationStruct2PbSample(registration_, pb_sample); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_Sample_fields, &pb_sample)) + { + std::cerr << "NanoPb eCAL::Registration::Sample encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + void PrepareDecoding(eCAL_pb_Sample& pb_sample_, eCAL::Registration::Sample& registration_) + { + // initialize + pb_sample_ = eCAL_pb_Sample_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + + /////////////////////////////////////////////// + // host information + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_sample_.host.hname, registration_.host.hname); + + /////////////////////////////////////////////// + // process information + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_sample_.process.hname, registration_.process.hname); + // hgname + eCAL::nanopb::decode_string(pb_sample_.process.hgname, registration_.process.hgname); + // pname + eCAL::nanopb::decode_string(pb_sample_.process.pname, registration_.process.pname); + // uname + eCAL::nanopb::decode_string(pb_sample_.process.uname, registration_.process.uname); + // pparam + eCAL::nanopb::decode_string(pb_sample_.process.pparam, registration_.process.pparam); + // state.info + eCAL::nanopb::decode_string(pb_sample_.process.state.info, registration_.process.state.info); + // tsync_mod_name + eCAL::nanopb::decode_string(pb_sample_.process.tsync_mod_name, registration_.process.tsync_mod_name); + // component_init_info + eCAL::nanopb::decode_string(pb_sample_.process.component_init_info, registration_.process.component_init_info); + // ecal_runtime_version + eCAL::nanopb::decode_string(pb_sample_.process.ecal_runtime_version, registration_.process.ecal_runtime_version); + + /////////////////////////////////////////////// + // service information + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_sample_.service.hname, registration_.service.hname); + // pname + eCAL::nanopb::decode_string(pb_sample_.service.pname, registration_.service.pname); + // uname + eCAL::nanopb::decode_string(pb_sample_.service.uname, registration_.service.uname); + // sname + eCAL::nanopb::decode_string(pb_sample_.service.sname, registration_.service.sname); + // sid + eCAL::nanopb::decode_string(pb_sample_.service.sid, registration_.service.sid); + // methods + eCAL::nanopb::decode_service_methods(pb_sample_.service.methods, registration_.service.methods); + + /////////////////////////////////////////////// + // client information + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_sample_.client.hname, registration_.client.hname); + // pname + eCAL::nanopb::decode_string(pb_sample_.client.pname, registration_.client.pname); + // uname + eCAL::nanopb::decode_string(pb_sample_.client.uname, registration_.client.uname); + // sname + eCAL::nanopb::decode_string(pb_sample_.client.sname, registration_.client.sname); + // sid + eCAL::nanopb::decode_string(pb_sample_.client.sid, registration_.client.sid); + + /////////////////////////////////////////////// + // topic information + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_sample_.topic.hname, registration_.topic.hname); + // hgname + eCAL::nanopb::decode_string(pb_sample_.topic.hgname, registration_.topic.hgname); + // pname + eCAL::nanopb::decode_string(pb_sample_.topic.pname, registration_.topic.pname); + // uname + eCAL::nanopb::decode_string(pb_sample_.topic.uname, registration_.topic.uname); + // tid + eCAL::nanopb::decode_string(pb_sample_.topic.tid, registration_.topic.tid); + // tname + eCAL::nanopb::decode_string(pb_sample_.topic.tname, registration_.topic.tname); + // direction + eCAL::nanopb::decode_string(pb_sample_.topic.direction, registration_.topic.direction); + // ttype + eCAL::nanopb::decode_string(pb_sample_.topic.ttype, registration_.topic.ttype); + // tdesc + eCAL::nanopb::decode_string(pb_sample_.topic.tdesc, registration_.topic.tdesc); + // tdatatype.name + eCAL::nanopb::decode_string(pb_sample_.topic.tdatatype.name, registration_.topic.tdatatype.name); + // tdatatype.encoding + eCAL::nanopb::decode_string(pb_sample_.topic.tdatatype.encoding, registration_.topic.tdatatype.encoding); + // tdatatype.desc + eCAL::nanopb::decode_string(pb_sample_.topic.tdatatype.desc, registration_.topic.tdatatype.desc); + // tlayer + eCAL::nanopb::decode_registration_layer(pb_sample_.topic.tlayer, registration_.topic.tlayer); + // attr + eCAL::nanopb::decode_map(pb_sample_.topic.attr, registration_.topic.attr); + } + + void AssignValues(const eCAL_pb_Sample& pb_sample_, eCAL::Registration::Sample& registration_) + { + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + // command type + registration_.cmd_type = static_cast(pb_sample_.cmd_type); + + /////////////////////////////////////////////// + // process information + /////////////////////////////////////////////// + // rclock + registration_.process.rclock = pb_sample_.process.rclock; + // pid + registration_.process.pid = pb_sample_.process.pid; + // datawrite + registration_.process.datawrite = pb_sample_.process.datawrite; + // dataread + registration_.process.dataread = pb_sample_.process.dataread; + // state.severity + registration_.process.state.severity = static_cast(pb_sample_.process.state.severity); + // state.severity_level + registration_.process.state.severity_level = static_cast(pb_sample_.process.state.severity_level); + // tsync_state + registration_.process.tsync_state = static_cast(pb_sample_.process.tsync_state); + // component_init_state + registration_.process.component_init_state = pb_sample_.process.component_init_state; + + /////////////////////////////////////////////// + // service information + /////////////////////////////////////////////// + // rclock + registration_.service.rclock = pb_sample_.service.rclock; + // pid + registration_.service.pid = pb_sample_.service.pid; + // version + registration_.service.version = pb_sample_.service.version; + // tcp_port_v0 + registration_.service.tcp_port_v0 = pb_sample_.service.tcp_port_v0; + // tcp_port_v1 + registration_.service.tcp_port_v1 = pb_sample_.service.tcp_port_v1; + + /////////////////////////////////////////////// + // client information + /////////////////////////////////////////////// + // rclock + registration_.client.rclock = pb_sample_.client.rclock; + // pid + registration_.client.pid = pb_sample_.client.pid; + // version + registration_.client.version = pb_sample_.client.version; + + /////////////////////////////////////////////// + // topic information + /////////////////////////////////////////////// + // rclock + registration_.topic.rclock = pb_sample_.topic.rclock; + // pid + registration_.topic.pid = pb_sample_.topic.pid; + // tsize + registration_.topic.tsize = pb_sample_.topic.tsize; + // connections_loc + registration_.topic.connections_loc = pb_sample_.topic.connections_loc; + // connections_ext + registration_.topic.connections_ext = pb_sample_.topic.connections_ext; + // message_drops + registration_.topic.message_drops = pb_sample_.topic.message_drops; + // did + registration_.topic.did = pb_sample_.topic.did; + // dclock + registration_.topic.dclock = pb_sample_.topic.dclock; + // dfreq + registration_.topic.dfreq = pb_sample_.topic.dfreq; + } + + bool Buffer2RegistrationStruct(const char* data_, size_t size_, eCAL::Registration::Sample& registration_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; + + /////////////////////////////////////////////// + // prepare sample for decoding + /////////////////////////////////////////////// + PrepareDecoding(pb_sample, registration_); + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_Sample_fields, &pb_sample)) + { + std::cerr << "NanoPb eCAL::Registration::Sample decode failed: " << pb_istream.errmsg << std::endl; + } + + /////////////////////////////////////////////// + // assign sample values + /////////////////////////////////////////////// + AssignValues(pb_sample, registration_); + + return true; + } + + ///////////////////////////////////////////////////////////////////////////////// + // eCAL::Registration::SampleList + ///////////////////////////////////////////////////////////////////////////////// + bool encode_sample_list_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* sample_list = (std::list*)(*arg); + + for (const auto& sample : *sample_list) + { + // encode sample tag + if (!pb_encode_tag_for_field(stream, field)) + { + return false; + } + + // encode single sample + eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; + PrepareEncoding(sample, pb_sample); + + // encode submessage + if (!pb_encode_submessage(stream, eCAL_pb_Sample_fields, &pb_sample)) + { + return false; + } + } + + return true; + } + + size_t RegistrationListStruct2PbSample(const eCAL::Registration::SampleList& registration_list_, eCAL_pb_SampleList& pb_sample_list_) + { + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + pb_sample_list_.samples.funcs.encode = &encode_sample_list_field; + pb_sample_list_.samples.arg = (void*)(®istration_list_.samples); + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr}; + pb_encode(&pb_sizestream, eCAL_pb_SampleList_fields, &pb_sample_list_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool RegistrationListStruct2Buffer(const eCAL::Registration::SampleList& registration_list_, T& target_buffer_) + { + target_buffer_.clear(); + + /////////////////////////////////////////////// + // prepare sample for encoding + /////////////////////////////////////////////// + eCAL_pb_SampleList pb_sample_list = eCAL_pb_SampleList_init_default; + size_t target_size = RegistrationListStruct2PbSample(registration_list_, pb_sample_list); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_SampleList_fields, &pb_sample_list)) + { + std::cerr << "NanoPb eCAL::Registration::SampleList encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + bool decode_sample_list_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + { + if (arg == nullptr) return false; + if (*arg == nullptr) return false; + + eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; + eCAL::Registration::Sample sample{}; + + // prepare sample for decoding + PrepareDecoding(pb_sample, sample); + + // decode it + if (!pb_decode(stream, eCAL_pb_Sample_fields, &pb_sample)) + { + return false; + } + + // apply sample values + AssignValues(pb_sample, sample); + + // add sample to list + auto* sample_list = (std::list*)(*arg); + sample_list->push_back(sample); + + return true; + } + + bool Buffer2RegistrationListStruct(const char* data_, size_t size_, eCAL::Registration::SampleList& registration_list_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_SampleList pb_sample_list = eCAL_pb_SampleList_init_default; + + /////////////////////////////////////////////// + // prepare sample for decoding + /////////////////////////////////////////////// + pb_sample_list.samples.funcs.decode = &decode_sample_list_field; + pb_sample_list.samples.arg = ®istration_list_.samples; + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_SampleList_fields, &pb_sample_list)) + { + std::cerr << "NanoPb eCAL::Registration::Sample decode failed: " << pb_istream.errmsg << std::endl; + } + + return true; + } +} + +namespace eCAL +{ + bool SerializeToBuffer(const Registration::Sample& source_sample_, std::vector& target_buffer_) + { + return RegistrationStruct2Buffer(source_sample_, target_buffer_); + } + + bool SerializeToBuffer(const Registration::Sample& source_sample_, std::string& target_buffer_) + { + return RegistrationStruct2Buffer(source_sample_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Registration::Sample& target_sample_) + { + return Buffer2RegistrationStruct(data_, size_, target_sample_); + } + + bool SerializeToBuffer(const Registration::SampleList& source_sample_list_, std::vector& target_buffer_) + { + return RegistrationListStruct2Buffer(source_sample_list_, target_buffer_); + } + + bool SerializeToBuffer(const Registration::SampleList& source_sample_list_, std::string& target_buffer_) + { + return RegistrationListStruct2Buffer(source_sample_list_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Registration::SampleList& target_sample_list_) + { + return Buffer2RegistrationListStruct(data_, size_, target_sample_list_); + } +} diff --git a/src/core/src/serialization/ecal_serialize_sample_registration.h b/src/core/src/serialization/ecal_serialize_sample_registration.h new file mode 100644 index 0000000..86c65ae --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_sample_registration.h @@ -0,0 +1,43 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_sample_registration.h + * @brief eCAL sample registration serialization / deserialization +**/ + +#pragma once + +#include "ecal_struct_sample_registration.h" + +#include +#include + +namespace eCAL +{ + // registration sample - serialize/deserialize + bool SerializeToBuffer (const Registration::Sample& registration_sample_, std::vector& target_buffer_); + bool SerializeToBuffer (const Registration::Sample& source_sample_, std::string& target_buffer_); + bool DeserializeFromBuffer (const char* data_, size_t size_, Registration::Sample& target_sample_); + + // registration sample list - serialize/deserialize + bool SerializeToBuffer (const Registration::SampleList& registration_sample_, std::vector& target_buffer_); + bool SerializeToBuffer (const Registration::SampleList& source_sample_list_, std::string& target_buffer_); + bool DeserializeFromBuffer (const char* data_, size_t size_, Registration::SampleList& target_sample_); +} diff --git a/src/core/src/serialization/ecal_serialize_service.cpp b/src/core/src/serialization/ecal_serialize_service.cpp new file mode 100644 index 0000000..c73947f --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_service.cpp @@ -0,0 +1,326 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_service.cpp + * @brief eCAL service (de)serialization +**/ + +#include "nanopb/pb_encode.h" +#include "nanopb/pb_decode.h" +#include "nanopb/ecal.pb.h" + +#include "ecal_serialize_common.h" +#include "ecal_serialize_service.h" + +#include + +namespace +{ + size_t RequestStruct2PbRequest(const eCAL::Service::Request& request_, eCAL_pb_Request& pb_request_) + { + pb_request_ = eCAL_pb_Request_init_default; + + /////////////////////////////////////////////// + // header information + /////////////////////////////////////////////// + pb_request_.has_header = true; + + // hname + eCAL::nanopb::encode_string(pb_request_.header.hname, request_.header.hname); + // sname + eCAL::nanopb::encode_string(pb_request_.header.sname, request_.header.sname); + // sid + eCAL::nanopb::encode_string(pb_request_.header.sid, request_.header.sid); + // mname + eCAL::nanopb::encode_string(pb_request_.header.mname, request_.header.mname); + // error + eCAL::nanopb::encode_string(pb_request_.header.error, request_.header.error); + // id + pb_request_.header.id = request_.header.id; + // state + pb_request_.header.state = static_cast(request_.header.state); + + /////////////////////////////////////////////// + // request + /////////////////////////////////////////////// + eCAL::nanopb::encode_string(pb_request_.request, request_.request); + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr}; + pb_encode(&pb_sizestream, eCAL_pb_Request_fields, &pb_request_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool RequestStruct2Buffer(const eCAL::Service::Request& request_, T& target_buffer_) + { + target_buffer_.clear(); + + /////////////////////////////////////////////// + // prepare request for encoding + /////////////////////////////////////////////// + eCAL_pb_Request pb_request = eCAL_pb_Request_init_default; + size_t target_size = RequestStruct2PbRequest(request_, pb_request); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_Request_fields, &pb_request)) + { + std::cerr << "NanoPb eCAL::Service::Request encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + bool Buffer2RequestStruct(const char* data_, size_t size_, eCAL::Service::Request& request_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_Request pb_request = eCAL_pb_Request_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + + /////////////////////////////////////////////// + // header information + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_request.header.hname, request_.header.hname); + // sname + eCAL::nanopb::decode_string(pb_request.header.sname, request_.header.sname); + // sid + eCAL::nanopb::decode_string(pb_request.header.sid, request_.header.sid); + // mname + eCAL::nanopb::decode_string(pb_request.header.mname, request_.header.mname); + // error + eCAL::nanopb::decode_string(pb_request.header.error, request_.header.error); + + /////////////////////////////////////////////// + // request + /////////////////////////////////////////////// + // request + eCAL::nanopb::decode_string(pb_request.request, request_.request); + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_Request_fields, &pb_request)) + { + std::cerr << "NanoPb eCAL::Service::Request decode failed: " << pb_istream.errmsg << std::endl; + } + + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + + /////////////////////////////////////////////// + // header information + /////////////////////////////////////////////// + // id + request_.header.id = pb_request.header.id; + // state + request_.header.state = static_cast(pb_request.header.state); + + return true; + } + + size_t ResponseStruct2PbResponse(const eCAL::Service::Response& response_, eCAL_pb_Response& pb_response_) + { + pb_response_ = eCAL_pb_Response_init_default; + + /////////////////////////////////////////////// + // header information + /////////////////////////////////////////////// + pb_response_.has_header = true; + + // hname + eCAL::nanopb::encode_string(pb_response_.header.hname, response_.header.hname); + // sname + eCAL::nanopb::encode_string(pb_response_.header.sname, response_.header.sname); + // sid + eCAL::nanopb::encode_string(pb_response_.header.sid, response_.header.sid); + // mname + eCAL::nanopb::encode_string(pb_response_.header.mname, response_.header.mname); + // error + eCAL::nanopb::encode_string(pb_response_.header.error, response_.header.error); + // id + pb_response_.header.id = response_.header.id; + // state + pb_response_.header.state = static_cast(response_.header.state); + + /////////////////////////////////////////////// + // response + /////////////////////////////////////////////// + eCAL::nanopb::encode_string(pb_response_.response, response_.response); + pb_response_.ret_state = response_.ret_state; + + /////////////////////////////////////////////// + // evaluate byte size + /////////////////////////////////////////////// + pb_ostream_t pb_sizestream = { nullptr, nullptr, 0, 0, nullptr}; + pb_encode(&pb_sizestream, eCAL_pb_Response_fields, &pb_response_); + + // return encoding byte size + return pb_sizestream.bytes_written; + } + + template + bool ResponseStruct2Buffer(const eCAL::Service::Response& response_, T& target_buffer_) + { + target_buffer_.clear(); + + /////////////////////////////////////////////// + // prepare response for encoding + /////////////////////////////////////////////// + eCAL_pb_Response pb_response = eCAL_pb_Response_init_default; + size_t target_size = ResponseStruct2PbResponse(response_, pb_response); + + /////////////////////////////////////////////// + // encode it + /////////////////////////////////////////////// + target_buffer_.resize(target_size); + pb_ostream_t pb_ostream; + pb_ostream = pb_ostream_from_buffer((pb_byte_t*)(target_buffer_.data()), target_buffer_.size()); + if (!pb_encode(&pb_ostream, eCAL_pb_Response_fields, &pb_response)) + { + std::cerr << "NanoPb eCAL::Service::Response encode failed: " << pb_ostream.errmsg << std::endl; + } + else + { + return true; + } + + return false; + } + + bool Buffer2ResponseStruct(const char* data_, size_t size_, eCAL::Service::Response& response_) + { + if (data_ == nullptr) return false; + if (size_ == 0) return false; + + // initialize + eCAL_pb_Response pb_response = eCAL_pb_Response_init_default; + + /////////////////////////////////////////////// + // assign decoder + /////////////////////////////////////////////// + + /////////////////////////////////////////////// + // header information + /////////////////////////////////////////////// + // hname + eCAL::nanopb::decode_string(pb_response.header.hname, response_.header.hname); + // sname + eCAL::nanopb::decode_string(pb_response.header.sname, response_.header.sname); + // sid + eCAL::nanopb::decode_string(pb_response.header.sid, response_.header.sid); + // mname + eCAL::nanopb::decode_string(pb_response.header.mname, response_.header.mname); + // error + eCAL::nanopb::decode_string(pb_response.header.error, response_.header.error); + + /////////////////////////////////////////////// + // response + /////////////////////////////////////////////// + // response + eCAL::nanopb::decode_string(pb_response.response, response_.response); + + /////////////////////////////////////////////// + // decode it + /////////////////////////////////////////////// + pb_istream_t pb_istream; + pb_istream = pb_istream_from_buffer((pb_byte_t*)data_, size_); + if (!pb_decode(&pb_istream, eCAL_pb_Response_fields, &pb_response)) + { + std::cerr << "NanoPb eCAL::Service::Response decode failed: " << pb_istream.errmsg << std::endl; + } + + /////////////////////////////////////////////// + // assign values + /////////////////////////////////////////////// + + /////////////////////////////////////////////// + // header information + /////////////////////////////////////////////// + // id + response_.header.id = pb_response.header.id; + // state + response_.header.state = static_cast(pb_response.header.state); + + /////////////////////////////////////////////// + // response + /////////////////////////////////////////////// + response_.ret_state = pb_response.ret_state; + + return true; + } +} + +namespace eCAL +{ + // service request - serialize/deserialize + bool SerializeToBuffer(const Service::Request& source_sample_, std::vector& target_buffer_) + { + return RequestStruct2Buffer(source_sample_, target_buffer_); + } + + bool SerializeToBuffer(const Service::Request& source_sample_, std::string& target_buffer_) + { + return RequestStruct2Buffer(source_sample_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Service::Request& target_sample_) + { + return Buffer2RequestStruct(data_, size_, target_sample_); + } + + // service response - serialize/deserialize + bool SerializeToBuffer(const Service::Response& source_sample_, std::vector& target_buffer_) + { + return ResponseStruct2Buffer(source_sample_, target_buffer_); + } + + bool SerializeToBuffer(const Service::Response& source_sample_, std::string& target_buffer_) + { + return ResponseStruct2Buffer(source_sample_, target_buffer_); + } + + bool DeserializeFromBuffer(const char* data_, size_t size_, Service::Response& target_sample_) + { + return Buffer2ResponseStruct(data_, size_, target_sample_); + } +} diff --git a/src/core/src/serialization/ecal_serialize_service.h b/src/core/src/serialization/ecal_serialize_service.h new file mode 100644 index 0000000..71cc255 --- /dev/null +++ b/src/core/src/serialization/ecal_serialize_service.h @@ -0,0 +1,43 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_serialize_service.h + * @brief eCAL service serialization / deserialization +**/ + +#pragma once + +#include "ecal_struct_service.h" + +#include +#include + +namespace eCAL +{ + // service request - serialize/deserialize + bool SerializeToBuffer(const Service::Request& source_sample_, std::vector& target_buffer_); + bool SerializeToBuffer(const Service::Request& source_sample_, std::string& target_buffer_); + bool DeserializeFromBuffer(const char* data_, size_t size_, Service::Request& target_sample_); + + // service response - serialize/deserialize + bool SerializeToBuffer(const Service::Response& source_sample_, std::vector& target_buffer_); + bool SerializeToBuffer(const Service::Response& source_sample_, std::string& target_buffer_); + bool DeserializeFromBuffer(const char* data_, size_t size_, Service::Response& target_sample_); +} diff --git a/src/core/src/serialization/ecal_struct_logging.h b/src/core/src/serialization/ecal_struct_logging.h new file mode 100644 index 0000000..6613341 --- /dev/null +++ b/src/core/src/serialization/ecal_struct_logging.h @@ -0,0 +1,54 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_struct_logging.h + * @brief eCAL logging as struct +**/ + +#pragma once + +#include + +#include +#include +#include + +namespace eCAL +{ + namespace Logging + { + struct LogMessage + { + int64_t time = 0; // time + std::string hname; // host name + int32_t pid = 0; // process id + std::string pname; // process name + std::string uname; // unit name + eCAL_Logging_eLogLevel level = log_level_none; // message level + std::string content; // message content + }; + + // LogMessage list + struct LogMessageList + { + std::list log_messages; // log messages + }; + } +} diff --git a/ecal/core/src/io/udp_receiver.h b/src/core/src/serialization/ecal_struct_sample_common.h similarity index 57% rename from ecal/core/src/io/udp_receiver.h rename to src/core/src/serialization/ecal_struct_sample_common.h index 500aaee..a30247b 100644 --- a/ecal/core/src/io/udp_receiver.h +++ b/src/core/src/serialization/ecal_struct_sample_common.h @@ -5,9 +5,9 @@ * 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. @@ -18,33 +18,36 @@ */ /** - * @brief UDP receiver class + * @file ecal_struct_sample_common.h + * @brief eCAL common struct types **/ #pragma once -#include -#include "ecal_receiver.h" - namespace eCAL { - class CUDPReceiverBase; - - class CUDPReceiver : public CReceiver + enum eCmdType { - public: - CUDPReceiver(); - - bool Create(const SReceiverAttr& attr_) override; - bool Destroy() override; - - bool AddMultiCastGroup(const char* ipaddr_); - bool RemMultiCastGroup(const char* ipaddr_); - - size_t Receive(char* buf_, size_t len_, int timeout_, ::sockaddr_in* address_ = nullptr) override; + bct_none = 0, + bct_set_sample = 1, + bct_reg_publisher = 2, + bct_reg_subscriber = 3, + bct_reg_process = 4, + bct_reg_service = 5, + bct_reg_client = 6, + bct_unreg_publisher = 12, + bct_unreg_subscriber = 13, + bct_unreg_process = 14, + bct_unreg_service = 15, + bct_unreg_client = 16 + }; - protected: - bool m_use_npcap; - std::shared_ptr m_socket_impl; + enum eTLayerType + { + tl_none = 0, + tl_ecal_udp_mc = 1, + tl_ecal_shm = 4, + tl_ecal_tcp = 5, + tl_all = 255, }; } diff --git a/src/core/src/serialization/ecal_struct_sample_payload.h b/src/core/src/serialization/ecal_struct_sample_payload.h new file mode 100644 index 0000000..2875f85 --- /dev/null +++ b/src/core/src/serialization/ecal_struct_sample_payload.h @@ -0,0 +1,80 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_struct_sample_payload.h + * @brief eCAL payload as struct +**/ + +#pragma once + +#include "ecal_struct_sample_common.h" + +#include +#include +#include + +namespace eCAL +{ + namespace Payload + { + // Topic information + struct Topic + { + std::string hname; // host name + std::string tid; // topic id + std::string tname; // topic name + }; + + // Topic content payload + enum ePayloadType + { + pl_none = 0, // payload not initialized + pl_raw = 1, // payload represented as raw pointer + size + pl_vec = 2, // payload represented std::vector + }; + + struct Payload + { + ePayloadType type = pl_none; + const char* raw_addr = nullptr; // payload represented as raw pointer + size_t raw_size = 0; // and its size + std::vector vec; // payload represented as std::vector + }; + + // Topic content + struct Content + { + int64_t id = 0; // payload id + int64_t clock = 0; // internal used clock + int64_t time = 0; // time the content was updated + int64_t hash = 0; // unique hash for that payload + int32_t size = 0; // size (additional for none payload "header only samples") + Payload payload; // payload represented as raw pointer or a std::vector + }; + + // Payload sample + struct Sample + { + eCmdType cmd_type = bct_none; // payload command type + Topic topic; // topic information + Content content; // topic content + }; + } +} diff --git a/src/core/src/serialization/ecal_struct_sample_registration.h b/src/core/src/serialization/ecal_struct_sample_registration.h new file mode 100644 index 0000000..33e8abd --- /dev/null +++ b/src/core/src/serialization/ecal_struct_sample_registration.h @@ -0,0 +1,196 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_struct_sample_registration.h + * @brief eCAL registration as struct +**/ + +#pragma once + +#include "ecal_struct_sample_common.h" +#include "ecal_struct_service.h" + +#include +#include +#include +#include + +namespace eCAL +{ + namespace Registration + { + enum eProcessSeverity + { + proc_sev_unknown = 0, + proc_sev_healthy = 1, + proc_sev_warning = 2, + proc_sev_critical = 3, + proc_sev_failed = 4 + }; + + enum eProcessSeverityLevel + { + proc_sev_level_unknown = 0, + proc_sev_level1 = 1, + proc_sev_level2 = 2, + proc_sev_level3 = 3, + proc_sev_level4 = 4, + proc_sev_level5 = 5 + }; + + enum eTSyncState + { + tsync_none = 0, + tsync_realtime = 1, + tsync_replay = 2 + }; + + // Operating system details + struct OSInfo + { + std::string osname; // name + }; + + // eCAL host + struct Host + { + std::string hname; // host name + OSInfo os; // operating system details + }; + + // Process severity information + struct ProcessState + { + eProcessSeverity severity = proc_sev_unknown; // severity + eProcessSeverityLevel severity_level = proc_sev_level_unknown; // severity level + std::string info; // info string + }; + + // Transport layer parameters for ecal udp multicast + struct LayerParUdpMC + { + }; + + // Transport layer parameters for ecal tcp + struct LayerParTcp + { + int32_t port = 0; // tcp writers port number + }; + + // Transport layer parameters for ecal shm + struct LayerParShm + { + std::list memory_file_list; // list of memory file names + }; + + // Connection parameter for reader/writer + struct ConnectionPar + { + 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 + }; + + // Transport layer information + struct TLayer + { + eTLayerType type = tl_none; // transport layer type + int32_t version = 0; // transport layer version + bool confirmed = false; // transport layer used? + ConnectionPar par_layer; // transport layer parameter + }; + + // Process information + struct Process + { + int32_t rclock = 0; // registration clock + std::string hname; // host name + std::string hgname; // host group name + int32_t pid = 0; // process id + std::string pname; // process name + std::string uname; // unit name + std::string pparam; // process parameter + int64_t datawrite = 0; // data write bytes per sec + int64_t dataread = 0; // data read bytes per sec + ProcessState state; // process state info + eTSyncState tsync_state = tsync_none; // time synchronization state + std::string tsync_mod_name; // time synchronization module name + 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 + }; + + // Data type information + struct DataTypeInformation + { + std::string name; // name of the datatype + std::string encoding; // encoding of the datatype (e.g., protobuf, flatbuffers, capnproto) + std::string desc; // descriptor information of the datatype (necessary for reflection) + }; + + // eCAL topic information + struct Topic + { + int32_t rclock = 0; // registration clock (heart beat) + std::string hname; // host name + std::string hgname; // host group name + int32_t pid = 0; // process id + std::string pname; // process name + std::string uname; // unit name + std::string tid; // topic id + std::string tname; // topic name + std::string direction; // direction (publisher, subscriber) + std::string ttype; // topic type + topic encoding (deprecated) + std::string tdesc; // topic description (protocol descriptor) (deprecated) + + DataTypeInformation tdatatype; // topic datatype information (encoding & type & description) + + std::vector 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 + int32_t connections_ext = 0; // number of external connected entities + int32_t message_drops = 0; // dropped messages + + int64_t did = 0; // data send id (publisher setid) + int64_t dclock = 0; // data clock (send / receive action) + int32_t dfreq = 0; // data frequency (send / receive registrations per second) [mHz] + + std::map attr; // generic topic description + }; + + // Registration sample + struct Sample + { + eCmdType cmd_type = bct_none; // registration command type + Host host; // host information + Process process; // process information + Service::Service service; // service information + Service::Client client ; // client information + Topic topic; // topic information + }; + + // Registration sample list + struct SampleList + { + std::list samples; // list of Samples used currently by SHM registration + }; + } +} diff --git a/src/core/src/serialization/ecal_struct_service.h b/src/core/src/serialization/ecal_struct_service.h new file mode 100644 index 0000000..c8cc783 --- /dev/null +++ b/src/core/src/serialization/ecal_struct_service.h @@ -0,0 +1,109 @@ +/* ========================= 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 ================================= +*/ + +/** + * @file ecal_struct_service.h + * @brief eCAL service as struct +**/ + +#pragma once + +#include +#include + +namespace eCAL +{ + namespace Service + { + // eServiceCallState + enum eMethodCallState + { + none = 0, + executed = 1, + failed = 2 + }; + + // ServiceHeader + struct ServiceHeader + { + std::string hname; // Host name + std::string sname; // Service name + std::string sid; // Service id + std::string mname; // Method name + std::string error; // Error message + int32_t id = 0; // Session id + eMethodCallState state = none; // Method call state + }; + + // Service Request + struct Request + { + ServiceHeader header; // Common service header + std::string request; // Request payload + }; + + // Service Response + struct Response + { + ServiceHeader header; // Common service header + std::string response; // Response payload + int64_t ret_state = 0; // Callback return state + }; + + // Service Method + struct Method + { + std::string mname; // Method name + std::string req_type; // Request type + std::string req_desc; // Request descriptor + std::string resp_type; // Response type + std::string resp_desc; // Response descriptor + int64_t call_count = 0; // Call counter + }; + + // Service + struct Service + { + int32_t rclock = 0; // Registration clock + std::string hname; // Host name + std::string pname; // Process name + std::string uname; // Unit name + int32_t pid = 0; // Process id + std::string sname; // Service name + std::string sid; // Service id + std::vector methods; // List of methods + uint32_t version = 0; // Service protocol version + uint32_t tcp_port_v0 = 0; // The TCP port used for that service (v0) + uint32_t tcp_port_v1 = 0; // The TCP port used for that service (v1) + }; + + // Client + struct Client + { + int32_t rclock = 0; // Registration clock + std::string hname; // Host name + std::string pname; // Process name + std::string uname; // Unit name + int32_t pid = 0; // Process id + std::string sname; // Service name + std::string sid; // Service id + uint32_t version = 0; // Client protocol version + }; + } +} diff --git a/src/core/src/serialization/nanopb/ecal.pb.c b/src/core/src/serialization/nanopb/ecal.pb.c new file mode 100644 index 0000000..aaf947b --- /dev/null +++ b/src/core/src/serialization/nanopb/ecal.pb.c @@ -0,0 +1,19 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "ecal.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_Content, eCAL_pb_Content, AUTO) + + +PB_BIND(eCAL_pb_Sample, eCAL_pb_Sample, 2) + + +PB_BIND(eCAL_pb_SampleList, eCAL_pb_SampleList, AUTO) + + + + diff --git a/src/core/src/serialization/nanopb/ecal.pb.h b/src/core/src/serialization/nanopb/ecal.pb.h new file mode 100644 index 0000000..60a5d6a --- /dev/null +++ b/src/core/src/serialization/nanopb/ecal.pb.h @@ -0,0 +1,156 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_ECAL_PB_H_INCLUDED +#define PB_ECAL_PB_ECAL_PB_H_INCLUDED +#include +#include "host.pb.h" +#include "process.pb.h" +#include "service.pb.h" +#include "topic.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _eCAL_pb_eCmdType { + eCAL_pb_eCmdType_bct_none = 0, /* undefined command */ + eCAL_pb_eCmdType_bct_set_sample = 1, /* set sample content */ + eCAL_pb_eCmdType_bct_reg_publisher = 2, /* register publisher */ + eCAL_pb_eCmdType_bct_reg_subscriber = 3, /* register subscriber */ + eCAL_pb_eCmdType_bct_reg_process = 4, /* register process */ + eCAL_pb_eCmdType_bct_reg_service = 5, /* register service */ + eCAL_pb_eCmdType_bct_reg_client = 6, /* register client */ + eCAL_pb_eCmdType_bct_unreg_publisher = 12, /* unregister publisher */ + eCAL_pb_eCmdType_bct_unreg_subscriber = 13, /* unregister subscriber */ + eCAL_pb_eCmdType_bct_unreg_process = 14, /* unregister process */ + eCAL_pb_eCmdType_bct_unreg_service = 15, /* unregister service */ + eCAL_pb_eCmdType_bct_unreg_client = 16 /* unregister client */ +} eCAL_pb_eCmdType; + +/* Struct definitions */ +typedef struct _eCAL_pb_Content { + int64_t id; /* sample id */ + int64_t clock; /* internal used clock */ + int64_t time; /* time the content was updated */ + pb_callback_t payload; /* octet stream */ + int32_t size; /* size (additional for none payload "header only samples") */ + int64_t hash; /* unique hash for that sample */ +} eCAL_pb_Content; + +typedef struct _eCAL_pb_Sample { + eCAL_pb_eCmdType cmd_type; /* sample command type */ + bool has_host; + eCAL_pb_Host host; /* host information */ + bool has_process; + eCAL_pb_Process process; /* process information */ + bool has_service; + eCAL_pb_Service service; /* service information */ + bool has_topic; + eCAL_pb_Topic topic; /* topic information */ + bool has_content; + eCAL_pb_Content content; /* topic content */ + bool has_client; + eCAL_pb_Client client; /* client information */ + pb_callback_t 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 */ +} eCAL_pb_Sample; + +typedef struct _eCAL_pb_SampleList { + pb_callback_t samples; /* list of Samples used currently by SHM registration */ +} eCAL_pb_SampleList; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _eCAL_pb_eCmdType_MIN eCAL_pb_eCmdType_bct_none +#define _eCAL_pb_eCmdType_MAX eCAL_pb_eCmdType_bct_unreg_client +#define _eCAL_pb_eCmdType_ARRAYSIZE ((eCAL_pb_eCmdType)(eCAL_pb_eCmdType_bct_unreg_client+1)) + + +#define eCAL_pb_Sample_cmd_type_ENUMTYPE eCAL_pb_eCmdType + + + +/* Initializer values for message structs */ +#define eCAL_pb_Content_init_default {0, 0, 0, {{NULL}, NULL}, 0, 0} +#define eCAL_pb_Sample_init_default {_eCAL_pb_eCmdType_MIN, false, eCAL_pb_Host_init_default, false, eCAL_pb_Process_init_default, false, eCAL_pb_Service_init_default, false, eCAL_pb_Topic_init_default, false, eCAL_pb_Content_init_default, false, eCAL_pb_Client_init_default, {{NULL}, NULL}} +#define eCAL_pb_SampleList_init_default {{{NULL}, NULL}} +#define eCAL_pb_Content_init_zero {0, 0, 0, {{NULL}, NULL}, 0, 0} +#define eCAL_pb_Sample_init_zero {_eCAL_pb_eCmdType_MIN, false, eCAL_pb_Host_init_zero, false, eCAL_pb_Process_init_zero, false, eCAL_pb_Service_init_zero, false, eCAL_pb_Topic_init_zero, false, eCAL_pb_Content_init_zero, false, eCAL_pb_Client_init_zero, {{NULL}, NULL}} +#define eCAL_pb_SampleList_init_zero {{{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_Content_id_tag 1 +#define eCAL_pb_Content_clock_tag 2 +#define eCAL_pb_Content_time_tag 3 +#define eCAL_pb_Content_payload_tag 4 +#define eCAL_pb_Content_size_tag 6 +#define eCAL_pb_Content_hash_tag 7 +#define eCAL_pb_Sample_cmd_type_tag 1 +#define eCAL_pb_Sample_host_tag 2 +#define eCAL_pb_Sample_process_tag 3 +#define eCAL_pb_Sample_service_tag 4 +#define eCAL_pb_Sample_topic_tag 5 +#define eCAL_pb_Sample_content_tag 6 +#define eCAL_pb_Sample_client_tag 7 +#define eCAL_pb_Sample_padding_tag 8 +#define eCAL_pb_SampleList_samples_tag 1 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_Content_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, id, 1) \ +X(a, STATIC, SINGULAR, INT64, clock, 2) \ +X(a, STATIC, SINGULAR, INT64, time, 3) \ +X(a, CALLBACK, SINGULAR, BYTES, payload, 4) \ +X(a, STATIC, SINGULAR, INT32, size, 6) \ +X(a, STATIC, SINGULAR, INT64, hash, 7) +#define eCAL_pb_Content_CALLBACK pb_default_field_callback +#define eCAL_pb_Content_DEFAULT NULL + +#define eCAL_pb_Sample_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, cmd_type, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, host, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, process, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, service, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, topic, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, content, 6) \ +X(a, STATIC, OPTIONAL, MESSAGE, client, 7) \ +X(a, CALLBACK, SINGULAR, BYTES, padding, 8) +#define eCAL_pb_Sample_CALLBACK pb_default_field_callback +#define eCAL_pb_Sample_DEFAULT NULL +#define eCAL_pb_Sample_host_MSGTYPE eCAL_pb_Host +#define eCAL_pb_Sample_process_MSGTYPE eCAL_pb_Process +#define eCAL_pb_Sample_service_MSGTYPE eCAL_pb_Service +#define eCAL_pb_Sample_topic_MSGTYPE eCAL_pb_Topic +#define eCAL_pb_Sample_content_MSGTYPE eCAL_pb_Content +#define eCAL_pb_Sample_client_MSGTYPE eCAL_pb_Client + +#define eCAL_pb_SampleList_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, samples, 1) +#define eCAL_pb_SampleList_CALLBACK pb_default_field_callback +#define eCAL_pb_SampleList_DEFAULT NULL +#define eCAL_pb_SampleList_samples_MSGTYPE eCAL_pb_Sample + +extern const pb_msgdesc_t eCAL_pb_Content_msg; +extern const pb_msgdesc_t eCAL_pb_Sample_msg; +extern const pb_msgdesc_t eCAL_pb_SampleList_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_Content_fields &eCAL_pb_Content_msg +#define eCAL_pb_Sample_fields &eCAL_pb_Sample_msg +#define eCAL_pb_SampleList_fields &eCAL_pb_SampleList_msg + +/* Maximum encoded size of messages (where known) */ +/* eCAL_pb_Content_size depends on runtime parameters */ +/* eCAL_pb_Sample_size depends on runtime parameters */ +/* eCAL_pb_SampleList_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/host.pb.c b/src/core/src/serialization/nanopb/host.pb.c new file mode 100644 index 0000000..e772023 --- /dev/null +++ b/src/core/src/serialization/nanopb/host.pb.c @@ -0,0 +1,15 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "host.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_OSInfo, eCAL_pb_OSInfo, AUTO) + + +PB_BIND(eCAL_pb_Host, eCAL_pb_Host, AUTO) + + + diff --git a/src/core/src/serialization/nanopb/host.pb.h b/src/core/src/serialization/nanopb/host.pb.h new file mode 100644 index 0000000..63d43d6 --- /dev/null +++ b/src/core/src/serialization/nanopb/host.pb.h @@ -0,0 +1,67 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_HOST_PB_H_INCLUDED +#define PB_ECAL_PB_HOST_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _eCAL_pb_OSInfo { + pb_callback_t osname; /* name */ +} eCAL_pb_OSInfo; + +typedef struct _eCAL_pb_Host { + pb_callback_t hname; /* host name */ + bool has_os; + eCAL_pb_OSInfo os; /* operating system details */ +} eCAL_pb_Host; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define eCAL_pb_OSInfo_init_default {{{NULL}, NULL}} +#define eCAL_pb_Host_init_default {{{NULL}, NULL}, false, eCAL_pb_OSInfo_init_default} +#define eCAL_pb_OSInfo_init_zero {{{NULL}, NULL}} +#define eCAL_pb_Host_init_zero {{{NULL}, NULL}, false, eCAL_pb_OSInfo_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_OSInfo_osname_tag 1 +#define eCAL_pb_Host_hname_tag 1 +#define eCAL_pb_Host_os_tag 2 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_OSInfo_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, osname, 1) +#define eCAL_pb_OSInfo_CALLBACK pb_default_field_callback +#define eCAL_pb_OSInfo_DEFAULT NULL + +#define eCAL_pb_Host_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, hname, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, os, 2) +#define eCAL_pb_Host_CALLBACK pb_default_field_callback +#define eCAL_pb_Host_DEFAULT NULL +#define eCAL_pb_Host_os_MSGTYPE eCAL_pb_OSInfo + +extern const pb_msgdesc_t eCAL_pb_OSInfo_msg; +extern const pb_msgdesc_t eCAL_pb_Host_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_OSInfo_fields &eCAL_pb_OSInfo_msg +#define eCAL_pb_Host_fields &eCAL_pb_Host_msg + +/* Maximum encoded size of messages (where known) */ +/* eCAL_pb_OSInfo_size depends on runtime parameters */ +/* eCAL_pb_Host_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/layer.pb.c b/src/core/src/serialization/nanopb/layer.pb.c new file mode 100644 index 0000000..e2dc733 --- /dev/null +++ b/src/core/src/serialization/nanopb/layer.pb.c @@ -0,0 +1,28 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "layer.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_LayerParUdpMC, eCAL_pb_LayerParUdpMC, AUTO) + + +PB_BIND(eCAL_pb_LayerParShm, eCAL_pb_LayerParShm, AUTO) + + +PB_BIND(eCAL_pb_LayerParInproc, eCAL_pb_LayerParInproc, AUTO) + + +PB_BIND(eCAL_pb_LayerParTcp, eCAL_pb_LayerParTcp, AUTO) + + +PB_BIND(eCAL_pb_ConnnectionPar, eCAL_pb_ConnnectionPar, AUTO) + + +PB_BIND(eCAL_pb_TLayer, eCAL_pb_TLayer, AUTO) + + + + diff --git a/src/core/src/serialization/nanopb/layer.pb.h b/src/core/src/serialization/nanopb/layer.pb.h new file mode 100644 index 0000000..2965be9 --- /dev/null +++ b/src/core/src/serialization/nanopb/layer.pb.h @@ -0,0 +1,166 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_LAYER_PB_H_INCLUDED +#define PB_ECAL_PB_LAYER_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _eCAL_pb_eTLayerType { + eCAL_pb_eTLayerType_tl_none = 0, /* undefined */ + eCAL_pb_eTLayerType_tl_ecal_udp_mc = 1, /* ecal udp multicast */ + eCAL_pb_eTLayerType_tl_ecal_shm = 4, /* ecal shared memory */ + eCAL_pb_eTLayerType_tl_ecal_tcp = 5, /* ecal tcp */ + eCAL_pb_eTLayerType_tl_all = 255 /* all layer */ +} eCAL_pb_eTLayerType; + +/* Struct definitions */ +typedef struct _eCAL_pb_LayerParUdpMC { + char dummy_field; +} eCAL_pb_LayerParUdpMC; + +typedef struct _eCAL_pb_LayerParShm { + pb_callback_t memory_file_list; /* list of memory file names */ +} eCAL_pb_LayerParShm; + +typedef struct _eCAL_pb_LayerParInproc { + char dummy_field; +} eCAL_pb_LayerParInproc; + +typedef struct _eCAL_pb_LayerParTcp { + int32_t port; /* tcp writers port number */ +} eCAL_pb_LayerParTcp; + +typedef struct _eCAL_pb_ConnnectionPar { + bool has_layer_par_udpmc; + eCAL_pb_LayerParUdpMC layer_par_udpmc; /* parameter for ecal udp multicast */ + bool has_layer_par_shm; + eCAL_pb_LayerParShm layer_par_shm; /* parameter for ecal shared memory */ + bool has_layer_par_tcp; + eCAL_pb_LayerParTcp layer_par_tcp; /* parameter for ecal tcp */ +} eCAL_pb_ConnnectionPar; + +typedef struct _eCAL_pb_TLayer { + eCAL_pb_eTLayerType type; /* transport layer type */ + int32_t version; /* transport layer version */ + bool confirmed; /* transport layer used ? */ + bool has_par_layer; + eCAL_pb_ConnnectionPar par_layer; /* transport layer parameter */ +} eCAL_pb_TLayer; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _eCAL_pb_eTLayerType_MIN eCAL_pb_eTLayerType_tl_none +#define _eCAL_pb_eTLayerType_MAX eCAL_pb_eTLayerType_tl_all +#define _eCAL_pb_eTLayerType_ARRAYSIZE ((eCAL_pb_eTLayerType)(eCAL_pb_eTLayerType_tl_all+1)) + + + + + + +#define eCAL_pb_TLayer_type_ENUMTYPE eCAL_pb_eTLayerType + + +/* Initializer values for message structs */ +#define eCAL_pb_LayerParUdpMC_init_default {0} +#define eCAL_pb_LayerParShm_init_default {{{NULL}, NULL}} +#define eCAL_pb_LayerParInproc_init_default {0} +#define eCAL_pb_LayerParTcp_init_default {0} +#define eCAL_pb_ConnnectionPar_init_default {false, eCAL_pb_LayerParUdpMC_init_default, false, eCAL_pb_LayerParShm_init_default, false, eCAL_pb_LayerParTcp_init_default} +#define eCAL_pb_TLayer_init_default {_eCAL_pb_eTLayerType_MIN, 0, 0, false, eCAL_pb_ConnnectionPar_init_default} +#define eCAL_pb_LayerParUdpMC_init_zero {0} +#define eCAL_pb_LayerParShm_init_zero {{{NULL}, NULL}} +#define eCAL_pb_LayerParInproc_init_zero {0} +#define eCAL_pb_LayerParTcp_init_zero {0} +#define eCAL_pb_ConnnectionPar_init_zero {false, eCAL_pb_LayerParUdpMC_init_zero, false, eCAL_pb_LayerParShm_init_zero, false, eCAL_pb_LayerParTcp_init_zero} +#define eCAL_pb_TLayer_init_zero {_eCAL_pb_eTLayerType_MIN, 0, 0, false, eCAL_pb_ConnnectionPar_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_LayerParShm_memory_file_list_tag 1 +#define eCAL_pb_LayerParTcp_port_tag 1 +#define eCAL_pb_ConnnectionPar_layer_par_udpmc_tag 1 +#define eCAL_pb_ConnnectionPar_layer_par_shm_tag 2 +#define eCAL_pb_ConnnectionPar_layer_par_tcp_tag 4 +#define eCAL_pb_TLayer_type_tag 1 +#define eCAL_pb_TLayer_version_tag 2 +#define eCAL_pb_TLayer_confirmed_tag 3 +#define eCAL_pb_TLayer_par_layer_tag 5 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_LayerParUdpMC_FIELDLIST(X, a) \ + +#define eCAL_pb_LayerParUdpMC_CALLBACK NULL +#define eCAL_pb_LayerParUdpMC_DEFAULT NULL + +#define eCAL_pb_LayerParShm_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, STRING, memory_file_list, 1) +#define eCAL_pb_LayerParShm_CALLBACK pb_default_field_callback +#define eCAL_pb_LayerParShm_DEFAULT NULL + +#define eCAL_pb_LayerParInproc_FIELDLIST(X, a) \ + +#define eCAL_pb_LayerParInproc_CALLBACK NULL +#define eCAL_pb_LayerParInproc_DEFAULT NULL + +#define eCAL_pb_LayerParTcp_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, port, 1) +#define eCAL_pb_LayerParTcp_CALLBACK NULL +#define eCAL_pb_LayerParTcp_DEFAULT NULL + +#define eCAL_pb_ConnnectionPar_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, layer_par_udpmc, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, layer_par_shm, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, layer_par_tcp, 4) +#define eCAL_pb_ConnnectionPar_CALLBACK NULL +#define eCAL_pb_ConnnectionPar_DEFAULT NULL +#define eCAL_pb_ConnnectionPar_layer_par_udpmc_MSGTYPE eCAL_pb_LayerParUdpMC +#define eCAL_pb_ConnnectionPar_layer_par_shm_MSGTYPE eCAL_pb_LayerParShm +#define eCAL_pb_ConnnectionPar_layer_par_tcp_MSGTYPE eCAL_pb_LayerParTcp + +#define eCAL_pb_TLayer_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, type, 1) \ +X(a, STATIC, SINGULAR, INT32, version, 2) \ +X(a, STATIC, SINGULAR, BOOL, confirmed, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, par_layer, 5) +#define eCAL_pb_TLayer_CALLBACK NULL +#define eCAL_pb_TLayer_DEFAULT NULL +#define eCAL_pb_TLayer_par_layer_MSGTYPE eCAL_pb_ConnnectionPar + +extern const pb_msgdesc_t eCAL_pb_LayerParUdpMC_msg; +extern const pb_msgdesc_t eCAL_pb_LayerParShm_msg; +extern const pb_msgdesc_t eCAL_pb_LayerParInproc_msg; +extern const pb_msgdesc_t eCAL_pb_LayerParTcp_msg; +extern const pb_msgdesc_t eCAL_pb_ConnnectionPar_msg; +extern const pb_msgdesc_t eCAL_pb_TLayer_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_LayerParUdpMC_fields &eCAL_pb_LayerParUdpMC_msg +#define eCAL_pb_LayerParShm_fields &eCAL_pb_LayerParShm_msg +#define eCAL_pb_LayerParInproc_fields &eCAL_pb_LayerParInproc_msg +#define eCAL_pb_LayerParTcp_fields &eCAL_pb_LayerParTcp_msg +#define eCAL_pb_ConnnectionPar_fields &eCAL_pb_ConnnectionPar_msg +#define eCAL_pb_TLayer_fields &eCAL_pb_TLayer_msg + +/* Maximum encoded size of messages (where known) */ +/* 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_LayerParInproc_size 0 +#define eCAL_pb_LayerParTcp_size 11 +#define eCAL_pb_LayerParUdpMC_size 0 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/logging.pb.c b/src/core/src/serialization/nanopb/logging.pb.c new file mode 100644 index 0000000..aba64cb --- /dev/null +++ b/src/core/src/serialization/nanopb/logging.pb.c @@ -0,0 +1,15 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "logging.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_LogMessage, eCAL_pb_LogMessage, AUTO) + + +PB_BIND(eCAL_pb_LogMessageList, eCAL_pb_LogMessageList, AUTO) + + + diff --git a/src/core/src/serialization/nanopb/logging.pb.h b/src/core/src/serialization/nanopb/logging.pb.h new file mode 100644 index 0000000..696fa02 --- /dev/null +++ b/src/core/src/serialization/nanopb/logging.pb.h @@ -0,0 +1,81 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_LOGGING_PB_H_INCLUDED +#define PB_ECAL_PB_LOGGING_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _eCAL_pb_LogMessage { + int64_t time; /* time */ + pb_callback_t hname; /* host name */ + int32_t pid; /* process id */ + pb_callback_t pname; /* process name */ + pb_callback_t uname; /* unit name */ + int32_t level; /* message level */ + pb_callback_t content; /* message content */ +} eCAL_pb_LogMessage; + +typedef struct _eCAL_pb_LogMessageList { + pb_callback_t log_messages; /* log messages */ +} eCAL_pb_LogMessageList; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define eCAL_pb_LogMessage_init_default {0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}} +#define eCAL_pb_LogMessageList_init_default {{{NULL}, NULL}} +#define eCAL_pb_LogMessage_init_zero {0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}} +#define eCAL_pb_LogMessageList_init_zero {{{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_LogMessage_time_tag 1 +#define eCAL_pb_LogMessage_hname_tag 2 +#define eCAL_pb_LogMessage_pid_tag 3 +#define eCAL_pb_LogMessage_pname_tag 4 +#define eCAL_pb_LogMessage_uname_tag 5 +#define eCAL_pb_LogMessage_level_tag 6 +#define eCAL_pb_LogMessage_content_tag 7 +#define eCAL_pb_LogMessageList_log_messages_tag 1 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_LogMessage_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, time, 1) \ +X(a, CALLBACK, SINGULAR, STRING, hname, 2) \ +X(a, STATIC, SINGULAR, INT32, pid, 3) \ +X(a, CALLBACK, SINGULAR, STRING, pname, 4) \ +X(a, CALLBACK, SINGULAR, STRING, uname, 5) \ +X(a, STATIC, SINGULAR, INT32, level, 6) \ +X(a, CALLBACK, SINGULAR, STRING, content, 7) +#define eCAL_pb_LogMessage_CALLBACK pb_default_field_callback +#define eCAL_pb_LogMessage_DEFAULT NULL + +#define eCAL_pb_LogMessageList_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, log_messages, 1) +#define eCAL_pb_LogMessageList_CALLBACK pb_default_field_callback +#define eCAL_pb_LogMessageList_DEFAULT NULL +#define eCAL_pb_LogMessageList_log_messages_MSGTYPE eCAL_pb_LogMessage + +extern const pb_msgdesc_t eCAL_pb_LogMessage_msg; +extern const pb_msgdesc_t eCAL_pb_LogMessageList_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_LogMessage_fields &eCAL_pb_LogMessage_msg +#define eCAL_pb_LogMessageList_fields &eCAL_pb_LogMessageList_msg + +/* Maximum encoded size of messages (where known) */ +/* eCAL_pb_LogMessage_size depends on runtime parameters */ +/* eCAL_pb_LogMessageList_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/monitoring.pb.c b/src/core/src/serialization/nanopb/monitoring.pb.c new file mode 100644 index 0000000..e512c68 --- /dev/null +++ b/src/core/src/serialization/nanopb/monitoring.pb.c @@ -0,0 +1,12 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "monitoring.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_Monitoring, eCAL_pb_Monitoring, AUTO) + + + diff --git a/src/core/src/serialization/nanopb/monitoring.pb.h b/src/core/src/serialization/nanopb/monitoring.pb.h new file mode 100644 index 0000000..e6cc1db --- /dev/null +++ b/src/core/src/serialization/nanopb/monitoring.pb.h @@ -0,0 +1,68 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_MONITORING_PB_H_INCLUDED +#define PB_ECAL_PB_MONITORING_PB_H_INCLUDED +#include +#include "host.pb.h" +#include "process.pb.h" +#include "service.pb.h" +#include "topic.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _eCAL_pb_Monitoring { + pb_callback_t hosts; /* hosts */ + pb_callback_t processes; /* processes */ + pb_callback_t services; /* services */ + pb_callback_t topics; /* topics */ + pb_callback_t clients; /* clients */ +} eCAL_pb_Monitoring; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define eCAL_pb_Monitoring_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define eCAL_pb_Monitoring_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_Monitoring_hosts_tag 1 +#define eCAL_pb_Monitoring_processes_tag 2 +#define eCAL_pb_Monitoring_services_tag 3 +#define eCAL_pb_Monitoring_topics_tag 4 +#define eCAL_pb_Monitoring_clients_tag 5 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_Monitoring_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, hosts, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, processes, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, services, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, topics, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, clients, 5) +#define eCAL_pb_Monitoring_CALLBACK pb_default_field_callback +#define eCAL_pb_Monitoring_DEFAULT NULL +#define eCAL_pb_Monitoring_hosts_MSGTYPE eCAL_pb_Host +#define eCAL_pb_Monitoring_processes_MSGTYPE eCAL_pb_Process +#define eCAL_pb_Monitoring_services_MSGTYPE eCAL_pb_Service +#define eCAL_pb_Monitoring_topics_MSGTYPE eCAL_pb_Topic +#define eCAL_pb_Monitoring_clients_MSGTYPE eCAL_pb_Client + +extern const pb_msgdesc_t eCAL_pb_Monitoring_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_Monitoring_fields &eCAL_pb_Monitoring_msg + +/* Maximum encoded size of messages (where known) */ +/* eCAL_pb_Monitoring_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/nanopb/pb.h b/src/core/src/serialization/nanopb/nanopb/pb.h new file mode 100644 index 0000000..c7a055f --- /dev/null +++ b/src/core/src/serialization/nanopb/nanopb/pb.h @@ -0,0 +1,917 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.8" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#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; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_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; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/src/core/src/serialization/nanopb/nanopb/pb_common.c b/src/core/src/serialization/nanopb/nanopb/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/src/core/src/serialization/nanopb/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/src/core/src/serialization/nanopb/nanopb/pb_common.h b/src/core/src/serialization/nanopb/nanopb/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/src/core/src/serialization/nanopb/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/src/core/src/serialization/nanopb/nanopb/pb_decode.c b/src/core/src/serialization/nanopb/nanopb/pb_decode.c new file mode 100644 index 0000000..788998e --- /dev/null +++ b/src/core/src/serialization/nanopb/nanopb/pb_decode.c @@ -0,0 +1,1727 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* 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. + */ +#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) + #define checkreturn +#else + #define checkreturn __attribute__((warn_unused_result)) +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + return false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/src/core/src/serialization/nanopb/nanopb/pb_decode.h b/src/core/src/serialization/nanopb/nanopb/pb_decode.h new file mode 100644 index 0000000..ae1d3cc --- /dev/null +++ b/src/core/src/serialization/nanopb/nanopb/pb_decode.h @@ -0,0 +1,193 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + void *state; /* Free field for use by callback implementation */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/nanopb/pb_encode.c b/src/core/src/serialization/nanopb/nanopb/pb_encode.c new file mode 100644 index 0000000..7f56201 --- /dev/null +++ b/src/core/src/serialization/nanopb/nanopb/pb_encode.c @@ -0,0 +1,1000 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#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. + */ +#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) + #define checkreturn +#else + #define checkreturn __attribute__((warn_unused_result)) +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/src/core/src/serialization/nanopb/nanopb/pb_encode.h b/src/core/src/serialization/nanopb/nanopb/pb_encode.h new file mode 100644 index 0000000..8913683 --- /dev/null +++ b/src/core/src/serialization/nanopb/nanopb/pb_encode.h @@ -0,0 +1,185 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#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. */ + +#ifndef PB_NO_ERRMSG + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/process.pb.c b/src/core/src/serialization/nanopb/process.pb.c new file mode 100644 index 0000000..60dfc2d --- /dev/null +++ b/src/core/src/serialization/nanopb/process.pb.c @@ -0,0 +1,18 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "process.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_ProcessState, eCAL_pb_ProcessState, AUTO) + + +PB_BIND(eCAL_pb_Process, eCAL_pb_Process, AUTO) + + + + + + diff --git a/src/core/src/serialization/nanopb/process.pb.h b/src/core/src/serialization/nanopb/process.pb.h new file mode 100644 index 0000000..19e9a3e --- /dev/null +++ b/src/core/src/serialization/nanopb/process.pb.h @@ -0,0 +1,155 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_PROCESS_PB_H_INCLUDED +#define PB_ECAL_PB_PROCESS_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _eCAL_pb_eProcessSeverity { + eCAL_pb_eProcessSeverity_proc_sev_unknown = 0, /* condition unknown */ + eCAL_pb_eProcessSeverity_proc_sev_healthy = 1, /* process healthy */ + eCAL_pb_eProcessSeverity_proc_sev_warning = 2, /* process warning level */ + eCAL_pb_eProcessSeverity_proc_sev_critical = 3, /* process critical */ + eCAL_pb_eProcessSeverity_proc_sev_failed = 4 /* process failed */ +} eCAL_pb_eProcessSeverity; + +typedef enum _eCAL_pb_eProcessSeverityLevel { + eCAL_pb_eProcessSeverityLevel_proc_sev_level_unknown = 0, /* condition unknown */ + eCAL_pb_eProcessSeverityLevel_proc_sev_level1 = 1, /* default severity level 1 */ + eCAL_pb_eProcessSeverityLevel_proc_sev_level2 = 2, /* severity level 2 */ + eCAL_pb_eProcessSeverityLevel_proc_sev_level3 = 3, /* severity level 3 */ + eCAL_pb_eProcessSeverityLevel_proc_sev_level4 = 4, /* severity level 4 */ + eCAL_pb_eProcessSeverityLevel_proc_sev_level5 = 5 /* severity level 5 */ +} eCAL_pb_eProcessSeverityLevel; + +typedef enum _eCAL_pb_eTSyncState { + eCAL_pb_eTSyncState_tsync_none = 0, /* not synchronized */ + eCAL_pb_eTSyncState_tsync_realtime = 1, /* real time sync mode */ + eCAL_pb_eTSyncState_tsync_replay = 2 /* replay time sync mode */ +} eCAL_pb_eTSyncState; + +/* Struct definitions */ +typedef struct _eCAL_pb_ProcessState { + eCAL_pb_eProcessSeverity severity; /* severity */ + pb_callback_t info; /* info string */ + eCAL_pb_eProcessSeverityLevel severity_level; /* severity level */ +} eCAL_pb_ProcessState; + +typedef struct _eCAL_pb_Process { + int32_t rclock; /* registration clock */ + pb_callback_t hname; /* host name */ + int32_t pid; /* process id */ + pb_callback_t pname; /* process name */ + pb_callback_t uname; /* unit name */ + pb_callback_t pparam; /* process parameter */ + int64_t datawrite; /* data write bytes per sec */ + int64_t dataread; /* data read bytes per sec */ + bool has_state; + eCAL_pb_ProcessState state; /* process state info */ + eCAL_pb_eTSyncState tsync_state; /* time synchronization state */ + pb_callback_t tsync_mod_name; /* time synchronization module name */ + int32_t component_init_state; /* eCAL component initialization state (eCAL::Initialize(..)) */ + pb_callback_t component_init_info; /* like comp_init_state as human readable string (pub|sub|srv|mon|log|time|proc) */ + pb_callback_t ecal_runtime_version; /* loaded / runtime eCAL version of a component */ + pb_callback_t hgname; /* host group name */ +} eCAL_pb_Process; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _eCAL_pb_eProcessSeverity_MIN eCAL_pb_eProcessSeverity_proc_sev_unknown +#define _eCAL_pb_eProcessSeverity_MAX eCAL_pb_eProcessSeverity_proc_sev_failed +#define _eCAL_pb_eProcessSeverity_ARRAYSIZE ((eCAL_pb_eProcessSeverity)(eCAL_pb_eProcessSeverity_proc_sev_failed+1)) + +#define _eCAL_pb_eProcessSeverityLevel_MIN eCAL_pb_eProcessSeverityLevel_proc_sev_level_unknown +#define _eCAL_pb_eProcessSeverityLevel_MAX eCAL_pb_eProcessSeverityLevel_proc_sev_level5 +#define _eCAL_pb_eProcessSeverityLevel_ARRAYSIZE ((eCAL_pb_eProcessSeverityLevel)(eCAL_pb_eProcessSeverityLevel_proc_sev_level5+1)) + +#define _eCAL_pb_eTSyncState_MIN eCAL_pb_eTSyncState_tsync_none +#define _eCAL_pb_eTSyncState_MAX eCAL_pb_eTSyncState_tsync_replay +#define _eCAL_pb_eTSyncState_ARRAYSIZE ((eCAL_pb_eTSyncState)(eCAL_pb_eTSyncState_tsync_replay+1)) + +#define eCAL_pb_ProcessState_severity_ENUMTYPE eCAL_pb_eProcessSeverity +#define eCAL_pb_ProcessState_severity_level_ENUMTYPE eCAL_pb_eProcessSeverityLevel + +#define eCAL_pb_Process_tsync_state_ENUMTYPE eCAL_pb_eTSyncState + + +/* Initializer values for message structs */ +#define eCAL_pb_ProcessState_init_default {_eCAL_pb_eProcessSeverity_MIN, {{NULL}, NULL}, _eCAL_pb_eProcessSeverityLevel_MIN} +#define eCAL_pb_Process_init_default {0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0, false, eCAL_pb_ProcessState_init_default, _eCAL_pb_eTSyncState_MIN, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define eCAL_pb_ProcessState_init_zero {_eCAL_pb_eProcessSeverity_MIN, {{NULL}, NULL}, _eCAL_pb_eProcessSeverityLevel_MIN} +#define eCAL_pb_Process_init_zero {0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0, false, eCAL_pb_ProcessState_init_zero, _eCAL_pb_eTSyncState_MIN, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_ProcessState_severity_tag 1 +#define eCAL_pb_ProcessState_info_tag 2 +#define eCAL_pb_ProcessState_severity_level_tag 3 +#define eCAL_pb_Process_rclock_tag 1 +#define eCAL_pb_Process_hname_tag 2 +#define eCAL_pb_Process_pid_tag 3 +#define eCAL_pb_Process_pname_tag 4 +#define eCAL_pb_Process_uname_tag 5 +#define eCAL_pb_Process_pparam_tag 6 +#define eCAL_pb_Process_datawrite_tag 10 +#define eCAL_pb_Process_dataread_tag 11 +#define eCAL_pb_Process_state_tag 12 +#define eCAL_pb_Process_tsync_state_tag 13 +#define eCAL_pb_Process_tsync_mod_name_tag 14 +#define eCAL_pb_Process_component_init_state_tag 15 +#define eCAL_pb_Process_component_init_info_tag 16 +#define eCAL_pb_Process_ecal_runtime_version_tag 17 +#define eCAL_pb_Process_hgname_tag 18 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_ProcessState_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, severity, 1) \ +X(a, CALLBACK, SINGULAR, STRING, info, 2) \ +X(a, STATIC, SINGULAR, UENUM, severity_level, 3) +#define eCAL_pb_ProcessState_CALLBACK pb_default_field_callback +#define eCAL_pb_ProcessState_DEFAULT NULL + +#define eCAL_pb_Process_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, rclock, 1) \ +X(a, CALLBACK, SINGULAR, STRING, hname, 2) \ +X(a, STATIC, SINGULAR, INT32, pid, 3) \ +X(a, CALLBACK, SINGULAR, STRING, pname, 4) \ +X(a, CALLBACK, SINGULAR, STRING, uname, 5) \ +X(a, CALLBACK, SINGULAR, STRING, pparam, 6) \ +X(a, STATIC, SINGULAR, INT64, datawrite, 10) \ +X(a, STATIC, SINGULAR, INT64, dataread, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, state, 12) \ +X(a, STATIC, SINGULAR, UENUM, tsync_state, 13) \ +X(a, CALLBACK, SINGULAR, STRING, tsync_mod_name, 14) \ +X(a, STATIC, SINGULAR, INT32, component_init_state, 15) \ +X(a, CALLBACK, SINGULAR, STRING, component_init_info, 16) \ +X(a, CALLBACK, SINGULAR, STRING, ecal_runtime_version, 17) \ +X(a, CALLBACK, SINGULAR, STRING, hgname, 18) +#define eCAL_pb_Process_CALLBACK pb_default_field_callback +#define eCAL_pb_Process_DEFAULT NULL +#define eCAL_pb_Process_state_MSGTYPE eCAL_pb_ProcessState + +extern const pb_msgdesc_t eCAL_pb_ProcessState_msg; +extern const pb_msgdesc_t eCAL_pb_Process_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_ProcessState_fields &eCAL_pb_ProcessState_msg +#define eCAL_pb_Process_fields &eCAL_pb_Process_msg + +/* Maximum encoded size of messages (where known) */ +/* eCAL_pb_ProcessState_size depends on runtime parameters */ +/* eCAL_pb_Process_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/service.pb.c b/src/core/src/serialization/nanopb/service.pb.c new file mode 100644 index 0000000..9a2b533 --- /dev/null +++ b/src/core/src/serialization/nanopb/service.pb.c @@ -0,0 +1,28 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "service.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_ServiceHeader, eCAL_pb_ServiceHeader, AUTO) + + +PB_BIND(eCAL_pb_Request, eCAL_pb_Request, AUTO) + + +PB_BIND(eCAL_pb_Response, eCAL_pb_Response, AUTO) + + +PB_BIND(eCAL_pb_Method, eCAL_pb_Method, AUTO) + + +PB_BIND(eCAL_pb_Service, eCAL_pb_Service, AUTO) + + +PB_BIND(eCAL_pb_Client, eCAL_pb_Client, AUTO) + + + + diff --git a/src/core/src/serialization/nanopb/service.pb.h b/src/core/src/serialization/nanopb/service.pb.h new file mode 100644 index 0000000..571776e --- /dev/null +++ b/src/core/src/serialization/nanopb/service.pb.h @@ -0,0 +1,242 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_SERVICE_PB_H_INCLUDED +#define PB_ECAL_PB_SERVICE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _eCAL_pb_ServiceHeader_eCallState { + eCAL_pb_ServiceHeader_eCallState_none = 0, + eCAL_pb_ServiceHeader_eCallState_executed = 1, + eCAL_pb_ServiceHeader_eCallState_failed = 2 +} eCAL_pb_ServiceHeader_eCallState; + +/* Struct definitions */ +typedef struct _eCAL_pb_ServiceHeader { + pb_callback_t hname; /* host name */ + pb_callback_t sname; /* service name */ + pb_callback_t mname; /* method name */ + pb_callback_t error; /* error message */ + int32_t id; /* session id */ + eCAL_pb_ServiceHeader_eCallState state; /* method call state */ + pb_callback_t sid; /* service id */ +} eCAL_pb_ServiceHeader; + +typedef struct _eCAL_pb_Request { + bool has_header; + eCAL_pb_ServiceHeader header; /* common service header */ + pb_callback_t request; /* request payload */ +} eCAL_pb_Request; + +typedef struct _eCAL_pb_Response { + bool has_header; + eCAL_pb_ServiceHeader header; /* common service header */ + pb_callback_t response; /* response payload */ + int64_t ret_state; /* callback return state */ +} eCAL_pb_Response; + +typedef struct _eCAL_pb_Method { + pb_callback_t mname; /* method name */ + pb_callback_t req_type; /* request type */ + pb_callback_t resp_type; /* response type */ + int64_t call_count; /* call counter */ + pb_callback_t req_desc; /* request descriptor */ + pb_callback_t resp_desc; /* response descriptor */ +} eCAL_pb_Method; + +typedef struct _eCAL_pb_Service { + int32_t rclock; /* registration clock */ + pb_callback_t hname; /* host name */ + pb_callback_t pname; /* process name */ + pb_callback_t uname; /* unit name */ + int32_t pid; /* process id */ + pb_callback_t sname; /* service name */ + uint32_t tcp_port_v0; /* the tcp port used for that service */ + pb_callback_t methods; /* list of methods */ + pb_callback_t sid; /* service id */ + /* transport specific parameter (for internal use) */ + uint32_t version; /* service protocol version */ + uint32_t tcp_port_v1; /* the tcp port used for that service */ +} eCAL_pb_Service; + +typedef struct _eCAL_pb_Client { + int32_t rclock; /* registration clock */ + pb_callback_t hname; /* host name */ + pb_callback_t pname; /* process name */ + pb_callback_t uname; /* unit name */ + int32_t pid; /* process id */ + pb_callback_t sname; /* service name */ + pb_callback_t sid; /* service id */ + /* transport specific parameter (for internal use) */ + uint32_t version; /* client protocol version */ +} eCAL_pb_Client; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _eCAL_pb_ServiceHeader_eCallState_MIN eCAL_pb_ServiceHeader_eCallState_none +#define _eCAL_pb_ServiceHeader_eCallState_MAX eCAL_pb_ServiceHeader_eCallState_failed +#define _eCAL_pb_ServiceHeader_eCallState_ARRAYSIZE ((eCAL_pb_ServiceHeader_eCallState)(eCAL_pb_ServiceHeader_eCallState_failed+1)) + +#define eCAL_pb_ServiceHeader_state_ENUMTYPE eCAL_pb_ServiceHeader_eCallState + + + + + + + +/* Initializer values for message structs */ +#define eCAL_pb_ServiceHeader_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, _eCAL_pb_ServiceHeader_eCallState_MIN, {{NULL}, NULL}} +#define eCAL_pb_Request_init_default {false, eCAL_pb_ServiceHeader_init_default, {{NULL}, NULL}} +#define eCAL_pb_Response_init_default {false, eCAL_pb_ServiceHeader_init_default, {{NULL}, NULL}, 0} +#define eCAL_pb_Method_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}} +#define eCAL_pb_Service_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0} +#define eCAL_pb_Client_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define eCAL_pb_ServiceHeader_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, _eCAL_pb_ServiceHeader_eCallState_MIN, {{NULL}, NULL}} +#define eCAL_pb_Request_init_zero {false, eCAL_pb_ServiceHeader_init_zero, {{NULL}, NULL}} +#define eCAL_pb_Response_init_zero {false, eCAL_pb_ServiceHeader_init_zero, {{NULL}, NULL}, 0} +#define eCAL_pb_Method_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}} +#define eCAL_pb_Service_init_zero {0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0} +#define eCAL_pb_Client_init_zero {0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_ServiceHeader_hname_tag 1 +#define eCAL_pb_ServiceHeader_sname_tag 2 +#define eCAL_pb_ServiceHeader_mname_tag 3 +#define eCAL_pb_ServiceHeader_error_tag 4 +#define eCAL_pb_ServiceHeader_id_tag 5 +#define eCAL_pb_ServiceHeader_state_tag 6 +#define eCAL_pb_ServiceHeader_sid_tag 7 +#define eCAL_pb_Request_header_tag 1 +#define eCAL_pb_Request_request_tag 2 +#define eCAL_pb_Response_header_tag 1 +#define eCAL_pb_Response_response_tag 2 +#define eCAL_pb_Response_ret_state_tag 3 +#define eCAL_pb_Method_mname_tag 1 +#define eCAL_pb_Method_req_type_tag 2 +#define eCAL_pb_Method_resp_type_tag 3 +#define eCAL_pb_Method_call_count_tag 4 +#define eCAL_pb_Method_req_desc_tag 5 +#define eCAL_pb_Method_resp_desc_tag 6 +#define eCAL_pb_Service_rclock_tag 1 +#define eCAL_pb_Service_hname_tag 2 +#define eCAL_pb_Service_pname_tag 3 +#define eCAL_pb_Service_uname_tag 4 +#define eCAL_pb_Service_pid_tag 5 +#define eCAL_pb_Service_sname_tag 6 +#define eCAL_pb_Service_tcp_port_v0_tag 7 +#define eCAL_pb_Service_methods_tag 8 +#define eCAL_pb_Service_sid_tag 9 +#define eCAL_pb_Service_version_tag 10 +#define eCAL_pb_Service_tcp_port_v1_tag 11 +#define eCAL_pb_Client_rclock_tag 1 +#define eCAL_pb_Client_hname_tag 2 +#define eCAL_pb_Client_pname_tag 3 +#define eCAL_pb_Client_uname_tag 4 +#define eCAL_pb_Client_pid_tag 5 +#define eCAL_pb_Client_sname_tag 6 +#define eCAL_pb_Client_sid_tag 7 +#define eCAL_pb_Client_version_tag 8 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_ServiceHeader_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, hname, 1) \ +X(a, CALLBACK, SINGULAR, STRING, sname, 2) \ +X(a, CALLBACK, SINGULAR, STRING, mname, 3) \ +X(a, CALLBACK, SINGULAR, STRING, error, 4) \ +X(a, STATIC, SINGULAR, INT32, id, 5) \ +X(a, STATIC, SINGULAR, UENUM, state, 6) \ +X(a, CALLBACK, SINGULAR, STRING, sid, 7) +#define eCAL_pb_ServiceHeader_CALLBACK pb_default_field_callback +#define eCAL_pb_ServiceHeader_DEFAULT NULL + +#define eCAL_pb_Request_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, header, 1) \ +X(a, CALLBACK, SINGULAR, BYTES, request, 2) +#define eCAL_pb_Request_CALLBACK pb_default_field_callback +#define eCAL_pb_Request_DEFAULT NULL +#define eCAL_pb_Request_header_MSGTYPE eCAL_pb_ServiceHeader + +#define eCAL_pb_Response_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, header, 1) \ +X(a, CALLBACK, SINGULAR, BYTES, response, 2) \ +X(a, STATIC, SINGULAR, INT64, ret_state, 3) +#define eCAL_pb_Response_CALLBACK pb_default_field_callback +#define eCAL_pb_Response_DEFAULT NULL +#define eCAL_pb_Response_header_MSGTYPE eCAL_pb_ServiceHeader + +#define eCAL_pb_Method_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, mname, 1) \ +X(a, CALLBACK, SINGULAR, STRING, req_type, 2) \ +X(a, CALLBACK, SINGULAR, STRING, resp_type, 3) \ +X(a, STATIC, SINGULAR, INT64, call_count, 4) \ +X(a, CALLBACK, SINGULAR, BYTES, req_desc, 5) \ +X(a, CALLBACK, SINGULAR, BYTES, resp_desc, 6) +#define eCAL_pb_Method_CALLBACK pb_default_field_callback +#define eCAL_pb_Method_DEFAULT NULL + +#define eCAL_pb_Service_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, rclock, 1) \ +X(a, CALLBACK, SINGULAR, STRING, hname, 2) \ +X(a, CALLBACK, SINGULAR, STRING, pname, 3) \ +X(a, CALLBACK, SINGULAR, STRING, uname, 4) \ +X(a, STATIC, SINGULAR, INT32, pid, 5) \ +X(a, CALLBACK, SINGULAR, STRING, sname, 6) \ +X(a, STATIC, SINGULAR, UINT32, tcp_port_v0, 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, methods, 8) \ +X(a, CALLBACK, SINGULAR, STRING, sid, 9) \ +X(a, STATIC, SINGULAR, UINT32, version, 10) \ +X(a, STATIC, SINGULAR, UINT32, tcp_port_v1, 11) +#define eCAL_pb_Service_CALLBACK pb_default_field_callback +#define eCAL_pb_Service_DEFAULT NULL +#define eCAL_pb_Service_methods_MSGTYPE eCAL_pb_Method + +#define eCAL_pb_Client_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, rclock, 1) \ +X(a, CALLBACK, SINGULAR, STRING, hname, 2) \ +X(a, CALLBACK, SINGULAR, STRING, pname, 3) \ +X(a, CALLBACK, SINGULAR, STRING, uname, 4) \ +X(a, STATIC, SINGULAR, INT32, pid, 5) \ +X(a, CALLBACK, SINGULAR, STRING, sname, 6) \ +X(a, CALLBACK, SINGULAR, STRING, sid, 7) \ +X(a, STATIC, SINGULAR, UINT32, version, 8) +#define eCAL_pb_Client_CALLBACK pb_default_field_callback +#define eCAL_pb_Client_DEFAULT NULL + +extern const pb_msgdesc_t eCAL_pb_ServiceHeader_msg; +extern const pb_msgdesc_t eCAL_pb_Request_msg; +extern const pb_msgdesc_t eCAL_pb_Response_msg; +extern const pb_msgdesc_t eCAL_pb_Method_msg; +extern const pb_msgdesc_t eCAL_pb_Service_msg; +extern const pb_msgdesc_t eCAL_pb_Client_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_ServiceHeader_fields &eCAL_pb_ServiceHeader_msg +#define eCAL_pb_Request_fields &eCAL_pb_Request_msg +#define eCAL_pb_Response_fields &eCAL_pb_Response_msg +#define eCAL_pb_Method_fields &eCAL_pb_Method_msg +#define eCAL_pb_Service_fields &eCAL_pb_Service_msg +#define eCAL_pb_Client_fields &eCAL_pb_Client_msg + +/* Maximum encoded size of messages (where known) */ +/* eCAL_pb_ServiceHeader_size depends on runtime parameters */ +/* eCAL_pb_Request_size depends on runtime parameters */ +/* eCAL_pb_Response_size depends on runtime parameters */ +/* eCAL_pb_Method_size depends on runtime parameters */ +/* eCAL_pb_Service_size depends on runtime parameters */ +/* eCAL_pb_Client_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/core/src/serialization/nanopb/topic.pb.c b/src/core/src/serialization/nanopb/topic.pb.c new file mode 100644 index 0000000..7b8316d --- /dev/null +++ b/src/core/src/serialization/nanopb/topic.pb.c @@ -0,0 +1,18 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "topic.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(eCAL_pb_DataTypeInformation, eCAL_pb_DataTypeInformation, AUTO) + + +PB_BIND(eCAL_pb_Topic, eCAL_pb_Topic, 2) + + +PB_BIND(eCAL_pb_Topic_AttrEntry, eCAL_pb_Topic_AttrEntry, AUTO) + + + diff --git a/src/core/src/serialization/nanopb/topic.pb.h b/src/core/src/serialization/nanopb/topic.pb.h new file mode 100644 index 0000000..9403f61 --- /dev/null +++ b/src/core/src/serialization/nanopb/topic.pb.h @@ -0,0 +1,151 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_ECAL_PB_TOPIC_PB_H_INCLUDED +#define PB_ECAL_PB_TOPIC_PB_H_INCLUDED +#include +#include "layer.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef struct _eCAL_pb_DataTypeInformation { + pb_callback_t name; /* name of the datatype */ + pb_callback_t encoding; /* encoding of the datatype (e.g. protobuf, flatbuffers, capnproto) */ + pb_callback_t desc; /* descriptor information of the datatype (necessary for reflection) */ +} eCAL_pb_DataTypeInformation; + +typedef struct _eCAL_pb_Topic { + int32_t rclock; /* registration clock (heart beat) */ + pb_callback_t hname; /* host name */ + int32_t pid; /* process id */ + pb_callback_t pname; /* process name */ + pb_callback_t uname; /* unit name */ + pb_callback_t tid; /* topic id */ + pb_callback_t tname; /* topic name */ + pb_callback_t direction; /* direction (publisher, subscriber) */ + pb_callback_t ttype; /* topic type + topic encoding (deprecated) */ + pb_callback_t tdesc; /* topic description (protocol descriptor) (deprecated) */ + pb_callback_t tlayer; /* active topic transport layers and it's specific parameter */ + int32_t tsize; /* topic size */ + int32_t connections_loc; /* number of local connected entities */ + int32_t connections_ext; /* number of external connected entities */ + int32_t message_drops; /* dropped messages */ + int64_t did; /* data send id (publisher setid) */ + int64_t dclock; /* data clock (send / receive action) */ + int32_t dfreq; /* data frequency (send / receive samples per second) [mHz] */ + pb_callback_t attr; /* generic topic description */ + pb_callback_t hgname; /* host group name */ + bool has_tdatatype; + eCAL_pb_DataTypeInformation tdatatype; /* topic datatype information (encoding & type & description) */ +} eCAL_pb_Topic; + +typedef struct _eCAL_pb_Topic_AttrEntry { + pb_callback_t key; + pb_callback_t value; +} eCAL_pb_Topic_AttrEntry; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define eCAL_pb_DataTypeInformation_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define eCAL_pb_Topic_init_default {0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0, 0, 0, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, false, eCAL_pb_DataTypeInformation_init_default} +#define eCAL_pb_Topic_AttrEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define eCAL_pb_DataTypeInformation_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define eCAL_pb_Topic_init_zero {0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0, 0, 0, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, false, eCAL_pb_DataTypeInformation_init_zero} +#define eCAL_pb_Topic_AttrEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define eCAL_pb_DataTypeInformation_name_tag 1 +#define eCAL_pb_DataTypeInformation_encoding_tag 2 +#define eCAL_pb_DataTypeInformation_desc_tag 3 +#define eCAL_pb_Topic_rclock_tag 1 +#define eCAL_pb_Topic_hname_tag 2 +#define eCAL_pb_Topic_pid_tag 3 +#define eCAL_pb_Topic_pname_tag 4 +#define eCAL_pb_Topic_uname_tag 5 +#define eCAL_pb_Topic_tid_tag 6 +#define eCAL_pb_Topic_tname_tag 7 +#define eCAL_pb_Topic_direction_tag 8 +#define eCAL_pb_Topic_ttype_tag 9 +#define eCAL_pb_Topic_tdesc_tag 10 +#define eCAL_pb_Topic_tlayer_tag 12 +#define eCAL_pb_Topic_tsize_tag 13 +#define eCAL_pb_Topic_connections_loc_tag 16 +#define eCAL_pb_Topic_connections_ext_tag 17 +#define eCAL_pb_Topic_message_drops_tag 18 +#define eCAL_pb_Topic_did_tag 19 +#define eCAL_pb_Topic_dclock_tag 20 +#define eCAL_pb_Topic_dfreq_tag 21 +#define eCAL_pb_Topic_attr_tag 27 +#define eCAL_pb_Topic_hgname_tag 28 +#define eCAL_pb_Topic_tdatatype_tag 30 +#define eCAL_pb_Topic_AttrEntry_key_tag 1 +#define eCAL_pb_Topic_AttrEntry_value_tag 2 + +/* Struct field encoding specification for nanopb */ +#define eCAL_pb_DataTypeInformation_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, name, 1) \ +X(a, CALLBACK, SINGULAR, STRING, encoding, 2) \ +X(a, CALLBACK, SINGULAR, BYTES, desc, 3) +#define eCAL_pb_DataTypeInformation_CALLBACK pb_default_field_callback +#define eCAL_pb_DataTypeInformation_DEFAULT NULL + +#define eCAL_pb_Topic_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, rclock, 1) \ +X(a, CALLBACK, SINGULAR, STRING, hname, 2) \ +X(a, STATIC, SINGULAR, INT32, pid, 3) \ +X(a, CALLBACK, SINGULAR, STRING, pname, 4) \ +X(a, CALLBACK, SINGULAR, STRING, uname, 5) \ +X(a, CALLBACK, SINGULAR, STRING, tid, 6) \ +X(a, CALLBACK, SINGULAR, STRING, tname, 7) \ +X(a, CALLBACK, SINGULAR, STRING, direction, 8) \ +X(a, CALLBACK, SINGULAR, STRING, ttype, 9) \ +X(a, CALLBACK, SINGULAR, BYTES, tdesc, 10) \ +X(a, CALLBACK, REPEATED, MESSAGE, tlayer, 12) \ +X(a, STATIC, SINGULAR, INT32, tsize, 13) \ +X(a, STATIC, SINGULAR, INT32, connections_loc, 16) \ +X(a, STATIC, SINGULAR, INT32, connections_ext, 17) \ +X(a, STATIC, SINGULAR, INT32, message_drops, 18) \ +X(a, STATIC, SINGULAR, INT64, did, 19) \ +X(a, STATIC, SINGULAR, INT64, dclock, 20) \ +X(a, STATIC, SINGULAR, INT32, dfreq, 21) \ +X(a, CALLBACK, REPEATED, MESSAGE, attr, 27) \ +X(a, CALLBACK, SINGULAR, STRING, hgname, 28) \ +X(a, STATIC, OPTIONAL, MESSAGE, tdatatype, 30) +#define eCAL_pb_Topic_CALLBACK pb_default_field_callback +#define eCAL_pb_Topic_DEFAULT NULL +#define eCAL_pb_Topic_tlayer_MSGTYPE eCAL_pb_TLayer +#define eCAL_pb_Topic_attr_MSGTYPE eCAL_pb_Topic_AttrEntry +#define eCAL_pb_Topic_tdatatype_MSGTYPE eCAL_pb_DataTypeInformation + +#define eCAL_pb_Topic_AttrEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define eCAL_pb_Topic_AttrEntry_CALLBACK pb_default_field_callback +#define eCAL_pb_Topic_AttrEntry_DEFAULT NULL + +extern const pb_msgdesc_t eCAL_pb_DataTypeInformation_msg; +extern const pb_msgdesc_t eCAL_pb_Topic_msg; +extern const pb_msgdesc_t eCAL_pb_Topic_AttrEntry_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define eCAL_pb_DataTypeInformation_fields &eCAL_pb_DataTypeInformation_msg +#define eCAL_pb_Topic_fields &eCAL_pb_Topic_msg +#define eCAL_pb_Topic_AttrEntry_fields &eCAL_pb_Topic_AttrEntry_msg + +/* Maximum encoded size of messages (where known) */ +/* eCAL_pb_DataTypeInformation_size depends on runtime parameters */ +/* eCAL_pb_Topic_size depends on runtime parameters */ +/* eCAL_pb_Topic_AttrEntry_size depends on runtime parameters */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/ecal/core/src/service/ecal_clientgate.cpp b/src/core/src/service/ecal_clientgate.cpp similarity index 60% rename from ecal/core/src/service/ecal_clientgate.cpp rename to src/core/src/service/ecal_clientgate.cpp index f4e2503..ffe8427 100644 --- a/ecal/core/src/service/ecal_clientgate.cpp +++ b/src/core/src/service/ecal_clientgate.cpp @@ -22,7 +22,6 @@ **/ #include "ecal_clientgate.h" -#include "ecal_descgate.h" #include "service/ecal_service_client_impl.h" namespace eCAL @@ -47,6 +46,14 @@ namespace eCAL void CClientGate::Destroy() { if (!m_created) return; + + // destroy all remaining clients + const std::shared_lock lock(m_client_set_sync); + for (const auto& client : m_client_set) + { + client->Destroy(); + } + m_created = false; } @@ -55,7 +62,7 @@ namespace eCAL if (!m_created) return(false); // register internal client - std::unique_lock lock(m_client_set_sync); + const std::unique_lock lock(m_client_set_sync); m_client_set.insert(client_); return(true); @@ -67,7 +74,7 @@ namespace eCAL bool ret_state(false); // unregister internal service - std::unique_lock lock(m_client_set_sync); + const std::unique_lock lock(m_client_set_sync); for (auto iter = m_client_set.begin(); iter != m_client_set.end();) { if (*iter == client_) @@ -84,42 +91,28 @@ namespace eCAL return(ret_state); } - void CClientGate::ApplyServiceRegistration(const eCAL::pb::Sample& ecal_sample_) + void CClientGate::ApplyServiceRegistration(const Registration::Sample& ecal_sample_) { SServiceAttr service; - auto& ecal_sample_service = ecal_sample_.service(); - service.hname = ecal_sample_service.hname(); - service.pname = ecal_sample_service.pname(); - service.uname = ecal_sample_service.uname(); - service.sname = ecal_sample_service.sname(); - service.sid = ecal_sample_service.sid(); - service.pid = static_cast(ecal_sample_service.pid()); - service.tcp_port = static_cast(ecal_sample_service.tcp_port()); - - // store description - if (g_descgate()) - { - for (auto method : ecal_sample_service.methods()) - { - // Calculate the quality of the current info - ::eCAL::CDescGate::QualityFlags quality = ::eCAL::CDescGate::QualityFlags::NO_QUALITY; - if (!(method.req_type().empty() && method.resp_type().empty())) - quality |= ::eCAL::CDescGate::QualityFlags::TYPE_AVAILABLE; - if (!(method.req_desc().empty() && method.resp_desc().empty())) - quality |= ::eCAL::CDescGate::QualityFlags::DESCRIPTION_AVAILABLE; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_CORRECT_TOPIC; - quality |= ::eCAL::CDescGate::QualityFlags::INFO_COMES_FROM_PUBLISHER; - - g_descgate()->ApplyServiceDescription(ecal_sample_service.sname(), method.mname(), method.req_type(), method.req_desc(), method.resp_type(), method.resp_desc(), quality); - } - } + const auto& ecal_sample_service = ecal_sample_.service; + service.hname = ecal_sample_service.hname; + service.pname = ecal_sample_service.pname; + service.uname = ecal_sample_service.uname; + service.sname = ecal_sample_service.sname; + service.sid = ecal_sample_service.sid; + service.pid = static_cast(ecal_sample_service.pid); + + // internal protocol specifics + service.version = static_cast(ecal_sample_service.version); + service.tcp_port_v0 = static_cast(ecal_sample_service.tcp_port_v0); + service.tcp_port_v1 = static_cast(ecal_sample_service.tcp_port_v1); // create service key service.key = service.sname + ":" + service.sid + "@" + std::to_string(service.pid) + "@" + service.hname; // add or remove (timeouted) services { - std::unique_lock lock(m_service_register_map_sync); + const std::unique_lock lock(m_service_register_map_sync); // add / update service m_service_register_map[service.key] = service; @@ -130,8 +123,8 @@ namespace eCAL // inform matching clients { - std::shared_lock lock(m_client_set_sync); - for (auto& iter : m_client_set) + const std::shared_lock lock(m_client_set_sync); + for (const auto& iter : m_client_set) { if (iter->GetServiceName() == service.sname) { @@ -144,9 +137,9 @@ namespace eCAL std::vector CClientGate::GetServiceAttr(const std::string& service_name_) { std::vector ret_vec; - std::shared_lock lock(m_service_register_map_sync); + const std::shared_lock lock(m_service_register_map_sync); - // Look for requested services + // look for requested services for (auto service : m_service_register_map) { if (service.second.sname == service_name_) @@ -162,10 +155,10 @@ namespace eCAL if (!m_created) return; // refresh service registrations - std::shared_lock lock(m_client_set_sync); - for (auto iter : m_client_set) + const std::shared_lock lock(m_client_set_sync); + for (auto *iter : m_client_set) { iter->RefreshRegistration(); } } -}; +} diff --git a/ecal/core/src/service/ecal_clientgate.h b/src/core/src/service/ecal_clientgate.h similarity index 80% rename from ecal/core/src/service/ecal_clientgate.h rename to src/core/src/service/ecal_clientgate.h index fde5f6a..6d2ac72 100644 --- a/ecal/core/src/service/ecal_clientgate.h +++ b/src/core/src/service/ecal_clientgate.h @@ -24,18 +24,11 @@ #pragma once #include "ecal_def.h" -#include "ecal_expmap.h" +#include "serialization/ecal_struct_sample_registration.h" +#include "util/ecal_expmap.h" #include -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - #include #include #include @@ -56,7 +49,7 @@ namespace eCAL bool Register (CServiceClientImpl* client_); bool Unregister(CServiceClientImpl* client_); - void ApplyServiceRegistration(const eCAL::pb::Sample& ecal_sample_); + void ApplyServiceRegistration(const Registration::Sample& ecal_sample_); std::vector GetServiceAttr(const std::string& service_name_); @@ -65,12 +58,12 @@ namespace eCAL protected: static std::atomic m_created; - typedef std::set ServiceNameServiceImplSetT; + using ServiceNameServiceImplSetT = std::set; std::shared_timed_mutex m_client_set_sync; ServiceNameServiceImplSetT m_client_set; - typedef Util::CExpMap ConnectedMapT; + using ConnectedMapT = Util::CExpMap; std::shared_timed_mutex m_service_register_map_sync; ConnectedMapT m_service_register_map; }; -}; +} diff --git a/ecal/core/src/service/ecal_service_client.cpp b/src/core/src/service/ecal_service_client.cpp similarity index 84% rename from ecal/core/src/service/ecal_service_client.cpp rename to src/core/src/service/ecal_service_client.cpp index d3ff55b..d23282f 100644 --- a/ecal/core/src/service/ecal_service_client.cpp +++ b/src/core/src/service/ecal_service_client.cpp @@ -22,6 +22,9 @@ **/ #include + +#include "ecal_clientgate.h" +#include "ecal_global_accessors.h" #include "ecal_service_client_impl.h" namespace eCAL @@ -66,9 +69,12 @@ namespace eCAL { if(m_created) return(false); - m_service_client_impl = new CServiceClientImpl; + m_service_client_impl = CServiceClientImpl::CreateInstance(service_name_); m_service_client_impl->Create(service_name_); + // register this client + if (g_clientgate() != nullptr) g_clientgate()->Register(m_service_client_impl.get()); + m_created = true; return(true); } @@ -83,8 +89,10 @@ namespace eCAL if(!m_created) return(false); m_created = false; + // unregister this client + if (g_clientgate() != nullptr) g_clientgate()->Unregister(m_service_client_impl.get()); + m_service_client_impl->Destroy(); - delete m_service_client_impl; m_service_client_impl = nullptr; return(true); @@ -135,36 +143,6 @@ namespace eCAL return(m_service_client_impl->Call(method_name_, request_, timeout_, service_response_vec_)); } - /** - * @brief Call method of this service, for specific host (deprecated). - * - * @param host_name_ Host name. - * @param method_name_ Method name. - * @param request_ Request string. - * @param [out] service_info_ Service response struct for detailed informations. - * @param [out] response_ Response string. - * - * @return True if successful. - **/ - bool CServiceClient::Call(const std::string& host_name_, const std::string& method_name_, const std::string& request_, struct SServiceResponse& service_info_, std::string& response_) - { - if (!m_created) return(false); - - m_service_client_impl->SetHostName(host_name_); - - ServiceResponseVecT service_response_vec; - if (m_service_client_impl->Call(method_name_, request_, -1, &service_response_vec)) - { - if (!service_response_vec.empty()) - { - service_info_ = service_response_vec[0]; - response_ = service_info_.response; - return(true); - } - } - return(false); - } - /** * @brief Call a method of this service asynchronously, responses will be returned by callback. * diff --git a/src/core/src/service/ecal_service_client_impl.cpp b/src/core/src/service/ecal_service_client_impl.cpp new file mode 100644 index 0000000..a3ad1b6 --- /dev/null +++ b/src/core/src/service/ecal_service_client_impl.cpp @@ -0,0 +1,735 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL service client implementation +**/ + +#include "ecal_global_accessors.h" + +#include "ecal_clientgate.h" +#include "ecal_service_client_impl.h" +#include "ecal_service_singleton_manager.h" +#include "registration/ecal_registration_provider.h" +#include "serialization/ecal_serialize_service.h" + +#include +#include +#include + +namespace eCAL +{ + std::shared_ptr CServiceClientImpl::CreateInstance() + { + return(std::shared_ptr(new CServiceClientImpl())); + } + + std::shared_ptr CServiceClientImpl::CreateInstance(const std::string& service_name_) + { + auto instance = std::shared_ptr(new CServiceClientImpl()); + instance->Create(service_name_); + return instance; + } + + /** + * @brief Service client implementation class. + **/ + CServiceClientImpl::CServiceClientImpl() : + m_response_callback(nullptr), + m_created(false) + { + } + + CServiceClientImpl::~CServiceClientImpl() + { + Destroy(); + } + + bool CServiceClientImpl::Create(const std::string& service_name_) + { + if (m_created) return(false); + + // set service name + m_service_name = service_name_; + + // create service id + std::stringstream counter; + counter << std::chrono::steady_clock::now().time_since_epoch().count(); + m_service_id = counter.str(); + + // register this client + Register(false); + + // mark as created + m_created = true; + + return(true); + } + + bool CServiceClientImpl::Destroy() + { + if (!m_created) return(false); + + // reset client map + { + std::lock_guard const lock(m_client_map_sync); + m_client_map.clear(); + } + + // reset method callback map + { + std::lock_guard const lock(m_response_callback_sync); + m_response_callback = nullptr; + } + + // reset event callback map + { + std::lock_guard const lock(m_event_callback_map_sync); + m_event_callback_map.clear(); + } + + // unregister this client + Unregister(); + + // reset internals + m_service_name.clear(); + m_service_id.clear(); + m_host_name.clear(); + + // mark as not created + m_created = false; + + return(true); + } + + bool CServiceClientImpl::SetHostName(const std::string& host_name_) + { + if (host_name_ == "*") m_host_name.clear(); + else m_host_name = host_name_; + return(true); + } + + // add callback function for service response + bool CServiceClientImpl::AddResponseCallback(const ResponseCallbackT& callback_) + { + std::lock_guard const lock(m_response_callback_sync); + m_response_callback = callback_; + return true; + } + + // remove callback function for service response + bool CServiceClientImpl::RemResponseCallback() + { + std::lock_guard const lock(m_response_callback_sync); + m_response_callback = nullptr; + return true; + } + + // add callback function for client events + bool CServiceClientImpl::AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_) + { + if (!m_created) return false; + + // store event callback + { + std::lock_guard const lock(m_event_callback_map_sync); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceClientImpl::AddEventCallback"); +#endif + m_event_callback_map[type_] = std::move(callback_); + } + + return true; + } + + // remove callback function for client events + bool CServiceClientImpl::RemEventCallback(eCAL_Client_Event type_) + { + if (!m_created) return false; + + // reset event callback + { + std::lock_guard const lock(m_event_callback_map_sync); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceClientImpl::RemEventCallback"); +#endif + m_event_callback_map[type_] = nullptr; + } + + return true; + } + + // blocking call, all responses will be returned in service_response_vec_ + bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_ms_, ServiceResponseVecT* service_response_vec_) + { + auto responses = CallBlocking(method_name_, request_, std::chrono::milliseconds(timeout_ms_)); + + if (service_response_vec_) + service_response_vec_->clear(); + + if (!responses) + { + return false; + } + else + { + // Copy our temporary return vector to the user's return vector + service_response_vec_->resize(responses->size()); + for (size_t i = 0; i < responses->size(); i++) + { + (*service_response_vec_)[i] = (*responses)[i].second; + } + + // Call the timeout callback for all services that have not returned yet + if (timeout_ms_ > 0) + { + std::lock_guard const lock_eb(m_event_callback_map_sync); + auto callback_it = m_event_callback_map.find(eCAL_Client_Event::client_event_timeout); + if (callback_it != m_event_callback_map.end()) + { + for (const auto& return_pair : (*responses)) + { + if (!return_pair.first) + { + SClientEventCallbackData sdata; + sdata.type = client_event_timeout; + sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + (callback_it->second)(m_service_name.c_str(), &sdata); + } + } + } + } + + // Determine if any call has been successful + for (size_t i = 0; i < responses->size(); i++) + { + if ((*responses)[i].second.call_state == call_state_executed) + return true; + } + return false; + } + } + + // blocking call, using callback + bool CServiceClientImpl::Call(const std::string& method_name_, const std::string& request_, int timeout_ms_) + { + auto responses = CallBlocking(method_name_, request_, std::chrono::milliseconds(timeout_ms_)); + + if (!responses) + { + return false; + } + else + { + // iterate over responses and call the response callbacks + { + std::lock_guard const lock_cb(m_response_callback_sync); + if (m_response_callback) + { + for (const auto& response_pair : *responses) + { + if (response_pair.first) + { + // Call callback, if the call didn't timeout + m_response_callback(response_pair.second); + } + } + } + } + + // iterate over responses and call the timeout callbacks + if (timeout_ms_ > 0) + { + std::lock_guard const lock_eb(m_event_callback_map_sync); + auto callback_it = m_event_callback_map.find(eCAL_Client_Event::client_event_timeout); + if (callback_it != m_event_callback_map.end()) + { + for (const auto& return_pair : (*responses)) + { + if (!return_pair.first) + { + SClientEventCallbackData sdata; + sdata.type = client_event_timeout; + sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + (callback_it->second)(m_service_name.c_str(), &sdata); + } + } + } + } + + // Determine if any call has been successful and return true, if so. + for (size_t i = 0; i < responses->size(); i++) + { + if ((*responses)[i].second.call_state == call_state_executed) + return true; + } + return false; + } + } + + // asynchronously call, using callback + bool CServiceClientImpl::CallAsync(const std::string& method_name_, const std::string& request_ /*, int timeout_ms_*/) + { + // TODO: implement timeout + + if (g_clientgate() == nullptr) + { + ErrorCallback(method_name_, "Clientgate error."); + return false; + } + + if (!m_created) + { + ErrorCallback(method_name_, "Client hasn't been created yet."); + return false; + } + + if (m_service_name.empty() + || method_name_.empty()) + { + ErrorCallback(method_name_, "Invalid service or method name."); + return false; + } + + // check for new server + CheckForNewServices(); + + // Copy raw request in a protocol buffer + // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) + Service::Request request; + request.header.mname = method_name_; + request.request = std::string(request_.data(), request_.size()); + + // serialize request + auto request_shared_ptr = std::make_shared(); + SerializeToBuffer(request, *request_shared_ptr); + + bool at_least_one_service_was_called (false); + + // Call all services + std::vector const service_vec = g_clientgate()->GetServiceAttr(m_service_name); + for (const auto& service : service_vec) + { + if (m_host_name.empty() || (m_host_name == service.hname)) + { + std::lock_guard const lock(m_client_map_sync); + auto client = m_client_map.find(service.key); + if (client != m_client_map.end()) + { + const eCAL::service::ClientResponseCallbackT response_callback + = [weak_me = std::weak_ptr(shared_from_this()), hostname = service.hname, servicename = service.sname] + (const eCAL::service::Error& response_error, const std::shared_ptr& response_) + { + auto me = weak_me.lock(); + if (!me) + { + return; + } + + std::lock_guard const lock(me->m_response_callback_sync); + + if (me->m_response_callback) + { + eCAL::SServiceResponse service_response_struct; + + service_response_struct.host_name = hostname; + service_response_struct.service_name = servicename; + + if (response_error) + { + service_response_struct.error_msg = response_error.ToString(); + service_response_struct.call_state = eCallState::call_state_failed; + service_response_struct.ret_state = 0; + } + else + { + fromSerializedProtobuf(*response_, service_response_struct); + } + + me->m_response_callback(service_response_struct); + } + }; + + if (client->second->async_call_service(request_shared_ptr, response_callback)) + at_least_one_service_was_called = true; + } + } + } + + return(at_least_one_service_was_called); + } + + // check connection state + bool CServiceClientImpl::IsConnected() + { + if (!m_created) return false; + + // check for connected clients + std::lock_guard const lock(m_connected_services_map_sync); + return !m_connected_services_map.empty(); + } + + // called by the eCAL::CClientGate to register a service + void CServiceClientImpl::RegisterService(const std::string& key_, const SServiceAttr& service_) + { + // check connections + std::lock_guard const lock(m_connected_services_map_sync); + + // is this a new connection ? + if (m_connected_services_map.find(key_) == m_connected_services_map.end()) + { + // call connect event + std::lock_guard const lock_eb(m_event_callback_map_sync); + auto e_iter = m_event_callback_map.find(client_event_connected); + if (e_iter != m_event_callback_map.end()) + { + SClientEventCallbackData sdata; + sdata.type = client_event_connected; + sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + sdata.attr = service_; + (e_iter->second)(m_service_name.c_str(), &sdata); + } + // add service + m_connected_services_map[key_] = service_; + } + } + + // called by eCAL:CClientGate every second to update registration layer + void CServiceClientImpl::RefreshRegistration() + { + if (!m_created) return; + Register(false); + } + + std::shared_ptr>> + CServiceClientImpl::CallBlocking(const std::string& method_name_ + , const std::string& request_ + , std::chrono::nanoseconds timeout_) + { + if (g_clientgate() == nullptr) return nullptr; + if (!m_created) return nullptr; + + if (m_service_name.empty() || method_name_.empty()) + return nullptr; + + // check for new server + CheckForNewServices(); + + // Copy raw request in a protocol buffer + // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) + Service::Request request; + request.header.mname = method_name_; + request.request = std::string(request_.data(), request_.size()); + // serialize request + auto request_shared_ptr = std::make_shared(); + SerializeToBuffer(request, *request_shared_ptr); + + std::vector const service_vec = g_clientgate()->GetServiceAttr(m_service_name); + + // Create a condition variable and a mutex to wait for the response + // All variables are in shared pointers, as we need to pass them to the + // callback function via the lambda capture. When the user uses the timeout, + // this method may finish earlier than the service calls, so we need to make + // sure the callbacks can still operate on those variables. + const auto mutex = std::make_shared(); + const auto condition_variable = std::make_shared(); + const auto responses = std::make_shared>>(); // Vector with [has_returned, response] pairs, so we know where a timeout has happened. + const auto block_modifying_responses = std::make_shared(false); + const auto finished_service_call_count = std::make_shared(0); + + const auto expected_service_call_count = std::make_shared(0); + + // Iterate over all service sessions and call each of them. + // Each successfull call will increment the finished_service_call_count. + // By comparing that with the expected_service_call_count we later know + // Whether all service calls have been completed. + for (const auto& service : service_vec) + { + // Only call service if host name matches + if (m_host_name.empty() || (m_host_name == service.hname)) + { + // Lock mutex for iterating over client session map + std::lock_guard const client_map_lock(m_client_map_sync); + + // Find the actual client session in the map + auto client = m_client_map.find(service.key); + if (client != m_client_map.end()) + { + eCAL::service::ClientResponseCallbackT response_callback; + + { + const std::lock_guard lock(*mutex); + (*expected_service_call_count)++; + responses->emplace_back(); + responses->back().first = false; // If this stays false, we have a timout + responses->back().second.host_name = service.hname; + responses->back().second.service_name = service.sname; + responses->back().second.service_id = service.key; + responses->back().second.method_name = method_name_; + responses->back().second.error_msg = "Timeout"; + responses->back().second.ret_state = 0; + responses->back().second.call_state = eCallState::call_state_failed; + responses->back().second.response = ""; + + // Create a response callback, that will set the response and notify the condition variable + response_callback + = [mutex, condition_variable, responses, block_modifying_responses, finished_service_call_count, i = (responses->size() - 1)] + (const eCAL::service::Error& response_error, const std::shared_ptr& response_) + { + const std::lock_guard lock(*mutex); + + if (!(*block_modifying_responses)) + { + // This calback has not timeouted. This does not tell us anything about the success, though. + (*responses)[i].first = true; + + if (response_error) + { + (*responses)[i].second.error_msg = response_error.ToString(); + (*responses)[i].second.call_state = eCallState::call_state_failed; + (*responses)[i].second.ret_state = 0; + } + else + { + fromSerializedProtobuf(*response_, (*responses)[i].second); + } + } + + (*finished_service_call_count)++; + condition_variable->notify_all(); + }; + + // Call service asynchronously + const bool call_success = client->second->async_call_service(request_shared_ptr, response_callback); + + if (!call_success) + { + // If the call failed, we know that the callback will never be called. + // Thus, we need to increment the finished_service_call_count here. + (*finished_service_call_count)++; + + // We also store an error in the response vector + responses->back().second.error_msg = "Stopped by user"; + responses->back().second.ret_state = 0; + responses->back().second.call_state = eCallState::call_state_failed; + } + + } // unlock mutex + + } + + } + } + + // Lock mutex, call service asynchronously and wait for the condition variable to be notified + { + std::unique_lock lock(*mutex); + if (timeout_ > std::chrono::nanoseconds::zero()) + { + condition_variable->wait_for(lock + , timeout_ + , [&expected_service_call_count, &finished_service_call_count]() + { + // Wait for all services to return something + return *expected_service_call_count == *finished_service_call_count; + }); + + } + else + { + condition_variable->wait(lock, [&expected_service_call_count, &finished_service_call_count]() + { + // Wait for all services to return something + return *expected_service_call_count == *finished_service_call_count; + }); + } + // Stop the callbacks from modifying the responses vector. This is important + //in a timeout case, as we want to preserve the vector from when the timeout + // occured. There is no way to stop the service calls from succeeding after + // that, but we don't want their values, any more. + *block_modifying_responses = true; + + return responses; + } + } + + void CServiceClientImpl::fromSerializedProtobuf(const std::string& response_pb_, eCAL::SServiceResponse& response_) + { + Service::Response response; + // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) + if (DeserializeFromBuffer(response_pb_.c_str(), response_pb_.size(), response)) + { + fromStruct(response, response_); + } + else + { + response_.error_msg = "Could not parse server response"; + response_.ret_state = 0; + response_.call_state = eCallState::call_state_failed; + response_.response = ""; + } + } + + void CServiceClientImpl::fromStruct(const Service::Response& response_struct_, eCAL::SServiceResponse& response_) + { + const auto& response_header = response_struct_.header; + response_.host_name = response_header.hname; + response_.service_name = response_header.sname; + response_.service_id = response_header.sid; + response_.method_name = response_header.mname; + response_.error_msg = response_header.error; + response_.ret_state = static_cast(response_struct_.ret_state); + switch (response_header.state) + { + case Service::eMethodCallState::executed: + response_.call_state = call_state_executed; + break; + case Service::eMethodCallState::failed: + response_.call_state = call_state_failed; + break; + default: + break; + } + response_.response = std::string(response_struct_.response.data(), response_struct_.response.size()); + } + + void CServiceClientImpl::Register(const bool force_) + { + if (m_service_name.empty()) return; + + Registration::Sample sample; + sample.cmd_type = bct_reg_client; + auto& service_client = sample.client; + service_client.version = m_client_version; + service_client.hname = Process::GetHostName(); + service_client.pname = Process::GetProcessName(); + service_client.uname = Process::GetUnitName(); + service_client.pid = Process::GetProcessID(); + service_client.sname = m_service_name; + service_client.sid = m_service_id; + + // register entity + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterClient(m_service_name, m_service_id, sample, force_); + + // refresh connected services map + CheckForNewServices(); + + // check for disconnected services + { + std::lock_guard const lock(m_client_map_sync); + for (auto& client : m_client_map) + { + if (client.second->get_state() == eCAL::service::State::FAILED) + { + std::string const service_key = client.first; + + // is the service still in the connecting map ? + auto iter = m_connected_services_map.find(service_key); + if (iter != m_connected_services_map.end()) + { + // call disconnect event + std::lock_guard const lock_cb(m_event_callback_map_sync); + auto e_iter = m_event_callback_map.find(client_event_disconnected); + if (e_iter != m_event_callback_map.end()) + { + SClientEventCallbackData sdata; + sdata.type = client_event_disconnected; + sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + sdata.attr = iter->second; + (e_iter->second)(m_service_name.c_str(), &sdata); + } + // remove service + m_connected_services_map.erase(iter); + } + } + } + } + } + + void CServiceClientImpl::Unregister() + { + if (m_service_name.empty()) return; + + Registration::Sample sample; + sample.cmd_type = bct_unreg_client; + auto& service_client = sample.client; + service_client.hname = Process::GetHostName(); + service_client.pname = Process::GetProcessName(); + service_client.uname = Process::GetUnitName(); + service_client.pid = Process::GetProcessID(); + service_client.sname = m_service_name; + service_client.sid = m_service_id; + service_client.version = m_client_version; + + // unregister entity + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterClient(m_service_name, m_service_id, sample, true); + } + + void CServiceClientImpl::CheckForNewServices() + { + if (g_clientgate() == nullptr) return; + + // check for new services + std::vector const service_vec = g_clientgate()->GetServiceAttr(m_service_name); + for (const auto& iter : service_vec) + { + std::lock_guard const lock(m_client_map_sync); + auto client = m_client_map.find(iter.key); + if (client == m_client_map.end()) + { + auto client_manager = eCAL::service::ServiceManager::instance()->get_client_manager(); + if (client_manager == nullptr || client_manager->is_stopped()) return; + + // Event callback (unused) + const eCAL::service::ClientSession::EventCallbackT event_callback + = [/*this, service_ = iter*/] // Using the this pointer here is extremely unsafe, as it actually forces us to manage the lifetime of this object. UPDATE: this class now inherits from shared_from_this, so when implementing this function, we can store a weak_ptr to this class. + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + { + // I have no idea why, but for some reason the event callbacks of the actual connetions are not even used. The connect / disconnect callbacks are executed whenever a new connection is found, and not when the client has actually connected or disconnected. I am preserving the previous behavior. + }; + + // Only connect via V0 protocol / V0 port, if V1 port is not available + const auto protocol_version = (iter.tcp_port_v1 != 0 ? iter.version : 0); + const auto port_to_use = (protocol_version == 0 ? iter.tcp_port_v0 : iter.tcp_port_v1); + + // Create the client and add it to the map + const auto new_client_session = client_manager->create_client(static_cast(protocol_version), iter.hname, port_to_use, event_callback); + if (new_client_session) + m_client_map[iter.key] = new_client_session; + } + } + } + + void CServiceClientImpl::ErrorCallback(const std::string& method_name_, const std::string& error_message_) + { + std::lock_guard const lock(m_response_callback_sync); + if (m_response_callback) + { + SServiceResponse service_response; + service_response.call_state = call_state_failed; + service_response.error_msg = error_message_; + service_response.ret_state = 0; + service_response.method_name = method_name_; + service_response.response.clear(); + m_response_callback(service_response); + } + } +} diff --git a/ecal/core/src/service/ecal_service_client_impl.h b/src/core/src/service/ecal_service_client_impl.h similarity index 56% rename from ecal/core/src/service/ecal_service_client_impl.h rename to src/core/src/service/ecal_service_client_impl.h index 9e290de..4211c48 100644 --- a/ecal/core/src/service/ecal_service_client_impl.h +++ b/src/core/src/service/ecal_service_client_impl.h @@ -22,24 +22,31 @@ **/ #include +#include #include +#include -#include "service/ecal_tcpclient.h" +#include #include #include +#include namespace eCAL { /** * @brief Service client implementation class. **/ - class CServiceClientImpl + class CServiceClientImpl : public std::enable_shared_from_this { public: + static std::shared_ptr CreateInstance(); + static std::shared_ptr CreateInstance(const std::string& service_name_); + + private: CServiceClientImpl(); - CServiceClientImpl(const std::string& service_name_); + public: ~CServiceClientImpl(); bool Create(const std::string& service_name_); @@ -56,18 +63,14 @@ namespace eCAL bool AddEventCallback(eCAL_Client_Event type_, ClientEventCallbackT callback_); bool RemEventCallback(eCAL_Client_Event type_); - // blocking call, no broadcast, first matching service only, response will be returned in service_response_ - [[deprecated]] - bool Call(const std::string& method_name_, const std::string& request_, struct SServiceResponse& service_response_); - // blocking call, all responses will be returned in service_response_vec_ - bool Call(const std::string& method_name_, const std::string& request_, int timeout_, ServiceResponseVecT* service_response_vec_); + bool Call(const std::string& method_name_, const std::string& request_, int timeout_ms_, ServiceResponseVecT* service_response_vec_); // blocking call, using callback - bool Call(const std::string& method_name_, const std::string& request_, int timeout_); + bool Call(const std::string& method_name_, const std::string& request_, int timeout_ms_); // asynchronously call, using callback (timeout not supported yet) - bool CallAsync(const std::string& method_name_, const std::string& request_ /*, int timeout_*/); + bool CallAsync(const std::string& method_name_, const std::string& request_ /*, int timeout_ms_*/); // check connection state bool IsConnected(); @@ -80,40 +83,46 @@ namespace eCAL std::string GetServiceName() { return m_service_name; }; - // this object must not be copied. + // this object must not be copied and moved CServiceClientImpl(const CServiceClientImpl&) = delete; CServiceClientImpl& operator=(const CServiceClientImpl&) = delete; + CServiceClientImpl(CServiceClientImpl&&) = delete; + CServiceClientImpl& operator=(CServiceClientImpl&&) = delete; - protected: - void CheckForNewServices(); + private: + std::shared_ptr>> CallBlocking(const std::string& method_name_, const std::string& request_, std::chrono::nanoseconds timeout_); - bool SendRequests(const std::string& host_name_, const std::string& method_name_, const std::string& request_, int timeout_); - bool SendRequest(std::shared_ptr client_, const std::string& method_name_, const std::string& request_, int timeout_, struct SServiceResponse& service_response_); + static void fromSerializedProtobuf(const std::string& response_pb_, eCAL::SServiceResponse& response_); + static void fromStruct(const Service::Response& response_struct_, eCAL::SServiceResponse& response_); - void SendRequestsAsync(const std::string& host_name_, const std::string& method_name_, const std::string& request_, int timeout_); - void SendRequestAsync(std::shared_ptr client_, const std::string& method_name_, const std::string& request_, int timeout_); + void Register(bool force_); + void Unregister(); + + void CheckForNewServices(); void ErrorCallback(const std::string &method_name_, const std::string &error_message_); - typedef std::map> ClientMapT; - std::mutex m_client_map_sync; - ClientMapT m_client_map; + using ClientMapT = std::map>; + std::mutex m_client_map_sync; + ClientMapT m_client_map; + + std::mutex m_response_callback_sync; + ResponseCallbackT m_response_callback; - std::mutex m_response_callback_sync; - ResponseCallbackT m_response_callback; + std::mutex m_event_callback_map_sync; + using EventCallbackMapT = std::map; + EventCallbackMapT m_event_callback_map; - std::mutex m_event_callback_map_sync; - typedef std::map EventCallbackMapT; - EventCallbackMapT m_event_callback_map; + std::mutex m_connected_services_map_sync; + using ServiceAttrMapT = std::map; + ServiceAttrMapT m_connected_services_map; - std::mutex m_connected_services_map_sync; - typedef std::map ServiceAttrMapT; - ServiceAttrMapT m_connected_services_map; + static constexpr int m_client_version = 1; - std::string m_service_name; - std::string m_service_id; - std::string m_host_name; + std::string m_service_name; + std::string m_service_id; + std::string m_host_name; - bool m_created; + bool m_created; }; } diff --git a/ecal/core/src/service/ecal_service_server.cpp b/src/core/src/service/ecal_service_server.cpp similarity index 85% rename from ecal/core/src/service/ecal_service_server.cpp rename to src/core/src/service/ecal_service_server.cpp index 427cf05..80c2d8b 100644 --- a/ecal/core/src/service/ecal_service_server.cpp +++ b/src/core/src/service/ecal_service_server.cpp @@ -23,6 +23,8 @@ #include +#include "ecal_servicegate.h" +#include "ecal_global_accessors.h" #include "ecal_service_server_impl.h" namespace eCAL @@ -71,7 +73,10 @@ namespace eCAL { if(m_created) return(false); - m_service_server_impl = new CServiceServerImpl(service_name_); + m_service_server_impl = CServiceServerImpl::CreateInstance(service_name_); + + // register this service + if (g_servicegate() != nullptr) g_servicegate()->Register(m_service_server_impl.get()); m_created = true; return(true); @@ -87,8 +92,10 @@ namespace eCAL if(!m_created) return(false); m_created = false; + // unregister this service + if (g_servicegate() != nullptr) g_servicegate()->Unregister(m_service_server_impl.get()); + m_service_server_impl->Destroy(); - delete m_service_server_impl; m_service_server_impl = nullptr; return(true); @@ -108,7 +115,13 @@ namespace eCAL bool CServiceServer::AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_) { if (!m_created) return false; - return m_service_server_impl->AddDescription(method_, req_type_, req_desc_, resp_type_, resp_desc_); + SDataTypeInformation request_type_information; + request_type_information.name = req_type_; + request_type_information.descriptor = req_desc_; + SDataTypeInformation response_type_information; + response_type_information.name = resp_type_; + response_type_information.descriptor = resp_desc_; + return m_service_server_impl->AddDescription(method_, request_type_information, response_type_information); } /** diff --git a/src/core/src/service/ecal_service_server_impl.cpp b/src/core/src/service/ecal_service_server_impl.cpp new file mode 100644 index 0000000..72e5088 --- /dev/null +++ b/src/core/src/service/ecal_service_server_impl.cpp @@ -0,0 +1,527 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL service server implementation +**/ + +#include + +#include "registration/ecal_registration_provider.h" +#include "ecal_servicegate.h" +#include "ecal_global_accessors.h" +#include "ecal_service_server_impl.h" +#include "ecal_service_singleton_manager.h" +#include "serialization/ecal_serialize_service.h" + +#include +#include +#include +#include + +namespace eCAL +{ + std::shared_ptr CServiceServerImpl::CreateInstance() + { + return std::shared_ptr(new CServiceServerImpl()); + } + + std::shared_ptr CServiceServerImpl::CreateInstance(const std::string& service_name_) + { + auto instance = std::shared_ptr (new CServiceServerImpl()); + instance->Create(service_name_); + return instance; + } + + CServiceServerImpl::CServiceServerImpl() = default; + + CServiceServerImpl::~CServiceServerImpl() + { + Destroy(); + } + + bool CServiceServerImpl::Create(const std::string& service_name_) + { + if (m_created) return(false); + + // set service name + m_service_name = service_name_; + + // create service id + std::stringstream counter; + counter << std::chrono::steady_clock::now().time_since_epoch().count(); + m_service_id = counter.str(); + + // Get global server manager + auto server_manager = eCAL::service::ServiceManager::instance()->get_server_manager(); + + if (!server_manager || server_manager->is_stopped()) + return false; + + // Create callback functions + const eCAL::service::Server::EventCallbackT event_callback + = [weak_me = std::weak_ptr(shared_from_this())] + (eCAL::service::ServerEventType event, const std::string& message) + { + auto me = weak_me.lock(); + + eCAL_Server_Event ecal_server_event = eCAL_Server_Event::server_event_none; + switch (event) + { + case eCAL::service::ServerEventType::Connected: + ecal_server_event = eCAL_Server_Event::server_event_connected; + break; + case eCAL::service::ServerEventType::Disconnected: + ecal_server_event = eCAL_Server_Event::server_event_disconnected; + break; + default: + break; + } + + if (me) + me->EventCallback(ecal_server_event, message); + }; + + const eCAL::service::Server::ServiceCallbackT service_callback + = [weak_me = std::weak_ptr(shared_from_this())] + (const std::shared_ptr& request, const std::shared_ptr& response) -> int + { + auto me = weak_me.lock(); + if (me) + return me->RequestCallback(*request, *response); + else + return -1; + }; + + // start service protocol version 0 + if (Config::IsServiceProtocolV0Enabled()) + { + m_tcp_server_v0 = server_manager->create_server(0, 0, service_callback, true, event_callback); + } + + // start service protocol version 1 + if (Config::IsServiceProtocolV1Enabled()) + { + m_tcp_server_v1 = server_manager->create_server(1, 0, service_callback, true, event_callback); + } + + // register this service + Register(false); + + // mark as created + m_created = true; + + return(true); + } + + bool CServiceServerImpl::Destroy() + { + if (!m_created) return(false); + + if (m_tcp_server_v0) + m_tcp_server_v0->stop(); + + if (m_tcp_server_v1) + m_tcp_server_v1->stop(); + + // reset method callback map + { + std::lock_guard const lock(m_method_map_sync); + m_method_map.clear(); + } + + // reset event callback map + { + std::lock_guard const lock(m_event_callback_map_sync); + m_event_callback_map.clear(); + } + + // unregister this service + Unregister(); + + // reset internals + m_service_name.clear(); + m_service_id.clear(); + + { + const std::lock_guard connected_lock(m_connected_mutex); + m_connected_v0 = false; + m_connected_v1 = false; + } + + m_created = false; + + return(true); + } + + bool CServiceServerImpl::AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_) + { + { + std::lock_guard const lock(m_method_map_sync); + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + iter->second.method.mname = method_; + iter->second.method.req_type = request_type_information_.name; + iter->second.method.req_desc = request_type_information_.descriptor; + iter->second.method.resp_type = response_type_information_.name; + iter->second.method.resp_desc = response_type_information_.descriptor; + } + else + { + SMethod method; + method.method.mname = method_; + method.method.req_type = request_type_information_.name; + method.method.req_desc = request_type_information_.descriptor; + method.method.resp_type = response_type_information_.name; + method.method.resp_desc = response_type_information_.descriptor; + m_method_map[method_] = method; + } + } + + return true; + + // update descgate infos + //return ApplyServiceToDescGate(method_, request_type_information_, response_type_information_); + } + + // add callback function for server method calls + bool CServiceServerImpl::AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_) + { + std::string req_desc; + std::string resp_desc; + { + std::lock_guard const lock(m_method_map_sync); + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + // should we overwrite this ? + iter->second.method.mname = method_; + iter->second.method.req_type = req_type_; + iter->second.method.resp_type = resp_type_; + // set callback + iter->second.callback = callback_; + + // read descriptors back from existing service method + req_desc = iter->second.method.req_desc; + resp_desc = iter->second.method.resp_desc; + } + else + { + SMethod method; + method.method.mname = method_; + method.method.req_type = req_type_; + method.method.resp_type = resp_type_; + method.callback = callback_; + m_method_map[method_] = method; + } + } + + SDataTypeInformation request_datatype_information; + request_datatype_information.name = req_type_; + request_datatype_information.descriptor = req_desc; + + SDataTypeInformation response_datatype_information; + response_datatype_information.name = resp_type_; + response_datatype_information.descriptor = resp_desc; + + return true; + } + + // remove callback function for server method calls + bool CServiceServerImpl::RemMethodCallback(const std::string& method_) + { + std::lock_guard const lock(m_method_map_sync); + + auto iter = m_method_map.find(method_); + if (iter != m_method_map.end()) + { + m_method_map.erase(iter); + return true; + } + return false; + } + + // add callback function for server events + bool CServiceServerImpl::AddEventCallback(eCAL_Server_Event type_, ServerEventCallbackT callback_) + { + if (!m_created) return false; + + // store event callback + { + std::lock_guard const lock(m_event_callback_map_sync); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::AddEventCallback"); +#endif + m_event_callback_map[type_] = std::move(callback_); + } + + return true; + } + + // remove callback function for server events + bool CServiceServerImpl::RemEventCallback(eCAL_Server_Event type_) + { + if (!m_created) return false; + + // reset event callback + { + std::lock_guard const lock(m_event_callback_map_sync); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_service_name + "::CServiceServerImpl::RemEventCallback"); +#endif + m_event_callback_map[type_] = nullptr; + } + + return true; + } + + // check connection state + bool CServiceServerImpl::IsConnected() + { + if (!m_created) return false; + + return (m_tcp_server_v0 && m_tcp_server_v0->is_connected()) + || (m_tcp_server_v1 && m_tcp_server_v1->is_connected()); + } + + // called by the eCAL::CServiceGate to register a client + void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) // TODO: This function is empty, why does it exist???? + { + } + + // called by eCAL:CServiceGate every second to update registration layer + void CServiceServerImpl::RefreshRegistration() + { + if (!m_created) return; + Register(false); + } + + void CServiceServerImpl::Register(const bool force_) + { + if (m_service_name.empty()) return; + + // might be zero in contruction phase + unsigned short const server_tcp_port_v0(m_tcp_server_v0 ? m_tcp_server_v0->get_port() : 0); + if ((Config::IsServiceProtocolV0Enabled()) && (server_tcp_port_v0 == 0)) return; + + unsigned short const server_tcp_port_v1(m_tcp_server_v1 ? m_tcp_server_v1->get_port() : 0); + if ((Config::IsServiceProtocolV1Enabled()) && (server_tcp_port_v1 == 0)) return; + + // create service registration sample + Registration::Sample sample; + sample.cmd_type = bct_reg_service; + auto& service = sample.service; + service.version = m_server_version; + service.hname = Process::GetHostName(); + service.pname = Process::GetProcessName(); + service.uname = Process::GetUnitName(); + service.pid = Process::GetProcessID(); + service.sname = m_service_name; + service.sid = m_service_id; + service.tcp_port_v0 = server_tcp_port_v0; + service.tcp_port_v1 = server_tcp_port_v1; + + // add methods + { + std::lock_guard const lock(m_method_map_sync); + for (const auto& iter : m_method_map) + { + Service::Method method; + method.mname = iter.first; + method.req_type = iter.second.method.req_type; + method.req_desc = iter.second.method.req_desc; + method.resp_type = iter.second.method.resp_type; + method.resp_desc = iter.second.method.resp_desc; + method.call_count = iter.second.method.call_count; + service.methods.push_back(method); + } + } + + // register entity + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterServer(m_service_name, m_service_id, sample, force_); + } + + void CServiceServerImpl::Unregister() + { + if (m_service_name.empty()) return; + + // create service registration sample + Registration::Sample sample; + sample.cmd_type = bct_unreg_service; + auto& service = sample.service; + service.version = m_server_version; + service.hname = Process::GetHostName(); + service.pname = Process::GetProcessName(); + service.uname = Process::GetUnitName(); + service.pid = Process::GetProcessID(); + service.sname = m_service_name; + service.sid = m_service_id; + + // unregister entity + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterServer(m_service_name, m_service_id, sample, true); + } + + int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) + { + // prepare response + Service::Response response; + auto& response_header = response.header; + response_header.hname = Process::GetHostName(); + response_header.sname = m_service_name; + response_header.sid = m_service_id; + + // try to parse request + Service::Request request; + if (!DeserializeFromBuffer(request_pb_.c_str(), request_pb_.size(), request)) + { + Logging::Log(log_level_error, m_service_name + "::CServiceServerImpl::RequestCallback failed to parse request message"); + + response_header.state = Service::eMethodCallState::failed; + std::string const emsg = "Service '" + m_service_name + "' request message could not be parsed."; + response_header.error = emsg; + + // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) + // serialize response and return "request message could not be parsed" + SerializeToBuffer(response, response_pb_); + + // Return Failed (error_code = -1), as parsing the request failed. The + // return value is not propagated to the remote caller. + return -1; + } + + // get method + SMethod method; + const auto& request_header = request.header; + response_header.mname = request_header.mname; + { + std::lock_guard const lock(m_method_map_sync); + + auto requested_method_iterator = m_method_map.find(request_header.mname); + if (requested_method_iterator == m_method_map.end()) + { + // set method call state 'failed' + response_header.state = Service::eMethodCallState::failed; + // set error message + std::string const emsg = "Service '" + m_service_name + "' has no method named '" + request_header.mname + "'"; + response_header.error = emsg; + + // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // Return Success (error_code = 0), as parsing the request worked. The + // return value is not propagated to the remote caller. + return 0; + } + else + { + // increase call count + auto call_count = requested_method_iterator->second.method.call_count; + requested_method_iterator->second.method.call_count = ++call_count; + + // store (copy) the method object, so we can release the mutex before calling the function + method = requested_method_iterator->second; + } + } + + // execute method (outside lock guard) + const std::string& request_s = request.request; + std::string response_s; + int const service_return_state = method.callback(method.method.mname, method.method.req_type, method.method.resp_type, request_s, response_s); + + // set method call state 'executed' + response_header.state = Service::eMethodCallState::executed; + // set method response and return state + response.response = response_s; + response.ret_state = service_return_state; + + // TODO: The next version of the service protocol should omit the double-serialization (i.e. copying the binary data in a protocol buffer and then serializing that again) + // serialize response and return "method not found" + SerializeToBuffer(response, response_pb_); + + // return success (error code 0) + return 0; + } + + void CServiceServerImpl::EventCallback(eCAL_Server_Event event_, const std::string& /*message_*/) + { + bool mode_changed(false); + + { + const std::lock_guard connected_lock(m_connected_mutex); + + // protocol version 0 + if (m_connected_v0) + { + if (m_tcp_server_v0 && !m_tcp_server_v0->is_connected()) + { + mode_changed = true; + m_connected_v0 = false; + Logging::Log(log_level_debug2, m_service_name + ": " + "client with protocol version 0 disconnected"); + } + } + else + { + if (m_tcp_server_v0 && m_tcp_server_v0->is_connected()) + { + mode_changed = true; + m_connected_v0 = true; + Logging::Log(log_level_debug2, m_service_name + ": " + "client with protocol version 0 connected"); + } + } + + // protocol version 1 + if (m_connected_v1) + { + if (m_tcp_server_v1 && !m_tcp_server_v1->is_connected()) + { + mode_changed = true; + m_connected_v1 = false; + Logging::Log(log_level_debug2, m_service_name + ": " + "client with protocol version 1 disconnected"); + } + } + else + { + if (m_tcp_server_v1 && m_tcp_server_v1->is_connected()) + { + mode_changed = true; + m_connected_v1 = true; + Logging::Log(log_level_debug2, m_service_name + ": " + "client with protocol version 1 connected"); + } + } + } + + if (mode_changed) + { + // call event + std::lock_guard const lock_cb(m_event_callback_map_sync); + auto e_iter = m_event_callback_map.find(event_); + if (e_iter != m_event_callback_map.end()) + { + SServerEventCallbackData sdata; + sdata.type = event_; + sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + (e_iter->second)(m_service_name.c_str(), &sdata); + } + } + } +} diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/src/core/src/service/ecal_service_server_impl.h similarity index 53% rename from ecal/core/src/service/ecal_service_server_impl.h rename to src/core/src/service/ecal_service_server_impl.h index f263071..c013ef6 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/src/core/src/service/ecal_service_server_impl.h @@ -25,30 +25,35 @@ #include #include - -#include "ecal_tcpserver.h" - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif +#include #include #include +#include + +#include "serialization/ecal_struct_service.h" + namespace eCAL { /** * @brief Service server implementation class. **/ - class CServiceServerImpl + class CServiceServerImpl : public std::enable_shared_from_this { public: + static std::shared_ptr CreateInstance(); + static std::shared_ptr CreateInstance(const std::string& service_name_); + + private: CServiceServerImpl(); - CServiceServerImpl(const std::string& service_name_); + + public: + // Delete copy and move constructors and assign operators. Necessary, as the class uses the this pointer, that would be dangling / pointing to a wrong object otherwise. + CServiceServerImpl(const CServiceServerImpl&) = delete; // Copy construct + CServiceServerImpl(CServiceServerImpl&&) = delete; // Move construct + CServiceServerImpl& operator=(const CServiceServerImpl&) = delete; // Copy assign + CServiceServerImpl& operator=(CServiceServerImpl&&) = delete; // Move assign ~CServiceServerImpl(); @@ -56,7 +61,7 @@ namespace eCAL bool Destroy(); - bool AddDescription(const std::string& method_, const std::string& req_type_, const std::string& req_desc_, const std::string& resp_type_, const std::string& resp_desc_); + bool AddDescription(const std::string& method_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_); // add and remove callback function for server method calls bool AddMethodCallback(const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const MethodCallbackT& callback_); @@ -77,41 +82,47 @@ namespace eCAL std::string GetServiceName() { return m_service_name; }; - // this object must not be copied. - CServiceServerImpl(const CServiceServerImpl&) = delete; - CServiceServerImpl& operator=(const CServiceServerImpl&) = delete; - protected: + void Register(bool force_); + void Unregister(); + /** * @brief Calls the request callback based on the request and fills the response * * @param[in] request_ The service request in serialized protobuf form * @param[out] response_ A serialized protobuf response. My not be set at all. * - * @return always 0. + * @return 0 if succeeded, -1 if not. */ - int RequestCallback(const std::string& request_, std::string& response_); + int RequestCallback(const std::string& request_pb_, std::string& response_pb_); void EventCallback(eCAL_Server_Event event_, const std::string& message_); - CTcpServer m_tcp_server; + std::shared_ptr m_tcp_server_v0; + std::shared_ptr m_tcp_server_v1; - std::string m_service_name; - std::string m_service_id; + static constexpr int m_server_version = 1; + + std::string m_service_name; + std::string m_service_id; struct SMethod { - eCAL::pb::Method method_pb; - MethodCallbackT callback; + Service::Method method; + MethodCallbackT callback; }; - std::mutex m_method_map_sync; - typedef std::map MethodMapT; - MethodMapT m_method_map; - std::mutex m_event_callback_map_sync; - typedef std::map EventCallbackMapT; - EventCallbackMapT m_event_callback_map; + using MethodMapT = std::map; + std::mutex m_method_map_sync; + MethodMapT m_method_map; + + using EventCallbackMapT = std::map; + std::mutex m_event_callback_map_sync; + EventCallbackMapT m_event_callback_map; - bool m_connected; - bool m_created; + bool m_created = false; + + mutable std::mutex m_connected_mutex; //!< mutex protecting the m_connected_v0 and m_connected_v1 variable, as those are modified by the event callbacks in another thread. + bool m_connected_v0 = false; + bool m_connected_v1 = false; }; } diff --git a/src/core/src/service/ecal_service_singleton_manager.cpp b/src/core/src/service/ecal_service_singleton_manager.cpp new file mode 100644 index 0000000..4cce08d --- /dev/null +++ b/src/core/src/service/ecal_service_singleton_manager.cpp @@ -0,0 +1,185 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "ecal_service_singleton_manager.h" + +#include + +namespace eCAL +{ + namespace service + { + eCAL::service::LoggerT ecal_logger(const std::string& node_name) + { + return [node_name](const LogLevel log_level, const std::string& message) + { + switch (log_level) + { + case LogLevel::DebugVerbose: + eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_debug4, "[" + node_name + "] " + message); + break; + case LogLevel::Debug: + eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_debug1, "[" + node_name + "] " + message); + break; + case LogLevel::Info: + eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_info, "[" + node_name + "] " + message); + break; + case LogLevel::Warning: + eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_warning, "[" + node_name + "] " + message); + break; + case LogLevel::Error: + eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_error, "[" + node_name + "] " + message); + break; + case LogLevel::Fatal: + eCAL::Logging::Log(eCAL_Logging_eLogLevel::log_level_fatal, "[" + node_name + "] " + message); + break; + default: + break; + } + }; + + } + + //////////////////////////////////////////////////////////// + // Singleton interface, Constructor, destructor + //////////////////////////////////////////////////////////// + constexpr size_t ServiceManager::num_io_threads; + + ServiceManager* ServiceManager::instance() + { + static ServiceManager instance; + return &instance; + } + + ServiceManager::ServiceManager() + : stopped(false) + {} + + ServiceManager::~ServiceManager() + { + stop(); + } + + //////////////////////////////////////////////////////////// + // Public API + //////////////////////////////////////////////////////////// + + std::shared_ptr ServiceManager::get_client_manager() + { + // Quickly check the atomic stopped boolean before actually locking the + // mutex. It can theoretically change before we got mutex access, so we + // will have to check it again. + if (stopped) + return nullptr; + + // Lock the mutex to actually make it thread safe + const std::lock_guard singleton_lock(singleton_mutex); + if (!stopped) + { + // Create io_context, if it didn't exist, yet + if (!io_context) + io_context = std::make_unique(); + + // Create the client manager, if it didn't exist, yet + if (!client_manager) + client_manager = eCAL::service::ClientManager::create(io_context, ecal_logger("Service Client")); + + // Start io threads, if necessary + if (io_threads.empty()) + { + for (size_t i = 0; i < num_io_threads; i++) + { + io_threads.emplace_back(std::make_unique([this]() { io_context->run(); })); + } + } + + // Return the client manager. The client manager has its own dummy work + // object, so it will keep the io_context alive, until the + // client_manager is stopped. + return client_manager; + } + return nullptr; + } + + std::shared_ptr ServiceManager::get_server_manager() + { + // Quickly check the atomic stopped boolean before actually locking the + // mutex. It can theoretically change before we got mutex access, so we + // will have to check it again. + if (stopped) + return nullptr; + + // Lock the mutex to actually make it thread safe + const std::lock_guard singleton_lock(singleton_mutex); + if (!stopped) + { + // Create io_context, if it didn't exist, yet + if (!io_context) + io_context = std::make_unique(); + + // Create the server manager, if it didn't exit, yet + if (!server_manager) + server_manager = eCAL::service::ServerManager::create(io_context, ecal_logger("Service Server")); + + // Start io threads, if necessary + if (io_threads.empty()) + { + for (size_t i = 0; i < num_io_threads; i++) + { + io_threads.emplace_back(std::make_unique([this]() { io_context->run(); })); + } + } + + // Return the server manager. The server manager has its own dummy work + // object, so it will keep the io_context alive, until the + // client_manager is stopped. + return server_manager; + } + return nullptr; + } + + void ServiceManager::stop() + { + const std::lock_guard singleton_lock(singleton_mutex); + + stopped = true; + + if (server_manager) + server_manager->stop(); + + if (client_manager) + client_manager->stop(); + + for (const auto& thread : io_threads) + thread->join(); + + server_manager.reset(); + client_manager.reset(); + io_threads.clear(); + io_context.reset(); + } + + void ServiceManager::reset() + { + const std::lock_guard singleton_lock(singleton_mutex); + stopped = false; + } + + } // namespace service +} // namespace eCAL diff --git a/src/core/src/service/ecal_service_singleton_manager.h b/src/core/src/service/ecal_service_singleton_manager.h new file mode 100644 index 0000000..6625da6 --- /dev/null +++ b/src/core/src/service/ecal_service_singleton_manager.h @@ -0,0 +1,80 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include + +#include + +namespace eCAL +{ + namespace service + { + class ServiceManager + { + //////////////////////////////////////////////////////////// + // Singleton interface, Constructor, destructor + //////////////////////////////////////////////////////////// + public: + static ServiceManager* instance(); + + private: + ServiceManager(); + + // Delete copy constructor and assignment operator + ServiceManager(const ServiceManager&) = delete; + ServiceManager& operator=(const ServiceManager&) = delete; + + // Delete move constructor and assignment operator + ServiceManager(ServiceManager&&) = delete; + ServiceManager& operator=(ServiceManager&&) = delete; + + public: + ~ServiceManager(); + + //////////////////////////////////////////////////////////// + // Public API + //////////////////////////////////////////////////////////// + public: + std::shared_ptr get_client_manager(); + std::shared_ptr get_server_manager(); + + void stop(); + void reset(); + + //////////////////////////////////////////////////////////// + // Member variables + //////////////////////////////////////////////////////////// + private: + static constexpr size_t num_io_threads = 4; + + std::mutex singleton_mutex; + + std::atomic stopped; + std::shared_ptr io_context; + std::vector> io_threads; + + std::shared_ptr client_manager; + std::shared_ptr server_manager; + }; + + } +} diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/src/core/src/service/ecal_servicegate.cpp similarity index 64% rename from ecal/core/src/service/ecal_servicegate.cpp rename to src/core/src/service/ecal_servicegate.cpp index 9aa5356..ba6e960 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/src/core/src/service/ecal_servicegate.cpp @@ -46,6 +46,14 @@ namespace eCAL void CServiceGate::Destroy() { if(!m_created) return; + + // destroy all remaining server + const std::shared_lock lock(m_service_set_sync); + for (const auto& service : m_service_set) + { + service->Destroy(); + } + m_created = false; } @@ -54,7 +62,7 @@ namespace eCAL if(!m_created) return(false); // register internal service - std::unique_lock lock(m_service_set_sync); + const std::unique_lock lock(m_service_set_sync); m_service_set.insert(service_); return(true); @@ -66,7 +74,7 @@ namespace eCAL bool ret_state(false); // unregister internal service - std::unique_lock lock(m_service_set_sync); + const std::unique_lock lock(m_service_set_sync); for (auto iter = m_service_set.begin(); iter != m_service_set.end();) { if (*iter == service_) @@ -83,42 +91,15 @@ namespace eCAL return(ret_state); } - void CServiceGate::ApplyClientRegistration(const eCAL::pb::Sample& ecal_sample_) - { - SClientAttr client; - auto& ecal_sample_client = ecal_sample_.client(); - client.hname = ecal_sample_client.hname(); - client.pname = ecal_sample_client.pname(); - client.uname = ecal_sample_client.uname(); - client.sname = ecal_sample_client.sname(); - client.sid = ecal_sample_client.sid(); - client.pid = static_cast(ecal_sample_client.pid()); - - // create unique client key - client.key = client.sname + ":" + client.sid + "@" + std::to_string(client.pid) + "@" + client.hname; - - // inform matching services - { - std::shared_lock lock(m_service_set_sync); - for (auto& iter : m_service_set) - { - if (iter->GetServiceName() == client.sname) - { - iter->RegisterClient(client.key, client); - } - } - } - } - void CServiceGate::RefreshRegistrations() { if (!m_created) return; // refresh service registrations - std::shared_lock lock(m_service_set_sync); - for (auto& iter : m_service_set) + std::shared_lock const lock(m_service_set_sync); + for (const auto& service_server_impl : m_service_set) { - iter->RefreshRegistration(); + service_server_impl->RefreshRegistration(); } } -}; +} diff --git a/ecal/core/src/service/ecal_servicegate.h b/src/core/src/service/ecal_servicegate.h similarity index 78% rename from ecal/core/src/service/ecal_servicegate.h rename to src/core/src/service/ecal_servicegate.h index 5ebb80e..4887461 100644 --- a/ecal/core/src/service/ecal_servicegate.h +++ b/src/core/src/service/ecal_servicegate.h @@ -25,16 +25,6 @@ #include "ecal_def.h" -#include - -#ifdef _MSC_VER -#pragma warning(push, 0) // disable proto warnings -#endif -#include -#ifdef _MSC_VER -#pragma warning(pop) -#endif - #include #include #include @@ -55,15 +45,13 @@ namespace eCAL bool Register (CServiceServerImpl* service_); bool Unregister(CServiceServerImpl* service_); - void ApplyClientRegistration(const eCAL::pb::Sample& ecal_sample_); - void RefreshRegistrations(); protected: - static std::atomic m_created; + static std::atomic m_created; - typedef std::set ServiceNameServiceImplSetT; + using ServiceNameServiceImplSetT = std::set; std::shared_timed_mutex m_service_set_sync; ServiceNameServiceImplSetT m_service_set; }; -}; +} diff --git a/src/core/src/time/ecal_time.cpp b/src/core/src/time/ecal_time.cpp new file mode 100644 index 0000000..e67d34d --- /dev/null +++ b/src/core/src/time/ecal_time.cpp @@ -0,0 +1,141 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL time interface +**/ + +#include + +#include +#include + +#if ECAL_CORE_TIMEGATE +#include "ecal_timegate.h" +#endif + +namespace eCAL +{ + namespace Time + { + std::string GetName() + { +#if ECAL_CORE_TIMEGATE + if ((g_timegate() != nullptr) && g_timegate()->IsValid()) + { + return(g_timegate()->GetName()); + } +#endif + return ""; + } + + long long GetMicroSeconds() + { +#if ECAL_CORE_TIMEGATE + if ((g_timegate() != nullptr) && g_timegate()->IsValid()) + { + return(g_timegate()->GetMicroSeconds()); + } +#endif + const std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + return(std::chrono::duration_cast(now.time_since_epoch()).count()); + } + + long long GetNanoSeconds() + { +#if ECAL_CORE_TIMEGATE + if ((g_timegate() != nullptr) && g_timegate()->IsValid()) + { + return(g_timegate()->GetNanoSeconds()); + } +#endif + const std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + return(std::chrono::duration_cast(now.time_since_epoch()).count()); + } + + bool SetNanoSeconds(long long time_) + { +#if ECAL_CORE_TIMEGATE + if ((g_timegate() != nullptr) && g_timegate()->IsValid()) + { + return(g_timegate()->SetNanoSeconds(time_)); + } +#endif + (void)time_; + return(false); + } + + bool IsSynchronized() + { +#if ECAL_CORE_TIMEGATE + if ((g_timegate() != nullptr) && g_timegate()->IsValid()) + { + return(g_timegate()->IsSynchronized()); + } +#endif + return(false); + } + + bool IsMaster() + { +#if ECAL_CORE_TIMEGATE + if ((g_timegate() != nullptr) && g_timegate()->IsValid()) + { + return(g_timegate()->IsMaster()); + } +#endif + return(false); + } + + void SleepForNanoseconds(long long duration_nsecs_) + { +#if ECAL_CORE_TIMEGATE + if ((g_timegate() != nullptr) && g_timegate()->IsValid()) + { + g_timegate()->SleepForNanoseconds(duration_nsecs_); + } +#endif + eCAL::Process::SleepFor(std::chrono::nanoseconds(duration_nsecs_)); + } + + void GetStatus(int& error_, std::string* const status_message_) + { +#if ECAL_CORE_TIMEGATE + if (g_timegate() == nullptr) + { + error_ = -1; + if (status_message_ != nullptr) + { + status_message_->assign("Timegate has not been initialized!"); + } + } + else + { + g_timegate()->GetStatus(error_, status_message_); + } +#else + error_ = -1; + if (status_message_ != nullptr) + { + status_message_->assign("Timegate functionality not available."); + } +#endif + } + } +} diff --git a/ecal/core/src/ecal_timegate.cpp b/src/core/src/time/ecal_timegate.cpp similarity index 94% rename from ecal/core/src/ecal_timegate.cpp rename to src/core/src/time/ecal_timegate.cpp index 9f206ac..681b30c 100644 --- a/ecal/core/src/ecal_timegate.cpp +++ b/src/core/src/time/ecal_timegate.cpp @@ -22,19 +22,15 @@ **/ #include -#include #include -#include "ecal_config_reader_hlp.h" +#include "config/ecal_config_reader_hlp.h" #include "ecal_def.h" -#include "ecal_process.h" #include "ecal_timegate.h" -#include "getenvvar.h" +#include "util/getenvvar.h" -#include #include -#include #define etime_initialize_name "etime_initialize" #define etime_finalize_name "etime_finalize" @@ -65,7 +61,7 @@ namespace eCAL m_successfully_loaded_replay(false), m_sync_mode(eTimeSyncMode::none) { - }; + } CTimeGate::~CTimeGate() { @@ -97,13 +93,13 @@ namespace eCAL } // initialize real time - if (!m_is_initialized_rt && m_time_sync_rt.etime_initialize_ptr) + if (!m_is_initialized_rt && (m_time_sync_rt.etime_initialize_ptr != nullptr)) { m_is_initialized_rt = m_time_sync_rt.etime_initialize_ptr() == 0; } // initialize replay - if (!m_is_initialized_replay && m_time_sync_replay.etime_initialize_ptr) + if (!m_is_initialized_replay && (m_time_sync_replay.etime_initialize_ptr != nullptr)) { m_is_initialized_replay = m_time_sync_replay.etime_initialize_ptr() == 0; } @@ -273,7 +269,7 @@ namespace eCAL { if (!m_created) { error_ = -1; - if (status_message_) { + if (status_message_ != nullptr) { status_message_->assign("eCAL Timegate has not been created."); } } @@ -284,19 +280,19 @@ namespace eCAL { case eTimeSyncMode::none: error_ = 0; - if (status_message_) { + if (status_message_ != nullptr) { status_message_->assign("Timesync mode is set to NONE."); } break; case eTimeSyncMode::realtime: if (!m_successfully_loaded_rt){ error_ = -1; - if (status_message_) { + if (status_message_ != nullptr) { status_message_->assign("Failed to load realtime timesync module "); status_message_->append(GetName()); } } else { - if (status_message_) { + if (status_message_ != nullptr) { char buffer[buffer_len]; buffer[0] = 0x0; m_time_sync_rt.etime_get_status_ptr(&error_, buffer, buffer_len); @@ -311,12 +307,12 @@ namespace eCAL case eTimeSyncMode::replay: if (!m_successfully_loaded_replay){ error_ = -1; - if (status_message_) { + if (status_message_ != nullptr) { status_message_->assign("Failed to load realtime timesync module "); status_message_->append(GetName()); } } else { - if (status_message_) { + if (status_message_ != nullptr) { char buffer[buffer_len]; buffer[0] = 0x0; m_time_sync_replay.etime_get_status_ptr(&error_, buffer, buffer_len); @@ -330,7 +326,7 @@ namespace eCAL break; default: error_ = -1; - if (status_message_) { + if (status_message_ != nullptr) { status_message_->assign("Unknown Error."); } } @@ -379,7 +375,7 @@ namespace eCAL module_name = "lib" + module_name + ".dylib"; #endif // __APPLE__ - if (!interface_.module_handle) + if (interface_.module_handle == nullptr) { #ifdef _WIN32 // try to load plugin from paths that are specified in the time plugin environment variable @@ -387,18 +383,17 @@ namespace eCAL { const auto module_path = ecal_time_plugin_path + "\\" + module_name; interface_.module_handle = LoadLibrary(module_path.c_str()); - if (interface_.module_handle) break; + if (interface_.module_handle != nullptr) break; } // try to load plugin in standard path - if (!interface_.module_handle) + if (interface_.module_handle == nullptr) { - const auto module_path = module_name; - interface_.module_handle = LoadLibrary(module_path.c_str()); + interface_.module_handle = LoadLibrary(module_name.c_str()); } // try to load plugin from sub folder "ecal_time_plugin_dir" - if (!interface_.module_handle) + if (interface_.module_handle == nullptr) { const auto module_path = std::string(ecal_time_plugin_dir) + "\\" + module_name; interface_.module_handle = LoadLibrary(module_path.c_str()); @@ -421,7 +416,7 @@ namespace eCAL } #endif // defined(__linux__) || defined(__APPLE__) - if (!interface_.module_handle) + if (interface_.module_handle == nullptr) { eCAL::Logging::Log(log_level_error, "Could not load eCAL time sync module " + module_name); return false; @@ -469,4 +464,4 @@ namespace eCAL return true; } -}; +} diff --git a/ecal/core/src/ecal_timegate.h b/src/core/src/time/ecal_timegate.h similarity index 98% rename from ecal/core/src/ecal_timegate.h rename to src/core/src/time/ecal_timegate.h index 01615b6..8a11e96 100644 --- a/ecal/core/src/ecal_timegate.h +++ b/src/core/src/time/ecal_timegate.h @@ -81,7 +81,7 @@ namespace eCAL void SleepForNanoseconds(long long duration_nsecs_); - void GetStatus(int& error_, std::string* const status_message_); + void GetStatus(int& error_, std::string* status_message_); bool IsValid(); eTimeSyncMode GetSyncMode() { return(m_sync_mode); }; @@ -132,4 +132,4 @@ namespace eCAL STimeDllInterface m_time_sync_rt; STimeDllInterface m_time_sync_replay; }; -}; +} diff --git a/ecal/core/src/ecal_timer.cpp b/src/core/src/time/ecal_timer.cpp similarity index 91% rename from ecal/core/src/ecal_timer.cpp rename to src/core/src/time/ecal_timer.cpp index 1d1d224..bde258a 100644 --- a/ecal/core/src/ecal_timer.cpp +++ b/src/core/src/time/ecal_timer.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include namespace eCAL { @@ -38,6 +38,10 @@ namespace eCAL CTimerImpl(const int timeout_, TimerCallbackT callback_, const int delay_) : m_stop(false), m_running(false) { Start(timeout_, callback_, delay_); } virtual ~CTimerImpl() { Stop(); } + CTimerImpl(const CTimerImpl&) = delete; + CTimerImpl& operator=(const CTimerImpl&) = delete; + CTimerImpl(CTimerImpl&& rhs) = delete; + CTimerImpl& operator=(CTimerImpl&& rhs) = delete; bool Start(const int timeout_, TimerCallbackT callback_, const int delay_) { @@ -66,7 +70,7 @@ namespace eCAL if (callback_ == nullptr) return; if (delay_ > 0) eCAL::Time::sleep_for(std::chrono::milliseconds(delay_)); - std::chrono::nanoseconds loop_duration((long long)timeout_ * 1000LL * 1000LL); + const std::chrono::nanoseconds loop_duration((long long)timeout_ * 1000LL * 1000LL); m_last_error = std::chrono::nanoseconds(0); while (!m_stop) @@ -131,19 +135,19 @@ namespace eCAL CTimer::CTimer() : m_timer(nullptr) { - m_timer = new CTimerImpl(); + m_timer = std::make_unique(); } CTimer::CTimer(const int timeout_, TimerCallbackT callback_, const int delay_ /*= 0*/) : m_timer(nullptr) { - m_timer = new CTimerImpl(); + m_timer = std::make_unique(); m_timer->Start(timeout_, callback_, delay_); } CTimer::~CTimer() { Stop(); - delete m_timer; + m_timer.reset(); } bool CTimer::Start(const int timeout_, TimerCallbackT callback_, const int delay_ /*= 0*/) diff --git a/ecal/core/src/custom_tclap/advanced_tclap_output.cpp b/src/core/src/util/advanced_tclap_output.cpp similarity index 91% rename from ecal/core/src/custom_tclap/advanced_tclap_output.cpp rename to src/core/src/util/advanced_tclap_output.cpp index c56cfb0..58f7d6a 100644 --- a/ecal/core/src/custom_tclap/advanced_tclap_output.cpp +++ b/src/core/src/util/advanced_tclap_output.cpp @@ -17,7 +17,7 @@ * ========================= eCAL LICENSE ================================= */ -#include +#include "advanced_tclap_output.h" #include @@ -34,8 +34,8 @@ namespace CustomTclap void AdvancedTclapOutput::version(TCLAP::CmdLineInterface &cmd) { - std::string progName = cmd.getProgramName(); - std::string xversion = cmd.getVersion(); + const std::string progName = cmd.getProgramName(); + const std::string xversion = cmd.getVersion(); std::stringstream ss; @@ -75,7 +75,7 @@ namespace CustomTclap // Create string - std::string progName = cmd.getProgramName(); + const std::string progName = cmd.getProgramName(); ss << "PARSE ERROR: " << e.argId() << std::endl << " " << e.error() << std::endl << std::endl; @@ -117,7 +117,7 @@ namespace CustomTclap void AdvancedTclapOutput::shortUsage(TCLAP::CmdLineInterface& cmd, std::ostream& os) const { std::list arg_list = cmd.getArgList(); - std::string prog_name = cmd.getProgramName(); + const std::string prog_name = cmd.getProgramName(); TCLAP::XorHandler xor_handler = cmd.getXorHandler(); std::vector> xor_list = xor_handler.getXorList(); @@ -159,7 +159,7 @@ namespace CustomTclap void AdvancedTclapOutput::longUsage(TCLAP::CmdLineInterface& cmd, std::ostream& os) const { std::list arg_list = cmd.getArgList(); - std::string message = cmd.getMessage(); + const std::string message = cmd.getMessage(); TCLAP::XorHandler xor_handler = cmd.getXorHandler(); std::vector> xor_list = xor_handler.getXorList(); @@ -201,14 +201,14 @@ namespace CustomTclap std::vector> cleaned_xor_list; // Remove hidden arguments from XOR list - for (size_t i = 0; i < xor_list.size(); i++) + for (const auto & i : xor_list) { std::vector arg_list; - for (size_t j = 0; j < xor_list[i].size(); j++) + for (size_t j = 0; j < i.size(); j++) { - if (hidden_arguments_.find(xor_list[i][j]) == hidden_arguments_.end()) + if (hidden_arguments_.find(i[j]) == hidden_arguments_.end()) { - arg_list.push_back(xor_list[i][j]); + arg_list.push_back(i[j]); } } diff --git a/ecal/core/src/custom_tclap/advanced_tclap_output.h b/src/core/src/util/advanced_tclap_output.h similarity index 94% rename from ecal/core/src/custom_tclap/advanced_tclap_output.h rename to src/core/src/util/advanced_tclap_output.h index 5415eb4..f640eb3 100644 --- a/ecal/core/src/custom_tclap/advanced_tclap_output.h +++ b/src/core/src/util/advanced_tclap_output.h @@ -19,6 +19,7 @@ #pragma once +#include #include #include @@ -69,9 +70,9 @@ namespace CustomTclap */ AdvancedTclapOutput(std::ostream* output_stream, int max_width); - virtual void version(TCLAP::CmdLineInterface &cmd) override; - virtual void usage(TCLAP::CmdLineInterface &cmd) override; - virtual void failure(TCLAP::CmdLineInterface &cmd, TCLAP::ArgException &e) override; + void version(TCLAP::CmdLineInterface &cmd) override; + void usage(TCLAP::CmdLineInterface &cmd) override; + void failure(TCLAP::CmdLineInterface &cmd, TCLAP::ArgException &e) override; /** * @brief Hides / un-hides an argument diff --git a/ecal/core/src/ecal_expmap.h b/src/core/src/util/ecal_expmap.h similarity index 69% rename from ecal/core/src/ecal_expmap.h rename to src/core/src/util/ecal_expmap.h index 7922c62..ba3959e 100644 --- a/ecal/core/src/ecal_expmap.h +++ b/src/core/src/util/ecal_expmap.h @@ -45,69 +45,94 @@ namespace eCAL class CExpMap { public: - typedef std::chrono::steady_clock clock_type; + using clock_type = std::chrono::steady_clock; // Key access history, most recent at back - typedef std::list> key_tracker_type; + using key_tracker_type = std::list>; // Key to value and key history iterator - typedef std::map< - Key, - std::pair< - T, - typename key_tracker_type::iterator - > - > key_to_value_type; - - typedef Alloc allocator_type; - typedef std::pair value_type; - typedef typename Alloc::reference reference; - typedef typename Alloc::const_reference const_reference; - typedef typename Alloc::difference_type difference_type; - typedef typename Alloc::size_type size_type; - typedef Key key_type; - typedef T mapped_type; + using key_to_value_type = std::map>; + + using allocator_type = Alloc; + using value_type = std::pair; + using reference = typename Alloc::reference; + using const_reference = typename Alloc::const_reference; + using difference_type = typename Alloc::difference_type; + using size_type = typename Alloc::size_type; + using key_type = Key; + using mapped_type = T; class iterator : public std::iterator> - //class iterator : public std::iterator { + friend class const_iterator; + public: - iterator(const iterator& i) - : it(i.it) - {}; iterator(const typename key_to_value_type::iterator _it) : it(_it) - {}; - ~iterator() = default; - iterator& operator=(const iterator& i) - { - it = i.it; - return *this; - }; + {} + iterator& operator++() { it++; return *this; - }; //prefix increment + } //prefix increment + iterator& operator--() { it--; return *this; - }; //prefix decrement - //reference operator*() const + } //prefix decrement + std::pair operator*() const { return std::make_pair(it->first, it->second.first); - }; + } + //friend void swap(iterator& lhs, iterator& rhs); //C++11 I think - bool operator==(const iterator& rhs) const { return it == rhs.it; }; - bool operator!=(const iterator& rhs) const { return it != rhs.it; }; - + bool operator==(const iterator& rhs) const { return it == rhs.it; } + bool operator!=(const iterator& rhs) const { return it != rhs.it; } private: typename key_to_value_type::iterator it; - mutable value_type current_ref; + }; + + class const_iterator : public std::iterator> + { + public: + const_iterator(const iterator& other) + : it(other.it) + {} + + const_iterator(const typename key_to_value_type::const_iterator _it) + : it(_it) + {} + + const_iterator& operator++() + { + it++; + return *this; + } //prefix increment + + const_iterator& operator--() + { + it--; + return *this; + } //prefix decrement + //reference operator*() const + + std::pair operator*() const + { + return std::make_pair(it->first, it->second.first); + } + + //friend void swap(iterator& lhs, iterator& rhs); //C++11 I think + bool operator==(const const_iterator& rhs) const { return it == rhs.it; } + bool operator!=(const const_iterator& rhs) const { return it != rhs.it; } + + private: + typename key_to_value_type::const_iterator it; }; + // Constructor specifies the timeout of the map CExpMap() : _timeout(std::chrono::milliseconds(5000)) {}; CExpMap(clock_type::duration t) : _timeout(t) {}; @@ -121,23 +146,42 @@ namespace eCAL iterator begin() noexcept { return iterator(_key_to_value.begin()); - }; + } iterator end() noexcept { return iterator(_key_to_value.end()); - }; + } + + const_iterator begin() const noexcept + { + return const_iterator(_key_to_value.begin()); + } + + const_iterator end() const noexcept + { + return const_iterator(_key_to_value.end()); + } + + // Const begin and end functions + const_iterator cbegin() const noexcept { + return const_iterator(_key_to_value.cbegin()); + } + + const_iterator cend() const noexcept { + return const_iterator(_key_to_value.cend()); + } // Capacity bool empty() const noexcept { return _key_to_value.empty(); - }; + } size_type size() const noexcept { return _key_to_value.size(); - }; + } size_type max_size() const noexcept { @@ -175,25 +219,30 @@ namespace eCAL mapped_type& at(const key_type& k) { return _key_to_value.at(k).first; - }; + } const mapped_type& at(const key_type& k) const { return _key_to_value.at(k).first; - }; + } // Modifiers std::pair insert(const value_type& val) { auto result = insert(val.first, val.second); return std::make_pair(iterator(result.first), result.second); - }; + } // Operations iterator find(const key_type& k) { return iterator(_key_to_value.find(k)); - }; + } + + const_iterator find(const Key& k) const + { + return const_iterator(_key_to_value.find(k)); + } // Purge the timed out elements from the cache void remove_deprecated(std::list* key_erased = nullptr) //-V826 @@ -210,7 +259,20 @@ namespace eCAL _key_to_value.erase(it->second); // erase the element from the map it = _key_tracker.erase(it); // erase the element from the list } - }; + } + + // Remove specific element from the cache + bool erase(const Key& k) + { + auto it = _key_to_value.find(k); + if (it != _key_to_value.end()) + { + _key_tracker.erase(it->second.second); // erase the element from the list + _key_to_value.erase(k); // erase the element from the map + return true; + } + return false; + } // Remove all elements from the cache void clear() @@ -224,7 +286,7 @@ namespace eCAL _key_to_value.erase(it->second); // erase the element from the map it = _key_tracker.erase(it); // erase the element from the list } - }; + } private: diff --git a/src/core/src/util/ecal_thread.h b/src/core/src/util/ecal_thread.h new file mode 100644 index 0000000..49aec75 --- /dev/null +++ b/src/core/src/util/ecal_thread.h @@ -0,0 +1,123 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL threading helper class +**/ + +#include +#include +#include +#include +#include + +#pragma once + +namespace eCAL +{ + /** + * @brief A class that encapsulates threaded functionality with a callback interface. + */ + class CCallbackThread + { + public: + /** + * @brief Constructor for the CallbackThread class. + * @param callback A callback function to be executed in the CallbackThread thread. + */ + CCallbackThread(std::function callback) + : callback_(callback) {} + + ~CCallbackThread() + { + stop(); + } + + CCallbackThread(const CCallbackThread&) = delete; + CCallbackThread& operator=(const CCallbackThread&) = delete; + CCallbackThread(CCallbackThread&& rhs) = delete; + CCallbackThread& operator=(CCallbackThread&& rhs) = delete; + + /** + * @brief Start the callback thread with a specified timeout. + * @param timeout The timeout duration for waiting in the callback thread. + */ + template + void start(DurationType timeout) + { + callbackThread_ = std::thread(&CCallbackThread::callbackFunction, this, timeout); + } + + /** + * @brief Stop the callback thread. + * Waits for the callback thread to finish its work. + */ + void stop() + { + { + const std::unique_lock lock(mtx_); + // Set the flag to signal the callback thread to stop + stopThread_ = true; + // Notify the callback thread to wake up and check the flag + cv_.notify_one(); + } + + // Wait for the callback thread to finish + if (callbackThread_.joinable()) { + callbackThread_.join(); + } + } + + private: + std::thread callbackThread_; /**< The callback thread object. */ + std::function callback_; /**< The callback function to be executed in the callback thread. */ + std::mutex mtx_; /**< Mutex for thread synchronization. */ + std::condition_variable cv_; /**< Condition variable for signaling between threads. */ + bool stopThread_{false}; /**< Flag to indicate whether the callback thread should stop. */ + + /** + * @brief Callback function that runs in the callback thread. + * Periodically checks the stopThread flag and executes the callback function. + * @tparam DurationType The type of the timeout duration (e.g., std::chrono::seconds, std::chrono::milliseconds). + * @param timeout The timeout duration for waiting in the callback thread. + */ + template + void callbackFunction(DurationType timeout) + { + while (true) + { + { + std::unique_lock lock(mtx_); + // Wait for a signal or a timeout + if (cv_.wait_for(lock, timeout, [this] { return stopThread_; })) + { + // If the stopThread flag is true, break out of the loop + break; + } + } + + // Do some work in the callback thread + if (callback_) + { + callback_(); + } + } + } + }; +} diff --git a/ecal/core/src/getenvvar.h b/src/core/src/util/getenvvar.h similarity index 99% rename from ecal/core/src/getenvvar.h rename to src/core/src/util/getenvvar.h index fc16f55..cc226d2 100644 --- a/ecal/core/src/getenvvar.h +++ b/src/core/src/util/getenvvar.h @@ -63,4 +63,4 @@ inline std::vector splitPaths(const std::string& paths_value) tokens.push_back(token); } return tokens; -}; +} diff --git a/ecal/core/src/win32/dll/dllmain.cpp b/src/core/src/win32/dll/dllmain.cpp similarity index 100% rename from ecal/core/src/win32/dll/dllmain.cpp rename to src/core/src/win32/dll/dllmain.cpp diff --git a/ecal/core/src/win32/dll/ecal.rc b/src/core/src/win32/dll/ecal.rc similarity index 100% rename from ecal/core/src/win32/dll/ecal.rc rename to src/core/src/win32/dll/ecal.rc diff --git a/ecal/core/src/win32/dll/resource.h b/src/core/src/win32/dll/resource.h similarity index 100% rename from ecal/core/src/win32/dll/resource.h rename to src/core/src/win32/dll/resource.h diff --git a/src/protobuf/ecal.proto b/src/protobuf/ecal.proto new file mode 100644 index 0000000..bf1ba16 --- /dev/null +++ b/src/protobuf/ecal.proto @@ -0,0 +1,71 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +import "host.proto"; +import "process.proto"; +import "service.proto"; +import "topic.proto"; + +package eCAL.pb; + +message Content // topic content +{ + int64 id = 1; // sample id + int64 clock = 2; // internal used clock + int64 time = 3; // time the content was updated + int64 hash = 7; // unique hash for that sample + int32 size = 6; // size (additional for none payload "header only samples") + bytes payload = 4; // octet stream +} + +enum eCmdType // command type +{ + bct_none = 0; // undefined command + bct_set_sample = 1; // set sample content + bct_reg_publisher = 2; // register publisher + bct_reg_subscriber = 3; // register subscriber + bct_reg_process = 4; // register process + bct_reg_service = 5; // register service + bct_reg_client = 6; // register client + + bct_unreg_publisher = 12; // unregister publisher + bct_unreg_subscriber = 13; // unregister subscriber + bct_unreg_process = 14; // unregister process + bct_unreg_service = 15; // unregister service + bct_unreg_client = 16; // unregister client +} + +message Sample // a sample is a topic, it's descriptions and it's content +{ + eCmdType cmd_type = 1; // sample command type + Host host = 2; // host information + Process process = 3; // process information + Service service = 4; // service information + Client client = 7; // client information + Topic topic = 5; // topic information + Content content = 6; // topic content + bytes padding = 8; // 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 +} + +message SampleList +{ + repeated Sample samples = 1; // list of Samples used currently by SHM registration +} diff --git a/samples/cpp/person/person_snd_dyn/src/protobuf/house.proto b/src/protobuf/host.proto similarity index 71% rename from samples/cpp/person/person_snd_dyn/src/protobuf/house.proto rename to src/protobuf/host.proto index fbe8cce..f16fdfe 100644 --- a/samples/cpp/person/person_snd_dyn/src/protobuf/house.proto +++ b/src/protobuf/host.proto @@ -19,9 +19,15 @@ syntax = "proto3"; -package pb.Environment; +package eCAL.pb; -message House +message OSInfo // operating system details { - int32 rooms = 1; + string osname = 1; // name +} + +message Host // eCAL host +{ + string hname = 1; // host name + OSInfo os = 2; // operating system details } diff --git a/src/protobuf/layer.proto b/src/protobuf/layer.proto new file mode 100644 index 0000000..d6de62d --- /dev/null +++ b/src/protobuf/layer.proto @@ -0,0 +1,64 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +package eCAL.pb; + +message LayerParUdpMC +{ +} + +message LayerParShm +{ + repeated string memory_file_list = 1; // list of memory file names +} + +message LayerParInproc +{ +} + +message LayerParTcp +{ + int32 port = 1; // tcp writers port number +} + +message ConnnectionPar // connection parameter for reader / writer +{ + LayerParUdpMC layer_par_udpmc = 1; // parameter for ecal udp multicast + LayerParShm layer_par_shm = 2; // parameter for ecal shared memory + LayerParTcp layer_par_tcp = 4; // parameter for ecal tcp +} + +enum eTLayerType // transport layer +{ + tl_none = 0; // undefined + tl_ecal_udp_mc = 1; // ecal udp multicast + tl_ecal_shm = 4; // ecal shared memory + tl_ecal_tcp = 5; // ecal tcp + tl_all = 255; // all layer +} + +message TLayer +{ + eTLayerType type = 1; // transport layer type + int32 version = 2; // transport layer version + bool confirmed = 3; // transport layer used ? + ConnnectionPar par_layer = 5; // transport layer parameter +} diff --git a/ecal/core_pb/src/ecal/core/pb/monitoring.proto b/src/protobuf/logging.proto similarity index 62% rename from ecal/core_pb/src/ecal/core/pb/monitoring.proto rename to src/protobuf/logging.proto index 86dce35..3957665 100644 --- a/ecal/core_pb/src/ecal/core/pb/monitoring.proto +++ b/src/protobuf/logging.proto @@ -19,14 +19,9 @@ syntax = "proto3"; -import "ecal/core/pb/host.proto"; -import "ecal/core/pb/process.proto"; -import "ecal/core/pb/service.proto"; -import "ecal/core/pb/topic.proto"; - package eCAL.pb; -message LogMessage // eCAL monitoring log message +message LogMessage // eCAL log message { int64 time = 1; // time string hname = 2; // host name @@ -37,16 +32,7 @@ message LogMessage // eCAL monitoring log message string content = 7; // message content } -message Monitoring // eCAL monitoring information -{ - repeated Host hosts = 1; // hosts - repeated Process processes = 2; // processes - repeated Service services = 3; // services - repeated Client clients = 5; // clients - repeated Topic topics = 4; // topics -} - -message Logging // eCAL logging information +message LogMessageList // eCAL log message list { - repeated LogMessage logs = 1; // log messages + repeated LogMessage log_messages = 1; // log messages } diff --git a/samples/cpp/person/person_snd_inproc/src/protobuf/person.proto b/src/protobuf/monitoring.proto similarity index 61% rename from samples/cpp/person/person_snd_inproc/src/protobuf/person.proto rename to src/protobuf/monitoring.proto index 4200a43..790e74f 100644 --- a/samples/cpp/person/person_snd_inproc/src/protobuf/person.proto +++ b/src/protobuf/monitoring.proto @@ -19,24 +19,18 @@ syntax = "proto3"; -import "animal.proto"; -import "house.proto"; +import "host.proto"; +import "process.proto"; +import "service.proto"; +import "topic.proto"; -package pb.People; +package eCAL.pb; -message Person +message Monitoring // eCAL monitoring information { - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - - Animal.Dog dog = 5; - Environment.House house = 6; + repeated Host hosts = 1; // hosts + repeated Process processes = 2; // processes + repeated Service services = 3; // services + repeated Client clients = 5; // clients + repeated Topic topics = 4; // topics } diff --git a/src/protobuf/process.proto b/src/protobuf/process.proto new file mode 100644 index 0000000..2c104e8 --- /dev/null +++ b/src/protobuf/process.proto @@ -0,0 +1,74 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +package eCAL.pb; + +enum eProcessSeverity // process severity +{ + proc_sev_unknown = 0; // condition unknown + proc_sev_healthy = 1; // process healthy + proc_sev_warning = 2; // process warning level + proc_sev_critical = 3; // process critical + proc_sev_failed = 4; // process failed +} + +enum eProcessSeverityLevel // process severity level +{ + proc_sev_level_unknown = 0; // condition unknown + proc_sev_level1 = 1; // default severity level 1 + proc_sev_level2 = 2; // severity level 2 + proc_sev_level3 = 3; // severity level 3 + proc_sev_level4 = 4; // severity level 4 + proc_sev_level5 = 5; // severity level 5 +} + +message ProcessState // process state +{ + eProcessSeverity severity = 1; // severity + eProcessSeverityLevel severity_level = 3; // severity level + string info = 2; // info string +} + +enum eTSyncState // time synchronisation +{ + tsync_none = 0; // not synchronized + tsync_realtime = 1; // real time sync mode + tsync_replay = 2; // replay time sync mode +} + +message Process // process +{ + int32 rclock = 1; // registration clock + string hname = 2; // host name + string hgname = 18; // host group name + int32 pid = 3; // process id + string pname = 4; // process name + string uname = 5; // unit name + string pparam = 6; // process parameter + int64 datawrite = 10; // data write bytes per sec + int64 dataread = 11; // data read bytes per sec + ProcessState state = 12; // process state info + eTSyncState tsync_state = 13; // time synchronization state + string tsync_mod_name = 14; // time synchronization module name + int32 component_init_state = 15; // eCAL component initialization state (eCAL::Initialize(..)) + string component_init_info = 16; // like comp_init_state as human readable string (pub|sub|srv|mon|log|time|proc) + string ecal_runtime_version = 17; // loaded / runtime eCAL version of a component +} diff --git a/src/protobuf/service.proto b/src/protobuf/service.proto new file mode 100644 index 0000000..6c4b591 --- /dev/null +++ b/src/protobuf/service.proto @@ -0,0 +1,94 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +package eCAL.pb; + +message ServiceHeader +{ + enum eCallState + { + none = 0; + executed = 1; + failed = 2; + } + + string hname = 1; // host name + string sname = 2; // service name + string sid = 7; // service id + string mname = 3; // method name + string error = 4; // error message + int32 id = 5; // session id + eCallState state = 6; // method call state +} + +message Request // client request +{ + ServiceHeader header = 1; // common service header + bytes request = 2; // request payload +} + +message Response // server response +{ + ServiceHeader header = 1; // common service header + bytes response = 2; // response payload + int64 ret_state = 3; // callback return state +} + +message Method // method +{ + string mname = 1; // method name + string req_type = 2; // request type + bytes req_desc = 5; // request descriptor + string resp_type = 3; // response type + bytes resp_desc = 6; // response descriptor + int64 call_count = 4; // call counter +} + +message Service // service +{ + int32 rclock = 1; // registration clock + string hname = 2; // host name + string pname = 3; // process name + string uname = 4; // unit name + int32 pid = 5; // process id + string sname = 6; // service name + string sid = 9; // service id + repeated Method methods = 8; // list of methods + + // transport specific parameter (for internal use) + uint32 version = 10; // service protocol version + uint32 tcp_port_v0 = 7; // the tcp port used for that service + uint32 tcp_port_v1 = 11; // the tcp port used for that service +} + +message Client // client +{ + int32 rclock = 1; // registration clock + string hname = 2; // host name + string pname = 3; // process name + string uname = 4; // unit name + int32 pid = 5; // process id + string sname = 6; // service name + string sid = 7; // service id + + // transport specific parameter (for internal use) + uint32 version = 8; // client protocol version +} diff --git a/src/protobuf/topic.proto b/src/protobuf/topic.proto new file mode 100644 index 0000000..5228d7f --- /dev/null +++ b/src/protobuf/topic.proto @@ -0,0 +1,61 @@ +/* ========================= 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 ================================= +*/ + +syntax = "proto3"; + +import "layer.proto"; + +package eCAL.pb; + +message DataTypeInformation +{ + string name = 1; // name of the datatype + string encoding = 2; // encoding of the datatype (e.g. protobuf, flatbuffers, capnproto) + bytes desc = 3; // descriptor information of the datatype (necessary for reflection) +} + +message Topic // eCAL topic +{ + int32 rclock = 1; // registration clock (heart beat) + string hname = 2; // host name + string hgname = 28; // host group name + int32 pid = 3; // process id + string pname = 4; // process name + string uname = 5; // unit name + string tid = 6; // topic id + string tname = 7; // topic name + string direction = 8; // direction (publisher, subscriber) + string ttype = 9; // topic type + topic encoding (deprecated) + bytes tdesc = 10; // topic description (protocol descriptor) (deprecated) + + DataTypeInformation tdatatype = 30; // topic datatype information (encoding & type & description) + + repeated TLayer tlayer = 12; // active topic transport layers and it's specific parameter + int32 tsize = 13; // topic size + + int32 connections_loc = 16; // number of local connected entities + int32 connections_ext = 17; // number of external connected entities + int32 message_drops = 18; // dropped messages + + int64 did = 19; // data send id (publisher setid) + int64 dclock = 20; // data clock (send / receive action) + int32 dfreq = 21; // data frequency (send / receive samples per second) [mHz] + + map attr = 27; // generic topic description +} diff --git a/samples/cpp/misc/timer/CMakeLists.txt b/src/service/CMakeLists.txt similarity index 59% rename from samples/cpp/misc/timer/CMakeLists.txt rename to src/service/CMakeLists.txt index f9d2a81..83a34bb 100644 --- a/samples/cpp/misc/timer/CMakeLists.txt +++ b/src/service/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2023 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,24 +16,15 @@ # # ========================= eCAL LICENSE ================================= -cmake_minimum_required(VERSION 3.10) +# Main library +add_subdirectory(ecal_service) -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +# Samples +if(ECAL_CORE_BUILD_SAMPLES) + add_subdirectory(sample) +endif() -project(timer) - -find_package(eCAL REQUIRED) - -set(timer_src - src/timer.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${timer_src}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/misc) +# Tests +if(ECAL_CORE_BUILD_TESTS) + add_subdirectory(test) +endif() \ No newline at end of file diff --git a/src/service/Readme.md b/src/service/Readme.md new file mode 100644 index 0000000..425b383 --- /dev/null +++ b/src/service/Readme.md @@ -0,0 +1,177 @@ +# ecal-service + +The eCAL Service lib encapsulates the binary data exchange of eCAL Servcies + +## API Concept + +- All classes are `std::shared_ptr` based. The constructors cannot be accessed, but an instanace of the class can be created by the static `::create(...)` methods. +- ASIO is exposed through the public API. It is the reponsibility of the user to create an `io_context` and have a thread executing it. +- Servers and Client should be created through the `ServerManager` and `ClientManager` classes. Those classes automatically keep the `io_context` alive. +- Having a running `io_context` is crucial for proper operation. Stopping the `io_context` can cause deadlocks. +- Instead of stopping the io_context, the user should stop their `ServerManager` and `ClientManager`. This will properly shutdown all Servers and Clients and let the `io_context` run out of work. The threads executing it can then be joined. +- There are no interal threads, only the thread(s) that the user created for executing the `io_context`. All callbacks are executed by the `io_context`, meaning that a long running callback can have a bad impact on everything else that was created with that `io_context`. + +## Example Code + +The following code illustrates the usage of eCAL::service in a separate server and client program. + +### Service Server + +```cpp +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + // Create an io_context + auto io_context = std::make_shared(); + + // Create a server manager + auto server_manager = eCAL::service::ServerManager::create(io_context); + + // Create and start an io_context thread. + // The io_context will be stopped, when the server_manager is stopped. + std::thread io_context_thread([&io_context]() { io_context->run(); }); + + // Server Service callback + // + // This callback will be called, when a client calls the service. + // It is responsible for filling the response object. + auto server_service_callback + = [](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + *response = "Response on \"" + *request + "\""; + }; + + // Event callback (empty) + auto server_event_callback = [](eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) {}; + + // Create server on port 1589 + auto server = server_manager->create_server(1, 1589, server_service_callback, true, server_event_callback); + + // Just don't exit + while(true) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // Use managers to stop servers and clients + server_manager->stop_servers(); + + // Join the io_context thread + io_context_thread.join(); +} +``` + +### Service Client + +```cpp +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + // Create an io_context + auto io_context = std::make_shared(); + + // Create a client manager + auto client_manager = eCAL::service::ClientManager::create(io_context); + + // Create and start an io_context thread. + // The io_context will be stopped, when the client_manager is stopped. + std::thread io_context_thread([&io_context]() { io_context->run(); }); + + + // Client Callback + // + // This callback will be called, when the service call is finished. + auto client_response_callback + = [](const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + if (error) + std::cerr << "Error calling service: " << error.ToString() << std::endl; + else + std::cout << "Received response: " << *response << std::endl; + }; + + // Event callback (empty) + auto client_event_callback = [](eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) {}; + + // Create client that connects to port 1589 + auto client = client_manager->create_client(1, "127.0.0.1", 1589, client_event_callback); + + // Call the service non-blocking. The response will be passed to the callback. + for (int i = 1; i <= 10; i++) + { + const auto request = std::make_shared("Hello World " + std::to_string(i)); + client->async_call_service(request, client_response_callback); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // Use managers to stop the clients + client_manager->stop_clients(); + + // Join the io_context thread + io_context_thread.join(); +} +``` + +## The protocol + +Currently, 2 protocols are supported: + +1. **Version 0**: This is a buggy legacy version, that is only kept for compatibility. It cannot be fixed while staying compatible. +2. **Version 1**: This is the fixed proper version, that is incompatible to version 0, though. It incorporates a protocol handshake while establishing the connection and communicates the version of the used protocol. Therefore, this version is expected to be downward compatible in the future. + +The user has to select manually, which protocol has be to used. + +All native messages are described in [`protocol_layout.h`](ecal_service/src/protocol_layout.h). Multi-byte datatypes are always sent in network-byte-order (Big Endian). + +## Version 1 + +- Client connects to Server. +- Client sends a ProtocolHandshakeRequest containing the maximum and minimum supported protocol version. +- Server selects one version and returns the selected protocol version as ProtocolHandshakeResponse. + - If no common protocol version is found, the server closes the connection, instead. +- The Client now sends Requests to the Server, which is always followed by a Response sent by the Server to the Client. + +All messages are prepended whith a header telling the other party about the message type and payload size. The request and response payloads are plain binary data. + +``` +Server Client + | | + | <- ProtocolHandshakeReq <- | + | -> ProtocolHandshakeResp -> | + | | + | <------- Request --------- | + | -------- Response --------> | + | | + | <------- Request --------- | + | -------- Response --------> | + | ... | +``` + +## Version 0 + +- Client connects to Server. +- _There is no handshake_ +- The Client now sends Requests to the Server, which is always followed by a Response sent by the Server to the Client. + +The Request message is prepended by a header telling the server about the payload size. +The Response message does not have any header, making it virtually impossible to tell whether more data is to be expected or not. + +``` +Server Client + | | + | <------- Request --------- | + | -------- Response --------> | + | | + | <------- Request --------- | + | -------- Response --------> | + | ... | +``` \ No newline at end of file diff --git a/src/service/ecal_service/CMakeLists.txt b/src/service/ecal_service/CMakeLists.txt new file mode 100644 index 0000000..61fefff --- /dev/null +++ b/src/service/ecal_service/CMakeLists.txt @@ -0,0 +1,104 @@ +cmake_minimum_required(VERSION 3.5.1) + +project(ecal_service) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Disable default export of symbols +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) + +find_package(Threads REQUIRED) +find_package(asio REQUIRED) + +# Public API include directory +set (includes + include/ecal/service/client_manager.h + include/ecal/service/client_session.h + include/ecal/service/client_session_types.h + include/ecal/service/error.h + include/ecal/service/logger.h + include/ecal/service/server.h + include/ecal/service/server_manager.h + include/ecal/service/server_session_types.h + include/ecal/service/state.h +) + +# Private source files +set(sources + src/client_manager.cpp + src/client_session.cpp + src/client_session_impl_base.h + src/client_session_impl_v0.cpp + src/client_session_impl_v0.h + src/client_session_impl_v1.cpp + src/client_session_impl_v1.h + src/condition_variable_signaler.h + src/log_defs.h + src/log_helpers.h + src/protocol_layout.h + src/protocol_v0.cpp + src/protocol_v0.h + src/protocol_v1.cpp + src/protocol_v1.h + src/server.cpp + src/server_impl.cpp + src/server_impl.h + src/server_manager.cpp + src/server_session_impl_base.h + src/server_session_impl_v0.cpp + src/server_session_impl_v0.h + src/server_session_impl_v1.cpp + src/server_session_impl_v1.h +) + +# Build as object library +add_library (${PROJECT_NAME} OBJECT + ${includes} + ${sources} +) + +target_link_libraries(${PROJECT_NAME} + PUBLIC + # Link header-only libs (asio & recycle) as described in this workaround: + # https://gitlab.kitware.com/cmake/cmake/-/issues/15415#note_633938 + $ + PRIVATE + Threads::Threads + $<$:ws2_32> + $<$:wsock32> + +) + +target_compile_definitions(${PROJECT_NAME} + PRIVATE + ASIO_STANDALONE + _WIN32_WINNT=0x0601 +) + +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) + +target_compile_options(${PROJECT_NAME} PRIVATE + $<$,$,$>: + -Wall -Wextra>) + +# Add own public include directory +target_include_directories(${PROJECT_NAME} + PUBLIC + $ + $ + PRIVATE + src/ +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + OUTPUT_NAME ${PROJECT_NAME} + FOLDER core/service +) + +################################## + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${includes} + ${sources} +) \ No newline at end of file diff --git a/src/service/ecal_service/include/ecal/service/client_manager.h b/src/service/ecal_service/include/ecal/service/client_manager.h new file mode 100644 index 0000000..9681501 --- /dev/null +++ b/src/service/ecal_service/include/ecal/service/client_manager.h @@ -0,0 +1,198 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include + +#include + +namespace eCAL +{ + namespace service + { + /** + * @brief Manager for eCAL::service::ClientSession instances + * + * The ClientManager is used to create and stop ClientSessions. It keeps + * track of ClientSession instances that it created. The user doesn't need + * to manage the Sessions manually, e.g. for stopping them from a central + * place. + * + * The ClientManager is only available as shared_ptr. It must be created + * using the static create() method. + * + * - Upon creation, the ClientManager will create a work object for the + * given io_context. This will keep the io_context alive, even if there + * are no clients running. + * + * - For creating a client, the create_client() method must be used. This + * will create a new client instance and return a shared_ptr to it. + * + * - For stopping all clients, the stop() method must be used. This + * will stop all clients and delete the internal work object, so the + * thread executing it can be joined. + * + * ========================================================================= + * _Important_: Do not stop the io_context. This may cause undefined behavior. + * Instead, stop the client manager and wait for the io_context + * to run out of work on its own. + * ========================================================================= + * + * Example code: + * + * @code {.cpp} + * + * // Create the io_context + * auto io_context = std::make_shared(); + * + * // Create a thread for the io_context + * std::thread io_context_thread([&io_context]() { io_context->run(); }); + * + * // Create a client manager + * auto client_manager = eCAL::service::ClientManager::create(io_context, ...); + * + * // Create actual client instances + * auto client1 = client_manager->create_client(...) + * auto client2 = client_manager->create_client(...) + * + * // DO STUFF + * + * // Stop ALL clients from a common place + * client_manager->stop(); + * + * // Join the io_context thread. The io_context does not need to be stopped. + * io_context_thread.join(); + * + * @endcode + * + */ + class ClientManager : public std::enable_shared_from_this + { + /////////////////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////////////////// + public: + /** + * @brief Create a new ClientManager instance, that can be used to create and stop ClientSession instances. + * + * After creation, the ClientManager will create a work object for the + * given io_context. This will keep the io_context alive, even if there + * are no clients running. When stopping the clients, that work object + * will be deleted and the io_context will run out of work on its own. + * + * @param io_context The io context, that will be used for all client sessions + * @param logger A logger-function that will be used for by all client sessions + * + * @return A shared_ptr to the newly created ClientManager instance + */ + static std::shared_ptr create(const std::shared_ptr& io_context, const LoggerT& logger = default_logger("Service Client")); + + // delete copy and move constructors and assign operators + ClientManager(const ClientManager&) = delete; // Copy construct + ClientManager(ClientManager&&) = delete; // Move construct + ClientManager& operator=(const ClientManager&) = delete; // Copy assign + ClientManager& operator=(ClientManager&&) = delete; // Move assign + + // Constructor, Destructor + protected: + ClientManager(const std::shared_ptr& io_context, const LoggerT& logger); + + public: + ~ClientManager(); + + /////////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////////// + public: + /** + * @brief Create a new ClienSession instance, which is managed by this client manager. + * + * The ClientSession will immediatelly connect to the server through the + * given address and port. The protocol version must match the server, + * especially when version 0 (the legacy buggy protocol) is used. Future + * versions are expected to be compatible to each other. + * + * The Event Callback will be called, when the client has connected to the + * server or disconnected from it. + * + * The new Client Session will be managed by the ClientManager and can be + * stopped from this central place. + * + * @param protocol_version The protocol version to use for the client session. If 0, the legacy buggy protocol will be used. + * @param address The address of the server to connect to + * @param port The port of the server to connect to + * @param event_callback The callback, that will be called, when the client has connected to the server or disconnected from it. The callback will be executed in the io_context thread. + * + * @return A shared_ptr to the newly created ClientSession instance + */ + std::shared_ptr create_client(std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const ClientSession::EventCallbackT& event_callback); + + /** + * @brief Returns the number of managed client sessions + * + * @return The number of managed client sessions + */ + size_t client_count() const; + + /** + * @brief Stops the client manager and all managed client sessions + * + * This will stop all managed client sessions and delete the internal + * work object, so the thread executing the io_context can be joined. + * The io context will run out of work on its own. + * + * Once stopped, the client manager cannot be used anymore. It must be + * deleted and a new one must be created. + * + * Make sure, that the io_context is executed by a thread again, when + * creating a new client manager. + */ + void stop(); + + /** + * @brief Returns true, if the client manager is stopped + * + * If stopped, the client manager cannot be restarted. Create a new one + * instead. Make sure, that the io_context is executed by a thread again, + * when creating a new client manager. + * + * @return true, if the client_manager is stopped. False otherwise. + */ + bool is_stopped() const; + + /////////////////////////////////////////////////////// + // Member variables + /////////////////////////////////////////////////////// + private: + const std::shared_ptr io_context_; //!< Reference to the asio io_context + const LoggerT logger_; //!< Logger for all clients + + mutable std::mutex client_manager_mutex_; //!< Mutex protecting the entire class + bool stopped_; //!< Flag indicating, if the manager is stopped + std::unique_ptr work_; //!< Work object to keep the io_context alive. Will be deleted, when the manager is stopped. + std::map> sessions_; //!< Map of all managed client sessions. The raw_ptr is used as key, because it is unique for each client. The weak_ptr is used to actually access the client object, because the client may already be dead and the raw ptr would be dangling in that case. + }; + + } +} diff --git a/src/service/ecal_service/include/ecal/service/client_session.h b/src/service/ecal_service/include/ecal/service/client_session.h new file mode 100644 index 0000000..826a215 --- /dev/null +++ b/src/service/ecal_service/include/ecal/service/client_session.h @@ -0,0 +1,291 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4834) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include +#include + +namespace eCAL +{ + namespace service + { + // Forward declarations + class ClientSessionBase; + + /** + * @brief The eCAL::sercice::ClientSession class represents the binary service client session. + * + * The ClientSession can connect to exactly one service server. For multiple + * connections, multiple ClientSession instances are required. + * + * The ClientSession needs an io_context to run. The io_context is not owned + * by the ClientSession and must be kept alive by the user, e.g. by a work + * object. + * + * Upon creation, the ClientSession will connect to a server via the given + * address and port. Both synchronous and asynchronous service calls are + * available. The async service callbacks are executed by the io_context + * thread. Therefore, the callbacks must not block the io_context thread for + * too long. + * + * ========================================================================= + * _Important_: Do not stop the io_context while the client session is + * running. This may cause undefined behavior. Instead, call + * stop() and wait for the io_context to run out of work on its + * own. + * + * _Tip_: Use the eCAL::service::ClientManager class to manage the + * client's lifecycle. This enables you to stop all client + * sessions from a single place when performing an application + * shutdown. + * ========================================================================= + * + * Sample conde: + * + * @code{.cpp} + * + * // Create an io_context and a work object to keep it alive + * auto io_context = std::make_shared(); + * asio::io_context::work work(*io_context); + * + * // Creat a thread for the io_context + * std::thread io_context_thread([&io_context]() { io_context->run(); }); + * + * // Create client sessions + * auto client_session = eCAL::service::ClientSession::create(io_context, ...) + * + * // call a service asynchronously + * client_session->async_call_service(...); + * + * // DO STUFF + * + * // Stop the client session + * client_session->stop(); + * + * // Wait for the io_context to run out of work + * io_context_thread.join(); + * + * @endcode + */ + class ClientSession + { + ////////////////////////////////////////////// + // Internal types for better consistency + ////////////////////////////////////////////// + public: + using EventCallbackT = ClientEventCallbackT; + using ResponseCallbackT = ClientResponseCallbackT; + using DeleteCallbackT = std::function; + + ////////////////////////////////////////////// + // Constructor, Destructor, Create + ////////////////////////////////////////////// + public: + /** + * @brief Creates a new ClientSession instance. + * + * A new ClientSession will immediatelly start connecting to a server on + * the given address and port. + * + * ========================================================================= + * _Important_: Do not stop the io_context while the client session is + * running. This may cause undefined behavior. Instead, call + * stop() and wait for the io_context to run out of work on its + * own. + * + * _Tip_: Use the eCAL::service::ClientManager class to manage the + * client's lifecycle. This enables you to stop all client + * sessions from a single place when performing an application + * shutdown. + * ========================================================================= + * + * @param io_context The io_context to use for the session and all callbacks. + * @param protocol_version The protocol version to use for the session. When this is 0, the legacy buggy protocol is used. + * @param address The address of the server to connect to. May be an IP or a Hostname, IPv6 is supported. + * @param port The port of the server to connect to. + * @param event_callback The callback to be called when the session's state changes, i.e. when the session successfully connected to a server or disconnected from it. + * @param logger The logger to use for logging. + * @param delete_callback The callback to be called when the session is deleted. This is useful for the eCAL::service::ClientManager to keep track of the number of active sessions. + * + * @return The new ClientSession instance as a shared_ptr. + */ + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger + , const DeleteCallbackT& delete_callback); + + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger = default_logger("Service Client")); + + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const DeleteCallbackT& delete_callback); + + protected: + ClientSession(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger); + + public: + // Delete copy constructor and assignment operator + ClientSession(const ClientSession&) = delete; + ClientSession& operator=(const ClientSession&) = delete; + + // Delete move constructor and assignment operator + ClientSession(ClientSession&&) = delete; + ClientSession& operator=(ClientSession&&) = delete; + + ~ClientSession(); + + ////////////////////////////////////////////// + // Public API + ////////////////////////////////////////////// + public: + /** + * @brief Calls the server asynchronously. + * + * This function will call the server asynchronously. The response_callback + * will be called when the server responds. The response_callback will be + * called from the io_context thread. Therefore, the response_callback must + * not block the io_context thread for too long. + * + * When an error occurs, the response_callback will be called with an error. + * + * When the client has been stopped manually, this function will return + * false. In that case, the response_callback will not be called, as there + * is no way to make sure, that the io_context is still running. + * + * @param request The request to send to the server. + * @param response_callback The callback to be called when the server responds or an error occurs. + * + * @return true if the request was sent enqueued successfully, false otherwise. If this returns false, the response_callback will not be called. + */ + bool async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback); + + /** + * @brief Calls the server synchronously. + * + * This function will call the server synchronously. The function will block + * until the server responds or an error occurs. The response will be + * written to the response parameter + * + * If this function deadlocks, you must check that the io_context is + * running, as that is required for the this function to un-block. The + * io_context must therefore never been stopped. Instead, call stop() and + * wait for the io_context to run out of work on its own. + * + * @param request The request to send to the server. + * @param response The server's response + * + * @return The error that occured or eCAL::service::Error::OK if no error occured. + */ + eCAL::service::Error call_service(const std::shared_ptr& request, std::shared_ptr& response); + + /** + * @brief Get the address that this client session has been created with. + * + * This function returns the address that this client session has been + * created with. It will not return the address of the server that this + * client session is connected to, which would actually be the same + * address, but probably resolved to an IP. + * + * @return The address that this client session has been created with. + */ + std::string get_address() const; + + /** + * @brief Get the port that this client session has been created with. + * + * This function returns the port that this client session has been + * created with. It is not said, that the connection has been established + * successfully. + * + * @return The port that this client session has been created with. + */ + std::uint16_t get_port() const; + + /** + * @brief Get the state of this client session. + * + * @return the state of this client session. + */ + State get_state() const; + + /** + * @brief Get the accepted protocol version that the server and client have agreed on. + * + * If the connection hasn't been established yet, this function will return 0. + * + * @return The accepted protocol version that the server and client have agreed on. + */ + std::uint8_t get_accepted_protocol_version() const; + + /** + * @brief Get the number of pending requests + * + * @return The number of pending requests + */ + int get_queue_size() const; + + /** + * @brief Stops the client session. + * + * This function will stop the client session. The client session will + * disconnect from the server and will not accept any new requests. Any + * further calls to async_call_service() will fail with an error. + * + * Once the client session has been stopped, it cannot be restarted. You + * must create a new client session instead. + */ + void stop(); + + ////////////////////////////////////////////// + // Member Variables + ////////////////////////////////////////////// + private: + std::shared_ptr impl_; //!< The implementation of the client session + }; + } // namespace service +} // namespace eCAL diff --git a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_maximum_array_dimensions.h b/src/service/ecal_service/include/ecal/service/client_session_types.h similarity index 65% rename from lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_maximum_array_dimensions.h rename to src/service/ecal_service/include/ecal/service/client_session_types.h index bfc7d08..113c259 100644 --- a/lib/ecal_protobuf/include/ecal/protobuf/ecal_proto_maximum_array_dimensions.h +++ b/src/service/ecal_service/include/ecal/service/client_session_types.h @@ -17,25 +17,25 @@ * ========================= eCAL LICENSE ================================= */ -#include +#pragma once -#include +#include +#include #include +#include + namespace eCAL { - namespace protobuf + namespace service { - class MaximumArrayDimensionsVisitor : public MessageVisitorDoubleIntegral + enum class ClientEventType: int { - public: - size_t MaxSize(const std::string& element); - - protected: - void ArrayStart(const MessageInfo& info_, const google::protobuf::FieldDescriptor::Type& type_, size_t size_) override; - - private: - std::map max_sizes; + Connected, + Disconnected, }; - } -} \ No newline at end of file + + using ClientEventCallbackT = std::function; + using ClientResponseCallbackT = std::function&)>; + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/include/ecal/service/error.h b/src/service/ecal_service/include/ecal/service/error.h new file mode 100644 index 0000000..e2ef82d --- /dev/null +++ b/src/service/ecal_service/include/ecal/service/error.h @@ -0,0 +1,110 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace service + { + class Error + { + ////////////////////////////////////////// + // Data model + ////////////////////////////////////////// + public: + enum ErrorCode + { + // Generic + OK, + GENERIC_ERROR, + + // Client Calls + CONNECTION_CLOSED, + UNKNOWN_SERVER, + PROTOCOL_ERROR, + STOPPED_BY_USER, + }; + + ////////////////////////////////////////// + // Constructor & Destructor + ////////////////////////////////////////// + public: + Error(ErrorCode error_code, const std::string& message) : error_code_(error_code), message_(message) {} + Error(ErrorCode error_code) : error_code_(error_code) {} + + ////////////////////////////////////////// + // Public API + ////////////////////////////////////////// + public: + inline std::string GetDescription() const + { + switch (error_code_) + { + // Generic + case OK: return "OK"; break; + case GENERIC_ERROR: return "Error"; break; + + // Client Calls + case CONNECTION_CLOSED: return "Connection closed"; break; + case UNKNOWN_SERVER: return "Unknown server"; break; + case PROTOCOL_ERROR: return "Protocol error"; break; + case STOPPED_BY_USER: return "Stopped by user"; break; + + default: return "Unknown error"; + } + } + + inline std::string ToString() const + { + return (message_.empty() ? GetDescription() : GetDescription() + " (" + message_ + ")"); + } + + const inline std::string& GetMessage() const + { + return message_; + } + + ////////////////////////////////////////// + // Operators + ////////////////////////////////////////// + inline operator bool() const { return error_code_ != ErrorCode::OK; } + inline bool operator== (const Error& other) const { return error_code_ == other.error_code_; } + inline bool operator== (const ErrorCode other) const { return error_code_ == other; } + inline bool operator!= (const Error& other) const { return error_code_ != other.error_code_; } + inline bool operator!= (const ErrorCode other) const { return error_code_ != other; } + + inline Error& operator=(ErrorCode error_code) + { + error_code_ = error_code; + return *this; + } + + ////////////////////////////////////////// + // Member Variables + ////////////////////////////////////////// + private: + ErrorCode error_code_; + std::string message_; + }; + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/include/ecal/service/logger.h b/src/service/ecal_service/include/ecal/service/logger.h new file mode 100644 index 0000000..57c31aa --- /dev/null +++ b/src/service/ecal_service/include/ecal/service/logger.h @@ -0,0 +1,75 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include +#include + +namespace eCAL +{ + namespace service + { + enum class LogLevel + { + DebugVerbose, + Debug, + Info, + Warning, + Error, + Fatal, + }; + + using LoggerT = std::function; + + inline LoggerT default_logger(const std::string& node_name, LogLevel min_log_level = LogLevel::DebugVerbose) + { + return [node_name, min_log_level](const LogLevel log_level, const std::string& message) + { + if (log_level < min_log_level) + return; + + switch (log_level) + { + case LogLevel::DebugVerbose: + std::cout << "[" + node_name + "] [Debug+] " + message + "\n"; + break; + case LogLevel::Debug: + std::cout << "[" + node_name + "] [Debug] " + message + "\n"; + break; + case LogLevel::Info: + std::cout << "[" + node_name + "] [Info] " + message + "\n"; + break; + case LogLevel::Warning: + std::cerr << "[" + node_name + "] [Warning] " + message + "\n"; + break; + case LogLevel::Error: + std::cerr << "[" + node_name + "] [Error] " + message + "\n"; + break; + case LogLevel::Fatal: + std::cerr << "[" + node_name + "] [Fatal] " + message + "\n"; + break; + default: + break; + } + }; + } + } +} \ No newline at end of file diff --git a/src/service/ecal_service/include/ecal/service/server.h b/src/service/ecal_service/include/ecal/service/server.h new file mode 100644 index 0000000..cdb23b1 --- /dev/null +++ b/src/service/ecal_service/include/ecal/service/server.h @@ -0,0 +1,209 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include + +#include + +#include "logger.h" +#include "server_session_types.h" + +namespace eCAL +{ + namespace service + { + // Forward declaration + class ServerImpl; + + /** + * @brief The eCAL::service::Server class represents a binary service server that can be used may multiple clients. + * + * The Server needs an io_context to run. It will listen on a specified + * port. When desired, the server can be opened on port 0, which will the OS + * chose a free port. + * The server uses one callback for the actual service function and another + * callback for events. When stopping, a Disconnect event for each connected + * client will be fired. + * + * Callbacks are executed in the context of the io_context. Therfore, long + * running callbacks can block the server and everything else, that is + * dependent on the io_context. + * + * ========================================================================= + * _Important_: Do not stop the io_context while the server is running. + * This may cause undefined behavior. In fact, the io_context + * should run out of work on its own. + * + * _Tip_: Use the eCAL::service::ServerManager class to manage the + * server's lifecycle. This enables you to stop all servers from + * a single place when performing an application shutdown. + * ========================================================================= + * + * Sample code: + * + * auto io_context = std::make_shared(); + * auto dummy_work = std::make_shared(*io_context); + * + * std::thread io_thread([&io_context]() { io_context->run(); }); + * + * auto server = eCAL::service::Server::create(io_context, ...) + * + * // Do stuff + * + * server->stop(); + * dummy_work->reset(); + * io_thread.join(); + * + */ + class Server + { + ////////////////////////////////////////////// + // Internal types for better consistency + ////////////////////////////////////////////// + public: + using EventCallbackT = ServerEventCallbackT; + using ServiceCallbackT = ServerServiceCallbackT; + using DeleteCallbackT = std::function; + + /////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////// + + public: + /** + * @brief Creates a new Server instance. + * + * A new server will directly start listening on the specified port and wait for new connections. + * + * ========================================================================= + * _Important_: Do not stop the io_context while the server is running. + * This may cause undefined behavior. In fact, the io_context + * should run out of work on its own. + * + * _Tip_: Use the eCAL::service::ServerManager class to manage the + * server's lifecycle. This enables you to stop all servers from + * a single place when performing an application shutdown. + * ========================================================================= + * + * @param io_context The io_context to use for the server and all callbacks + * @param protocol_version The protocol version to use. When this is 0, the buggy protocol version 0 will be used. + * @param port The port to listen on. When this is 0, the OS will chose a free port. + * @param service_callback The callback to use for service calls. Will be executed in the context of the io_context. + * @param parallel_service_calls_enabled When true, service calls will be executed in parallel. When false, service calls will be executed sequentially. + * @param event_callback The callback to use for events (clients connect or clients disconnect). Will be executed in the context of the io_context. + * @param logger A function used for logging. + * @param delete_callback A callback that will be executed when the server is deleted. + * + * @return The new server instance. + */ + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const LoggerT& logger + , const DeleteCallbackT& delete_callback); + + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const LoggerT& logger = default_logger("Service Server")); + + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const DeleteCallbackT& delete_callback); + protected: + Server(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const LoggerT& logger); + + public: + Server(const Server&) = delete; // Copy construct + Server(Server&&) = delete; // Move construct + + Server& operator=(const Server&) = delete; // Copy assign + Server& operator=(Server&&) = delete; // Move assign + + ~Server() = default; + + /////////////////////////////////////////// + // API + /////////////////////////////////////////// + + public: + + /** + * @brief Checks if any client is currently connected to the server. + * + * @return true, when at least one client is connected. False otherwise. + */ + bool is_connected() const; + + /** + * @brief Get the number of currently connected clients. + * + * @return the number of connected clients + */ + int get_connection_count() const; + + /** + * @brief Returns the port the server is listening on. + * + * When the server was created with port 0, this will return the port the + * OS chose. + * + * @return The port + */ + std::uint16_t get_port() const; + + /** + * @brief Stops the server + * + * This closes the socket and stops the server. After the server has been + * stopped, it will still execute disconnect callbacks. The server will + * not accept new connections, though. + * + * A server that has been stopped cannot be restarted. Create a new + * server, when desired. + */ + void stop(); + + /////////////////////////////////////////// + // Member Variables + /////////////////////////////////////////// + private: + std::shared_ptr impl_; //!< The private implementation + }; + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/include/ecal/service/server_manager.h b/src/service/ecal_service/include/ecal/service/server_manager.h new file mode 100644 index 0000000..fc6bfa4 --- /dev/null +++ b/src/service/ecal_service/include/ecal/service/server_manager.h @@ -0,0 +1,184 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include + +#include + +namespace eCAL +{ + namespace service + { + /** + * @brief Manager for eCAL::service::server instances + * + * The ServerManager keeps track of and manages all eCAL::service::server + * instances that it created. It is used to create and stop servers. The + * user doesn't need to manage the servers manually, e.g. for stopping them + * from a central place. + * + * The ServerManager is only available as shared_ptr. It must be created + * using the static create() method. + * + * - Upon creation, the ServerManager will create a work object for the + * given io_context. This will keep the io_context alive, even if there + * are no servers running. + * + * - For creating a server, the create_server() method must be used. This + * will create a new server instance and return a shared_ptr to it. + * + * - For stopping all servers, the stop() method must be used. This + * will stop all servers and delete the internal work object, so the + * thread executing it can be joined. + * + * ========================================================================= + * _Important_: Do not stop the io_context. This may cause undefined + * behavior. Instead, stop the server manager and wait for the + * io_context to run out of work on its own. + * ========================================================================= + * + * Example code: + * + * @code {.cpp} + * + * // Create a server manager + * auto server_manager = eCAL::service::ServerManager::create(io_context); + * + * // Create and start an io_context thread. + * std::thread io_context_thread([&io_context]() { io_context->run(); }); + * + * // Create actual server instances + * auto server1 = server_manager->create_server(...) + * auto server2 = server_manager->create_server(...) + * + * // DO STUFF + * + * // Stop ALL servers from a common place + * server_manager->stop(); + * + * // Join the io_context thread. The io_context does not need to be stopped. + * io_context_thread.join(); + * + * @endcode + */ + class ServerManager : public std::enable_shared_from_this + { + /////////////////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////////////////// + public: + /** + * @brief Create a new server manager, that can be used to create and stop servers + * + * After creation, the server manager will create a work object for the + * given io_context. This will keep the io_context alive, even if there + * are no servers running. When stopping the servers, that work object + * will be deleted and the io_context will run out of work on its own. + * + * @param io_context The io context, that will be used for all servers + * @param logger A logger-function that will be used for by all servers + * + * @return A shared_ptr to the created server manager + */ + static std::shared_ptr create(const std::shared_ptr& io_context, const LoggerT& logger = default_logger("Service Server")); + + // delete copy and move constructors and assign operators + ServerManager(const ServerManager&) = delete; // Copy construct + ServerManager(ServerManager&&) = delete; // Move construct + ServerManager& operator=(const ServerManager&) = delete; // Copy assign + ServerManager& operator=(ServerManager&&) = delete; // Move assign + + // Constructor, Destructor + protected: + ServerManager(const std::shared_ptr& io_context, const LoggerT& logger); + + public: + ~ServerManager(); + + /////////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////////// + + public: + /** + * @brief Create a new server instance, which is managed by this server manager. + * + * The server manager will keep track of the create server internally. The + * server (and all other servers, that have been created by this manager) + * can be stopped via the stop() method. + * + * @param protocol_version The protocol version, that will be used by this server + * @param port The port, that the server will listen on. If 0, the OS will choose a free port. + * @param service_callback The callback, that will be called for each incoming service call. The callback will be executed in the io_context thread. + * @param parallel_service_calls_enabled If true, the server will handle incoming service calls in parallel. If false, the server will handle incoming service calls sequentially. + * @param event_callback The callback, that will be called whenever a client connects or disconnects. The callback will be executed in the io_context thread. + * + * @return a shared pointer to the created server + */ + std::shared_ptr create_server(std::uint8_t protocol_version + , std::uint16_t port + , const Server::ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const Server::EventCallbackT& event_callback); + + /** + * @brief Get the number of servers, that are currently managed by this server manager + * @return The number of servers + */ + size_t server_count() const; + + /** + * @brief Stop all servers that are currently managed by this server manager + * + * This will also delete the internal work object, so the thread executing + * the io_context can be joined. The io context will run out of work on + * its own. + * Obviously, this is only true if no other managers etc. use the io_context. + */ + void stop(); + + /** + * @brief Returns true, if the server manager is stopped + * + * If stopped, the server manager cannot be restarted. Create a new one + * instead. Make sure, that the io_context is executed by a thread again, + * when creating a new server manger. + * + * @return true, if the server_manager is stopped. False otherwise. + */ + bool is_stopped() const; + + /////////////////////////////////////////////////////// + // Member variables + /////////////////////////////////////////////////////// + private: + const std::shared_ptr io_context_; //!< Reference to the asio io_context + const LoggerT logger_; //!< Logger for all servers + + mutable std::mutex server_manager_mutex_; //!< Mutex protecting the entire class + bool stopped_; //!< Flag indicating, if the manager is stopped + std::unique_ptr work_; //!< Work object to keep the io_context alive. Will be deleted, when the manager is stopped. + std::map> sessions_; //!< Map of all servers, that are currently managed by this server manager. The raw_ptr is used as key, because it is unique for each server. The weak_ptr is used to actually access the server object, because the server may already be dead and the raw ptr would be dangling in that case. + }; + + } +} diff --git a/ecal/core/src/io/udp_sender.h b/src/service/ecal_service/include/ecal/service/server_session_types.h similarity index 59% rename from ecal/core/src/io/udp_sender.h rename to src/service/ecal_service/include/ecal/service/server_session_types.h index 179dd4e..fffc828 100644 --- a/ecal/core/src/io/udp_sender.h +++ b/src/service/ecal_service/include/ecal/service/server_session_types.h @@ -17,31 +17,23 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @brief UDP sender class -**/ - #pragma once +#include #include -#include "ecal_sender.h" +#include namespace eCAL { - class CUDPSenderImpl; - - class CUDPSender : public CSender + namespace service { - public: - CUDPSender(); - - bool Create(const SSenderAttr& attr_); - bool Destroy(); - - size_t Send (const void* buf_, const size_t len_, const char* ipaddr_ = nullptr); - void SendAsync(const void* buf_, const size_t len_, const char* ipaddr_ = nullptr); - - protected: - std::shared_ptr m_socket_impl; - }; -} + enum class ServerEventType: int + { + Connected, //!< A client has connected successfully. + Disconnected, //!< The connection to a client has been closed for any reason. + }; + + using ServerServiceCallbackT = std::function& request, const std::shared_ptr& response)>; + using ServerEventCallbackT = std::function; + } // namespace service +} // namespace eCAL diff --git a/ecal/core/src/ecal_process.h b/src/service/ecal_service/include/ecal/service/state.h similarity index 70% rename from ecal/core/src/ecal_process.h rename to src/service/ecal_service/include/ecal/service/state.h index 2be615e..aec91a3 100644 --- a/ecal/core/src/ecal_process.h +++ b/src/service/ecal_service/include/ecal/service/state.h @@ -17,27 +17,18 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @file ecal_process.h - * @brief eCAL process interface (internal) -**/ - #pragma once -#include - namespace eCAL { - namespace Process + namespace service { - namespace internal // non-public namespace of eCAL::Process + enum class State { - /** - * @brief Get unique host id. - * - * @return Host id or zero if failed. - **/ - ECAL_API int GetHostID(); - } + NOT_CONNECTED, //!< Initial state + HANDSHAKE, //!< The connection is currently in handshake state. + CONNECTED, //!< The connection is established and ready to exchange data. + FAILED, //!< The connection has been closed due to an error or by the user + }; } } diff --git a/src/service/ecal_service/src/client_manager.cpp b/src/service/ecal_service/src/client_manager.cpp new file mode 100644 index 0000000..9573f5d --- /dev/null +++ b/src/service/ecal_service/src/client_manager.cpp @@ -0,0 +1,109 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +namespace eCAL +{ + namespace service + { + /////////////////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////////////////// + std::shared_ptr ClientManager::create(const std::shared_ptr& io_context, const LoggerT& logger) + { + return std::shared_ptr(new ClientManager(io_context, logger)); + } + + ClientManager::ClientManager(const std::shared_ptr& io_context, const LoggerT& logger) + : io_context_(io_context) + , logger_(logger) + , stopped_(false) + , work_(std::make_unique(*io_context)) + {} + + ClientManager::~ClientManager() + { + stop(); + } + + /////////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////////// + std::shared_ptr ClientManager::create_client(std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const ClientSession::EventCallbackT& event_callback) + { + const std::lock_guard lock(client_manager_mutex_); + if (stopped_) + { + return nullptr; + } + + auto deleter = [weak_me = std::weak_ptr(shared_from_this())](ClientSession* session) + { + auto me = weak_me.lock(); + if (me) + { + // Remove the session from the sessions_ map + const std::lock_guard lock(me->client_manager_mutex_); + me->sessions_.erase(session); + } + }; + + auto client = ClientSession::create(io_context_, protocol_version, address, port, event_callback, logger_, deleter); + sessions_.emplace(client.get(), client); + return client; + } + + size_t ClientManager::client_count() const + { + const std::lock_guard lock(client_manager_mutex_); + return sessions_.size(); + } + + void ClientManager::stop() + { + std::map> sessions_copy; + { + const std::lock_guard lock(client_manager_mutex_); + stopped_ = true; + sessions_copy = sessions_; + } + + // stop all clients without having the mutex locked, so we don't crash, when this thread directly calls the delete callback, that itself needs to have the mutex locked. + for (auto& server_weak : sessions_copy) + { + auto server = server_weak.second.lock(); + if (server) + server->stop(); + } + + work_.reset(); + } + + bool ClientManager::is_stopped() const + { + const std::lock_guard lock(client_manager_mutex_); + return stopped_; + } + + } +} diff --git a/src/service/ecal_service/src/client_session.cpp b/src/service/ecal_service/src/client_session.cpp new file mode 100644 index 0000000..06ae276 --- /dev/null +++ b/src/service/ecal_service/src/client_session.cpp @@ -0,0 +1,146 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include "client_session_impl_v1.h" +#include "client_session_impl_v0.h" +#include "condition_variable_signaler.h" + +namespace eCAL +{ + namespace service + { + std::shared_ptr ClientSession::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger + , const DeleteCallbackT& delete_callback) + { + auto deleter = [delete_callback](ClientSession* session) + { + delete_callback(session); + delete session; // NOLINT(cppcoreguidelines-owning-memory) + }; + + return std::shared_ptr(new ClientSession(io_context, protocol_version, address, port, event_callback, logger), deleter); + } + + std::shared_ptr ClientSession::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger) + { + return std::shared_ptr(new ClientSession(io_context, protocol_version, address, port, event_callback, logger)); + } + + std::shared_ptr ClientSession::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const DeleteCallbackT& delete_callback) + { + return ClientSession::create(io_context, protocol_version, address, port, event_callback, default_logger("Service Client"), delete_callback); + } + + ClientSession::ClientSession(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger) + { + if (protocol_version == 0) + { + impl_ = ClientSessionV0::create(io_context, address, port, event_callback, logger); + } + else + { + impl_ = ClientSessionV1::create(io_context, address, port, event_callback, logger); + } + } + + ClientSession::~ClientSession() + { + impl_->stop(); + } + + ////////////////////////////////////////////// + // Public API + ////////////////////////////////////////////// + bool ClientSession::async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback) + { + return impl_->async_call_service(request, response_callback); + } + + eCAL::service::Error ClientSession::call_service(const std::shared_ptr& request, std::shared_ptr& response) + { + eCAL::service::Error error(Error::GENERIC_ERROR); + + // Create a condition variable and a mutex to wait for the response + std::mutex mutex; + std::condition_variable condition_variable; + bool is_signaled = false; + + bool async_call_successful(false); + + { + // Create a response callback, that will set the response and notify the condition variable + const ResponseCallbackT response_callback + = [&error, &response, signaler = std::make_shared(condition_variable, mutex, is_signaled)] + (const eCAL::service::Error& response_error, const std::shared_ptr& response_) + { + response = response_; + error = response_error; + }; + async_call_successful = async_call_service(request, response_callback); + } + + if(async_call_successful) + { + // Lock mutex, call service asynchronously and wait for the condition variable to be notified + std::unique_lock lock(mutex); + condition_variable.wait(lock, [&is_signaled]() { return is_signaled; }); + return error; + } + else + { + // If the async service call has not been successfull, that means that + // the user has stopped the client session. In that case, the request + // hasn't even been enqueued, as there is no way to make sure that the + // io_context is still being executed. In that case, we must not wait + // for it to unblock the condition variable. + // Thus, we just return an error + return eCAL::service::Error::ErrorCode::STOPPED_BY_USER; + } + } + + State ClientSession::get_state() const { return impl_->get_state(); } + std::uint8_t ClientSession::get_accepted_protocol_version() const { return impl_->get_accepted_protocol_version(); } + int ClientSession::get_queue_size() const { return impl_->get_queue_size(); } + std::string ClientSession::get_address() const { return impl_->get_address(); } + std::uint16_t ClientSession::get_port() const { return impl_->get_port(); } + void ClientSession::stop() { impl_->stop(); } + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/client_session_impl_base.h b/src/service/ecal_service/src/client_session_impl_base.h new file mode 100644 index 0000000..4a3ce5e --- /dev/null +++ b/src/service/ecal_service/src/client_session_impl_base.h @@ -0,0 +1,94 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4834) +#endif +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include + +#include + +namespace eCAL +{ + namespace service + { + class ClientSessionBase + { + ///////////////////////////////////// + // Custom types for API + ///////////////////////////////////// + public: + using EventCallbackT = eCAL::service::ClientEventCallbackT; + using ResponseCallbackT = eCAL::service::ClientResponseCallbackT; + + ///////////////////////////////////// + // Constructor, Destructor, Create + ///////////////////////////////////// + protected: + ClientSessionBase(const std::shared_ptr& io_context_, const EventCallbackT& event_callback) + : io_context_ (io_context_) + , socket_ (*io_context_) + , event_callback_(event_callback) + {} + + public: + ClientSessionBase(const ClientSessionBase&) = delete; + ClientSessionBase(ClientSessionBase&&) = delete; + ClientSessionBase& operator=(const ClientSessionBase&) = delete; + ClientSessionBase& operator=(ClientSessionBase&&) = delete; + + virtual ~ClientSessionBase() = default; + + ///////////////////////////////////// + // API + ///////////////////////////////////// + public: + virtual bool async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback) = 0; + + virtual std::string get_address() const = 0; + virtual std::uint16_t get_port() const = 0; + + virtual State get_state() const = 0; + virtual std::uint8_t get_accepted_protocol_version() const = 0; + virtual int get_queue_size() const = 0; + + virtual void stop() = 0; + + ///////////////////////////////////// + // Member variables + ///////////////////////////////////// + protected: + const std::shared_ptr io_context_; + asio::ip::tcp::socket socket_; + mutable std::mutex socket_mutex_; + const EventCallbackT event_callback_; + + }; + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/client_session_impl_v0.cpp b/src/service/ecal_service/src/client_session_impl_v0.cpp new file mode 100644 index 0000000..36c8522 --- /dev/null +++ b/src/service/ecal_service/src/client_session_impl_v0.cpp @@ -0,0 +1,516 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "client_session_impl_v0.h" + +#include "protocol_v0.h" +#include "log_helpers.h" +#include "log_defs.h" + +#include + +namespace eCAL +{ + namespace service + { + ///////////////////////////////////// + // Constructor, Destructor, Create + ///////////////////////////////////// + std::shared_ptr ClientSessionV0::create(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger) + { + std::shared_ptr instance(new ClientSessionV0(io_context, address, port, event_callback, logger)); + + instance->resolve_endpoint(); + + return instance; + } + + ClientSessionV0::ClientSessionV0(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger) + : ClientSessionBase(io_context, event_callback) + , address_ (address) + , port_ (port) + , service_call_queue_strand_(*io_context) + , resolver_ (*io_context) + , logger_ (logger) + , state_ (State::NOT_CONNECTED) + , stopped_by_user_ (false) + , service_call_in_progress_ (false) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Created"); + } + + ClientSessionV0::~ClientSessionV0() + { + ClientSessionV0::stop(); + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Deleted"); + } + + ////////////////////////////////////// + // Connection establishement + ////////////////////////////////////// + void ClientSessionV0::resolve_endpoint() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "Resolving endpoint [" + address_ + ":" + std::to_string(port_) + "]..."); + + const asio::ip::tcp::resolver::query query(address_, std::to_string(port_)); + + resolver_.async_resolve(query + , service_call_queue_strand_.wrap([me = enable_shared_from_this::shared_from_this()] + (asio::error_code ec, const asio::ip::tcp::resolver::iterator& resolved_endpoints) + { + if (ec) + { + const std::string message = "Failed resolving endpoint [" + me->address_ + ":" + std::to_string(me->port_) + "]: " + ec.message(); + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + return; + } + else + { +#if ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED + // Verbose-debug log of all endpoints + { + std::string endpoints_str = "Resolved endpoints for " + me->address_ + ": "; + for (auto it = resolved_endpoints; it != asio::ip::tcp::resolver::iterator(); ++it) + { + endpoints_str += endpoint_to_string(*it) + ", "; + } + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, endpoints_str); + } +#endif //ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED + me->connect_to_endpoint(resolved_endpoints); + } + })); + } + + void ClientSessionV0::connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints) + { + // Look for the best endpoint to connect to. If possible, we use a loopback + // endpoint. Otherwise, we just use the first one. + + + auto endpoint_to_connect_to = resolved_endpoints->endpoint(); // Default to first endpoint + for (auto it = resolved_endpoints; it != asio::ip::tcp::resolver::iterator(); it++) + { + if (it->endpoint().address().is_loopback()) + { + // If we find a loopback endpoint we use that one. + endpoint_to_connect_to = it->endpoint(); + break; + } + } + + ECAL_SERVICE_LOG_DEBUG(logger_, "Successfully resolved endpoint to [" + endpoint_to_string(endpoint_to_connect_to) + "]. Connecting..."); + + const std::lock_guard socket_lock(socket_mutex_); + socket_.async_connect(endpoint_to_connect_to + , service_call_queue_strand_.wrap([me = shared_from_this(), endpoint_to_connect_to](asio::error_code ec) + { + if (ec) + { + const std::string message = "Failed to connect to endpoint [" + endpoint_to_string(endpoint_to_connect_to) + "]: " + ec.message(); + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + return; + } + else + { + ECAL_SERVICE_LOG_DEBUG(me->logger_, "Successfully connected to endpoint [" + endpoint_to_string(endpoint_to_connect_to) + "]"); + + // Disable Nagle's algorithm. Nagles Algorithm will otherwise cause the + // Socket to wait for more data, if it encounters a frame that can still + // fit more data. Obviously, this is an awfull default behaviour, if we + // want to transmit our data in a timely fashion. + { + asio::error_code socket_option_ec; + { + const std::lock_guard socket_lock(me->socket_mutex_); + me->socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + } + if (socket_option_ec) + { + me->logger_(LogLevel::Warning, "[" + get_connection_info_string(me->socket_) + "] " + "Failed setting tcp::no_delay option: " + socket_option_ec.message()); + } + } + + const std::string message = "Connected to server. Using protocol version 0."; + me->logger_(LogLevel::Info, "[" + get_connection_info_string(me->socket_) + "] " + message); + + { + const std::lock_guard lock(me->service_state_mutex_); + me->state_ = State::CONNECTED; + } + + // Call event callback + me->event_callback_(eCAL::service::ClientEventType::Connected, message); + + // Start sending service requests, if there are any + { + const std::lock_guard lock(me->service_state_mutex_); + if (!me->service_call_queue_.empty()) + { + // If there are service calls in the queue, we send the next one. + me->service_call_in_progress_ = true; + me->send_next_service_request(me->service_call_queue_.front().request, me->service_call_queue_.front().response_cb); + me->service_call_queue_.pop_front(); + } + else + { + // If there are no more service calls to send, we go to error-peeking. + // While error peeking we basically do nothing, except from non-destructively + // reading 1 byte from the socket (i.e. without removing it from the socket). + // This will cause asio / the OS to notify us, when the server closed the connection. + + me->service_call_in_progress_ = false; + me->peek_for_error(); + } + } + } + })); + } + + ////////////////////////////////////// + // Service calls + ////////////////////////////////////// + + bool ClientSessionV0::async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback) + { + // Lock mutex for stopped_by_user_ variable + const std::lock_guard service_state_lock(service_state_mutex_); + + if (stopped_by_user_) + { + return false; + } + else + { + service_call_queue_strand_.post([me = shared_from_this(), request, response_callback]() + { + // Variable that enables us to unlock the mutex before actually calling the callback + bool call_response_callback_with_error(false); + + { + const std::lock_guard lock(me->service_state_mutex_); + if (me->state_ != State::FAILED) + { + // If we are not in failed state, let's check + // whether we directly invoke the call of if we add it to the queue + + if (!me->service_call_in_progress_ && (me->state_ == State::CONNECTED)) + { + // Directly call the the service, iff + // + // - There is no call in progress + // + // and + // + // - We are connected + // + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " No service call in progress. Directly starting next service call."); + me->service_call_in_progress_ = true; + me->send_next_service_request(request, response_callback); + } + else + { + // Add the call to the queue, iff: + // + // - A call is already in progress + // + // or + // + // - We are not connected, yet + // + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Queuing new service request"); + me->service_call_queue_.push_back(ServiceCall{request, response_callback}); + } + } + else + { + // If we are in FAILED state, we directly call the callback with an error. + call_response_callback_with_error = true; + } + } + + if(call_response_callback_with_error) + { + // If we are in FAILED state, we directly call the callback with an error. + // The mutex is unlocked at this point. That is important, as we have no + // influence on when the callback will return. + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " Client is in FAILED state. Calling callback with error."); + response_callback(eCAL::service::Error::ErrorCode::CONNECTION_CLOSED, nullptr); + } + }); + return true; + } + } + + void ClientSessionV0::send_next_service_request(const std::shared_ptr& request, const ResponseCallbackT& response_cb) + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Sending service request..."); + + // V0 writes payload with no header + + const std::lock_guard socket_lock(socket_mutex_); + asio::async_write(socket_ + , asio::buffer(*request) + , [me = shared_from_this(), request, response_cb](asio::error_code ec, std::size_t /*bytes_sent*/) + { + if (ec) + { + const std::string message = "Failed sending service request: " + ec.message(); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + + // Call the callback with an error + response_cb(Error(Error::ErrorCode::CONNECTION_CLOSED, message), nullptr); + + // Further handle the error, e.g. unwinding pending service calls and calling the event callback + me->handle_connection_loss_error(message); + return; + } + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Successfully sent service request."); + me->receive_service_response(response_cb); + }); + + } + + void ClientSessionV0::receive_service_response(const ResponseCallbackT& response_cb) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "[" + get_connection_info_string(socket_) + "] " + "Waiting for service response..."); + + eCAL::service::ProtocolV0::async_receive_payload_with_header(socket_, socket_mutex_ + , service_call_queue_strand_.wrap([me = shared_from_this(), response_cb](asio::error_code ec) + { + const std::string message = "Failed receiving service response: " + ec.message(); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + + // Call the callback with an error + response_cb(Error(Error::ErrorCode::CONNECTION_CLOSED, message), nullptr); + + // Further handle the error, e.g. unwinding pending service calls and calling the event callback + me->handle_connection_loss_error(message); + }) + , service_call_queue_strand_.wrap([me = shared_from_this(), response_cb](const std::shared_ptr& /*header_buffer*/, const std::shared_ptr& payload_buffer) + { + // The response is a Service response + ECAL_SERVICE_LOG_DEBUG(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Successfully received service response of " + std::to_string(payload_buffer->size()) + " bytes"); + + // Call the user's callback + response_cb(Error::OK, payload_buffer); + + // Check if there are more items in the queue. If so, send the next request + // The mutex must be locket, as we access the queue. + { + const std::lock_guard lock(me->service_state_mutex_); + + if (!me->service_call_queue_.empty()) + { + // If there are more items, continue calling the service + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " Service call queue contains " + std::to_string(me->service_call_queue_.size()) + " Entries. Starting next service call."); + me->service_call_in_progress_ = true; + me->send_next_service_request(me->service_call_queue_.front().request, me->service_call_queue_.front().response_cb); + me->service_call_queue_.pop_front(); + } + else + { + // If there are no more service calls to send, we go to error-peeking. + // While error peeking we basically do nothing, except from non-destructively + // reading 1 byte from the socket (i.e. without removing it from the socket). + // This will cause asio / the OS to notify us, when the server closed the connection. + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " No further servcice calls."); + me->service_call_in_progress_ = false; + me->peek_for_error(); + } + } + })); + + + } + + ////////////////////////////////////// + // Status API + ////////////////////////////////////// + + std::string ClientSessionV0::get_address() const + { + return address_; + } + + std::uint16_t ClientSessionV0::get_port() const + { + return port_; + } + + State ClientSessionV0::get_state() const + { + const std::lock_guard lock(service_state_mutex_); + return state_; + } + + std::uint8_t ClientSessionV0::get_accepted_protocol_version() const + { + return 0; + } + + int ClientSessionV0::get_queue_size() const + { + const std::lock_guard lock(service_state_mutex_); + return static_cast(service_call_queue_.size()); + } + + ////////////////////////////////////// + // Shutdown + ////////////////////////////////////// + void ClientSessionV0::peek_for_error() + { + const std::shared_ptr> peek_buffer = std::make_shared>(1, '\0'); + + const std::lock_guard socket_lock(socket_mutex_); + socket_.async_receive(asio::buffer(*peek_buffer) + , asio::socket_base::message_peek + , service_call_queue_strand_.wrap([me = shared_from_this(), peek_buffer](const asio::error_code& ec, std::size_t /*bytes_transferred*/) { + if (ec) + { + const std::string message = "Connection loss while idling: " + ec.message(); + me->logger_(eCAL::service::LogLevel::Info, "[" + get_connection_info_string(me->socket_) + "] " + message); + me->handle_connection_loss_error("Connection loss while idling: " + ec.message()); + } + })); + } + + void ClientSessionV0::handle_connection_loss_error(const std::string& error_message) + { + bool call_event_callback (false); // Variable that enables us to unlock the mutex before we execute the event callback. + + // Close the socket, so all waiting async operations are actually woken + // up and fail with an error code. If we wouldn't do that, at least on + // Ubuntu only 1 waiting operations would wake up, while the others would + // indefinitively continue to wait. + // Having multiple async operations on the same socket actually does + // happen, as we are peeking for errors whenever we don't send or + // receive anything. + close_socket(); + + { + const std::lock_guard lock(service_state_mutex_); + + // cancel the connection loss handling, if we are already in FAILED state + if (state_ == State::FAILED) + return; + + if (state_ == State::CONNECTED) + { + // Event callback + call_event_callback = true; + } + + // Set the state to FAILED + state_ = State::FAILED; + + // call all callbacks from the queue with an error + if (!service_call_queue_.empty()) + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Calling " + std::to_string(service_call_queue_.size()) + " service callbacks with error"); + call_all_callbacks_with_error(); + } + } + + if (call_event_callback) + { + event_callback_(eCAL::service::ClientEventType::Disconnected, error_message); + } + } + + void ClientSessionV0::call_all_callbacks_with_error() + { + service_call_queue_strand_.post([me = shared_from_this()]() + { + ServiceCall first_service_call; + bool more_service_calls(false); + + { + // Lock the mutex and manipulate the queue. We want the mutex unlocked for the event callback call. + const std::lock_guard lock(me->service_state_mutex_); + + if (me->service_call_queue_.empty()) + return; + + first_service_call = std::move(me->service_call_queue_.front()); + me->service_call_queue_.pop_front(); + + more_service_calls = (!me->service_call_queue_.empty()); + } + + // Execute the callback with an error + first_service_call.response_cb(eCAL::service::Error::ErrorCode::CONNECTION_CLOSED, nullptr); // TODO: I should probably store the error that lead to this somewhere and tell the actual error. + + // If there are more sevice calls, call those with an error, as well + if (more_service_calls) + me->call_all_callbacks_with_error(); + }); + } + + void ClientSessionV0::stop() + { + // This is a function that gets used both by the API and by potentially + // multiple failing async operations at once. + + { + // Set the stopped_by_user_ flag to true, so that the async operations stop enqueuing new service calls. + std::lock_guard service_state_lock(service_state_mutex_); + stopped_by_user_ = true; + } + + { + close_socket(); + } + } + + void ClientSessionV0::close_socket() + { + // Close the socket, so all waiting async operations will fail with an + // error code. This will also cause the client to call all pending + // callbacks with an error. + const std::lock_guard socket_lock(socket_mutex_); + + if (socket_.is_open()) + { + { + asio::error_code ec; + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + } + + { + asio::error_code ec; + socket_.close(ec); + } + } + } + + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/client_session_impl_v0.h b/src/service/ecal_service/src/client_session_impl_v0.h new file mode 100644 index 0000000..c792e83 --- /dev/null +++ b/src/service/ecal_service/src/client_session_impl_v0.h @@ -0,0 +1,151 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include "client_session_impl_base.h" +#include + +#include +#include + +namespace eCAL +{ + namespace service + { + class ClientSessionV0 + : public ClientSessionBase + , public std::enable_shared_from_this + { + ////////////////////////////////////// + // Internal types + ////////////////////////////////////// + private: + struct ServiceCall + { + std::shared_ptr request; + ResponseCallbackT response_cb; + }; + + ///////////////////////////////////// + // Constructor, Destructor, Create + ///////////////////////////////////// + public: + static std::shared_ptr create(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger = default_logger("Service Client V1")); + + protected: + ClientSessionV0(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger); + + public: + // Delete copy / move constructor and assignment operator + ClientSessionV0(const ClientSessionV0&) = delete; // Copy construct + ClientSessionV0(ClientSessionV0&&) = delete; // Move construct + + ClientSessionV0& operator=(const ClientSessionV0&) = delete; // Copy assign + ClientSessionV0& operator=(ClientSessionV0&&) = delete; // Move assign + + ~ClientSessionV0() override; + + ////////////////////////////////////// + // Connection establishement + ////////////////////////////////////// + private: + void resolve_endpoint(); + void connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints); + + ////////////////////////////////////// + // Service calls + ////////////////////////////////////// + public: + bool async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback) override; + + private: + void send_next_service_request(const std::shared_ptr& request, const ResponseCallbackT& response_cb); + void receive_service_response(const ResponseCallbackT& response_cb); + + ////////////////////////////////////// + // Status API + ////////////////////////////////////// + public: + std::string get_address() const override; + std::uint16_t get_port() const override; + State get_state() const override; + std::uint8_t get_accepted_protocol_version() const override; + int get_queue_size() const override; + + ////////////////////////////////////// + // Shutdown + ////////////////////////////////////// + public: + void peek_for_error(); + void handle_connection_loss_error(const std::string& message); + void call_all_callbacks_with_error(); + + /** + * @brief Stop the client. + * + * This will close the connection AND set the state to stopped. This + * means, that no further async service calls will be accepted. From now + * on, all async service calls will just return false and the callback + * will not be called any more in order to give the io_thread the chance + * of shutting down. + */ + void stop() override; + + private: + /** + * @brief Closes the internal socket + * + * This will close the connection without setting the state to stopped. + * This means, that further async service calls will still be accepted, + * but they will fail. The failure is still communicated by calling the + * callback. For a proper shutdown, where the plan is to stop the + * io_context thread as well, the stop() function must be used, or the + * client must be destroyed. + */ + void close_socket(); + + ////////////////////////////////////// + // Member variables + ////////////////////////////////////// + private: + + const std::string address_; //!< The original address that this client was created with. + const std::uint16_t port_; //!< The original port that this client was created with. + + asio::io_context::strand service_call_queue_strand_; + asio::ip::tcp::resolver resolver_; + const LoggerT logger_; + + mutable std::mutex service_state_mutex_; + State state_; //!< The connection state of this client. Protected by service_state_mutex_. + bool stopped_by_user_; //!< Telling whether we actively stopped the client. Protected by service_state_mutex_. When set, the client will not accept any more async service calls. + std::deque service_call_queue_; + bool service_call_in_progress_; + }; + } +} diff --git a/src/service/ecal_service/src/client_session_impl_v1.cpp b/src/service/ecal_service/src/client_session_impl_v1.cpp new file mode 100644 index 0000000..9379b48 --- /dev/null +++ b/src/service/ecal_service/src/client_session_impl_v1.cpp @@ -0,0 +1,635 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "client_session_impl_v1.h" + +#include "protocol_v1.h" +#include "log_helpers.h" +#include "log_defs.h" + +#include + +namespace eCAL +{ + namespace service + { + constexpr std::uint8_t ClientSessionV1::MIN_SUPPORTED_PROTOCOL_VERSION; + constexpr std::uint8_t ClientSessionV1::MAX_SUPPORTED_PROTOCOL_VERSION; + + ///////////////////////////////////// + // Constructor, Destructor, Create + ///////////////////////////////////// + std::shared_ptr ClientSessionV1::create(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger) + { + std::shared_ptr instance(new ClientSessionV1(io_context, address, port, event_callback, logger)); + + instance->resolve_endpoint(); + + return instance; + } + + ClientSessionV1::ClientSessionV1(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger) + : ClientSessionBase(io_context, event_callback) + , address_ (address) + , port_ (port) + , service_call_queue_strand_(*io_context) + , resolver_ (*io_context) + , logger_ (logger) + , accepted_protocol_version_(0) + , state_ (State::NOT_CONNECTED) + , stopped_by_user_ (false) + , service_call_in_progress_ (false) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Created"); + } + + ClientSessionV1::~ClientSessionV1() + { + ClientSessionV1::stop(); + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Deleted"); + } + + ////////////////////////////////////// + // Connection establishement + ////////////////////////////////////// + void ClientSessionV1::resolve_endpoint() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "Resolving endpoint [" + address_ + ":" + std::to_string(port_) + "]..."); + + const asio::ip::tcp::resolver::query query(address_, std::to_string(port_)); + + resolver_.async_resolve(query + , service_call_queue_strand_.wrap([me = enable_shared_from_this::shared_from_this()] + (asio::error_code ec, const asio::ip::tcp::resolver::iterator& resolved_endpoints) + { + if (ec) + { + const std::string message = "Failed resolving endpoint [" + me->address_ + ":" + std::to_string(me->port_) + "]: " + ec.message(); + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + return; + } + else + { +#if ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED + // Verbose-debug log of all endpoints + { + std::string endpoints_str = "Resolved endpoints for " + me->address_ + ": "; + for (auto it = resolved_endpoints; it != asio::ip::tcp::resolver::iterator(); ++it) + { + endpoints_str += endpoint_to_string(*it) + ", "; + } + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, endpoints_str); + } +#endif //ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED + me->connect_to_endpoint(resolved_endpoints); + } + })); + } + + void ClientSessionV1::connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints) + { + // Look for the best endpoint to connect to. If possible, we use a loopback + // endpoint. Otherwise, we just use the first one. + + auto endpoint_to_connect_to = resolved_endpoints->endpoint(); // Default to first endpoint + for (auto it = resolved_endpoints; it != asio::ip::tcp::resolver::iterator(); it++) + { + if (it->endpoint().address().is_loopback()) + { + // If we find a loopback endpoint we use that one. + endpoint_to_connect_to = it->endpoint(); + break; + } + } + + ECAL_SERVICE_LOG_DEBUG(logger_, "Successfully resolved endpoint to [" + endpoint_to_string(endpoint_to_connect_to) + "]. Connecting..."); + + const std::lock_guard socket_lock(socket_mutex_); + socket_.async_connect(endpoint_to_connect_to + , service_call_queue_strand_.wrap([me = shared_from_this(), endpoint_to_connect_to](asio::error_code ec) + { + if (ec) + { + const std::string message = "Failed to connect to endpoint [" + endpoint_to_string(endpoint_to_connect_to) + "]: " + ec.message(); + me->logger_(LogLevel::Error, message); + me->handle_connection_loss_error(message); + return; + } + else + { + ECAL_SERVICE_LOG_DEBUG(me->logger_, "Successfully connected to endpoint [" + endpoint_to_string(endpoint_to_connect_to) + "]"); + + // Disable Nagle's algorithm. Nagles Algorithm will otherwise cause the + // Socket to wait for more data, if it encounters a frame that can still + // fit more data. Obviously, this is an awfull default behaviour, if we + // want to transmit our data in a timely fashion. + { + asio::error_code socket_option_ec; + { + const std::lock_guard socket_lock(me->socket_mutex_); + me->socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + } + if (socket_option_ec) + { + me->logger_(LogLevel::Warning, "[" + get_connection_info_string(me->socket_) + "] " + "Failed setting tcp::no_delay option: " + socket_option_ec.message()); + } + } + + // Start sending the protocol handshake to the server. This will tell us the actual protocol version. + me->send_protocol_handshake_request(); + } + })); + } + + void ClientSessionV1::send_protocol_handshake_request() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Sending protocol handshake request..."); + + // Go to handshake state + { + const std::lock_guard lock(service_state_mutex_); + state_ = State::HANDSHAKE; + } + + // Create buffers + const std::shared_ptr header_buffer = std::make_shared(); + const std::shared_ptr payload_buffer = std::make_shared(); + + // Fill Handshake Request Message + payload_buffer->resize(sizeof(ProtocolHandshakeRequestMessage), '\0'); + ProtocolHandshakeRequestMessage* handshake_request_message = reinterpret_cast(const_cast(payload_buffer->data())); + handshake_request_message->min_supported_protocol_version = MIN_SUPPORTED_PROTOCOL_VERSION; + handshake_request_message->max_supported_protocol_version = MAX_SUPPORTED_PROTOCOL_VERSION; + + // Fill TCP Header + header_buffer->package_size_n = htonl(sizeof(ProtocolHandshakeRequestMessage)); + header_buffer->version = 1; + header_buffer->message_type = MessageType::ProtocolHandshakeRequest; + header_buffer->header_size_n = htons(sizeof(TcpHeaderV1)); + + eCAL::service::ProtocolV1::async_send_payload(socket_, socket_mutex_, header_buffer, payload_buffer + , service_call_queue_strand_.wrap([me = shared_from_this()](asio::error_code ec) + { + const std::string message = "Failed sending protocol handshake request: " + ec.message(); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + me->handle_connection_loss_error(message); + }) + , [me = shared_from_this()]() + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Successfully sent protocol handshake request."); + me->receive_protocol_handshake_response(); + }); + } + + void ClientSessionV1::receive_protocol_handshake_response() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Waiting for protocol handshake response..."); + + eCAL::service::ProtocolV1::async_receive_payload(socket_, socket_mutex_ + , service_call_queue_strand_.wrap([me = shared_from_this()](asio::error_code ec) + { + const std::string message = "Failed receiving protocol handshake response: " + ec.message(); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + me->handle_connection_loss_error(message); + }) + , service_call_queue_strand_.wrap([me = shared_from_this()](const std::shared_ptr>& header_buffer, const std::shared_ptr& payload_buffer) + { + TcpHeaderV1* header = reinterpret_cast(header_buffer->data()); + if (header->message_type != eCAL::service::MessageType::ProtocolHandshakeResponse) + { + // The response is not a Handshake response. + const std::string message = "Received invalid handshake response from server. Expected message type " + + std::to_string(static_cast(eCAL::service::MessageType::ProtocolHandshakeResponse)) + + ", but received " + std::to_string(static_cast(header->message_type)); + me->logger_(LogLevel::Fatal, "[" + get_connection_info_string(me->socket_) + "] " + message); + + me->handle_connection_loss_error(message); + return; + } + else + { + // The response is a Handshake response + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Received a handshake response of " + std::to_string(payload_buffer->size()) + " bytes."); + + // Resize payload if necessary. Will probably never be necessary + if (payload_buffer->size() < sizeof(ProtocolHandshakeResponseMessage)) + { + payload_buffer->resize(sizeof(ProtocolHandshakeResponseMessage), '\0'); + } + const ProtocolHandshakeResponseMessage* handshake_response = reinterpret_cast(payload_buffer->data()); + + if ((handshake_response->accepted_protocol_version >= MIN_SUPPORTED_PROTOCOL_VERSION) + && (handshake_response->accepted_protocol_version <= MIN_SUPPORTED_PROTOCOL_VERSION)) + { + { + const std::lock_guard lock(me->service_state_mutex_); + me->accepted_protocol_version_ = handshake_response->accepted_protocol_version; + me->state_ = State::CONNECTED; + } + + const std::string message = "Connected to server. Using protocol version " + std::to_string(me->accepted_protocol_version_); + me->logger_(LogLevel::Info, "[" + get_connection_info_string(me->socket_) + "] " + message); + + // Call event callback + me->event_callback_(eCAL::service::ClientEventType::Connected, message); + + // Start sending service requests, if there are any + { + const std::lock_guard lock(me->service_state_mutex_); + if (!me->service_call_queue_.empty()) + { + // If there are service calls in the queue, we send the next one. + me->service_call_in_progress_ = true; + me->send_next_service_request(me->service_call_queue_.front().request, me->service_call_queue_.front().response_cb); + me->service_call_queue_.pop_front(); + } + else + { + // If there are no more service calls to send, we go to error-peeking. + // While error peeking we basically do nothing, except from non-destructively + // reading 1 byte from the socket (i.e. without removing it from the socket). + // This will cause asio / the OS to notify us, when the server closed the connection. + + me->service_call_in_progress_ = false; + me->peek_for_error(); + } + } + + return; + } + else + { + const std::string message = "Error connecting to server. Server reported an un-supported protocol version: " + std::to_string(handshake_response->accepted_protocol_version); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + + me->handle_connection_loss_error(message); + return; + } + + } + })); + } + + ////////////////////////////////////// + // Service calls + ////////////////////////////////////// + + bool ClientSessionV1::async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback) + { + // Lock mutex for stopped_by_user_ variable + const std::lock_guard service_state_lock(service_state_mutex_); + + if (stopped_by_user_) + { + return false; + } + else + { + service_call_queue_strand_.post([me = shared_from_this(), request, response_callback]() + { + // Variable that enables us to unlock the mutex before actually calling the callback + bool call_response_callback_with_error(false); + + { + const std::lock_guard lock(me->service_state_mutex_); + if (me->state_ != State::FAILED) + { + // If we are not in failed state, let's check + // whether we directly invoke the call of if we add it to the queue + + if (!me->service_call_in_progress_ && (me->state_ == State::CONNECTED)) + { + // Directly call the the service, iff + // + // - There is no call in progress + // + // and + // + // - We are connected + // + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " No service call in progress. Directly starting next service call."); + me->service_call_in_progress_ = true; + me->send_next_service_request(request, response_callback); + } + else + { + // Add the call to the queue, iff: + // + // - A call is already in progress + // + // or + // + // - We are not connected, yet + // + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Queuing new service request"); + me->service_call_queue_.push_back(ServiceCall{request, response_callback}); + } + } + else + { + // If we are in FAILED state, we directly call the callback with an error. + call_response_callback_with_error = true; + } + } + + if(call_response_callback_with_error) + { + // If we are in FAILED state, we directly call the callback with an error. + // The mutex is unlocked at this point. That is important, as we have no + // influence on when the callback will return. + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " Client is in FAILED state. Calling callback with error."); + response_callback(eCAL::service::Error::ErrorCode::CONNECTION_CLOSED, nullptr); + } + }); + return true; + } + } + + void ClientSessionV1::send_next_service_request(const std::shared_ptr& request, const ResponseCallbackT& response_cb) + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Sending service request..."); + + // Create header_buffer + const std::shared_ptr header_buffer = std::make_shared(); + header_buffer->package_size_n = htonl(static_cast(request->size())); + header_buffer->version = accepted_protocol_version_; + header_buffer->message_type = MessageType::ServiceRequest; + header_buffer->header_size_n = htons(sizeof(TcpHeaderV1)); + + eCAL::service::ProtocolV1::async_send_payload(socket_, socket_mutex_, header_buffer, request + , service_call_queue_strand_.wrap([me = shared_from_this(), response_cb](asio::error_code ec) + { + const std::string message = "Failed sending service request: " + ec.message(); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + + // Call the callback with an error + response_cb(Error(Error::ErrorCode::CONNECTION_CLOSED, message), nullptr); + + // Further handle the error, e.g. unwinding pending service calls and calling the event callback + me->handle_connection_loss_error(message); + }) + , [me = shared_from_this(), response_cb]() + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Successfully sent service request."); + me->receive_service_response(response_cb); + }); + } + + void ClientSessionV1::receive_service_response(const ResponseCallbackT& response_cb) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "[" + get_connection_info_string(socket_) + "] " + "Waiting for service response..."); + + eCAL::service::ProtocolV1::async_receive_payload(socket_, socket_mutex_ + , service_call_queue_strand_.wrap([me = shared_from_this(), response_cb](asio::error_code ec) + { + const std::string message = "Failed receiving service response: " + ec.message(); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + + // Call the callback with an error + response_cb(Error(Error::ErrorCode::CONNECTION_CLOSED, message), nullptr); + + // Further handle the error, e.g. unwinding pending service calls and calling the event callback + me->handle_connection_loss_error(message); + }) + , service_call_queue_strand_.wrap([me = shared_from_this(), response_cb](const std::shared_ptr>& header_buffer, const std::shared_ptr& payload_buffer) + { + TcpHeaderV1* header = reinterpret_cast(header_buffer->data()); + if (header->message_type != eCAL::service::MessageType::ServiceResponse) + { + const std::string message = "Received invalid service response from server. Expected message type " + + std::to_string(static_cast(eCAL::service::MessageType::ServiceResponse)) + + ", but received " + std::to_string(static_cast(header->message_type)); + me->logger_(LogLevel::Fatal, "[" + get_connection_info_string(me->socket_) + "] " + message); + + // Call the callback with an error + response_cb(Error(Error::ErrorCode::PROTOCOL_ERROR, message), nullptr); + + // Further handle the error, e.g. unwinding pending service calls and calling the event callback + me->handle_connection_loss_error(message); + return; + } + else + { + // The response is a Service response + ECAL_SERVICE_LOG_DEBUG(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Successfully received service response of " + std::to_string(payload_buffer->size()) + " bytes"); + + // Call the user's callback + response_cb(Error::OK, payload_buffer); + + // Check if there are more items in the queue. If so, send the next request + // The mutex must be locket, as we access the queue. + { + const std::lock_guard lock(me->service_state_mutex_); + + if (!me->service_call_queue_.empty()) + { + // If there are more items, continue calling the service + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " Service call queue contains " + std::to_string(me->service_call_queue_.size()) + " Entries. Starting next service call."); + me->service_call_in_progress_ = true; + me->send_next_service_request(me->service_call_queue_.front().request, me->service_call_queue_.front().response_cb); + me->service_call_queue_.pop_front(); + } + else + { + // If there are no more service calls to send, we go to error-peeking. + // While error peeking we basically do nothing, except from non-destructively + // reading 1 byte from the socket (i.e. without removing it from the socket). + // This will cause asio / the OS to notify us, when the server closed the connection. + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + " No further servcice calls."); + me->service_call_in_progress_ = false; + me->peek_for_error(); + } + } + } + })); + + + } + + ////////////////////////////////////// + // Status API + ////////////////////////////////////// + + std::string ClientSessionV1::get_address() const + { + return address_; + } + + std::uint16_t ClientSessionV1::get_port() const + { + return port_; + } + + State ClientSessionV1::get_state() const + { + const std::lock_guard lock(service_state_mutex_); + return state_; + } + + std::uint8_t ClientSessionV1::get_accepted_protocol_version() const + { + return accepted_protocol_version_; + } + + int ClientSessionV1::get_queue_size() const + { + const std::lock_guard lock(service_state_mutex_); + return static_cast(service_call_queue_.size()); + } + + ////////////////////////////////////// + // Shutdown + ////////////////////////////////////// + void ClientSessionV1::peek_for_error() + { + const std::shared_ptr> peek_buffer = std::make_shared>(1, '\0'); + + const std::lock_guard socket_lock(socket_mutex_); + socket_.async_receive(asio::buffer(*peek_buffer) + , asio::socket_base::message_peek + , service_call_queue_strand_.wrap([me = shared_from_this(), peek_buffer](const asio::error_code& ec, std::size_t /*bytes_transferred*/) { + if (ec) + { + const std::string message = "Connection loss while idling: " + ec.message(); + me->logger_(eCAL::service::LogLevel::Info, "[" + get_connection_info_string(me->socket_) + "] " + message); + me->handle_connection_loss_error("Connection loss while idling: " + ec.message()); + } + })); + } + + void ClientSessionV1::handle_connection_loss_error(const std::string& error_message) + { + bool call_event_callback (false); // Variable that enables us to unlock the mutex before we execute the event callback. + + // Close the socket, so all waiting async operations are actually woken + // up and fail with an error code. If we wouldn't do that, at least on + // Ubuntu only 1 waiting operations would wake up, while the others would + // indefinitively continue to wait. + // Having multiple async operations on the same socket actually does + // happen, as we are peeking for errors whenever we don't send or + // receive anything. + close_socket(); + + { + const std::lock_guard lock(service_state_mutex_); + + // cancel the connection loss handling, if we are already in FAILED state + if (state_ == State::FAILED) + return; + + if (state_ == State::CONNECTED) + { + // Event callback + call_event_callback = true; + } + + // Set the state to FAILED + state_ = State::FAILED; + + // call all callbacks from the queue with an error + if (!service_call_queue_.empty()) + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Calling " + std::to_string(service_call_queue_.size()) + " service callbacks with error"); + call_all_callbacks_with_error(); + } + } + + if (call_event_callback) + { + event_callback_(eCAL::service::ClientEventType::Disconnected, error_message); + } + } + + void ClientSessionV1::call_all_callbacks_with_error() + { + service_call_queue_strand_.post([me = shared_from_this()]() + { + ServiceCall first_service_call; + bool more_service_calls(false); + + { + // Lock the mutex and manipulate the queue. We want the mutex unlocked for the event callback call. + const std::lock_guard lock(me->service_state_mutex_); + + if (me->service_call_queue_.empty()) + return; + + first_service_call = std::move(me->service_call_queue_.front()); + me->service_call_queue_.pop_front(); + + more_service_calls = (!me->service_call_queue_.empty()); + } + + // Execute the callback with an error + first_service_call.response_cb(eCAL::service::Error::ErrorCode::CONNECTION_CLOSED, nullptr); // TODO: I should probably store the error that lead to this somewhere and tell the actual error. + + // If there are more sevice calls, call those with an error, as well + if (more_service_calls) + me->call_all_callbacks_with_error(); + }); + } + + void ClientSessionV1::stop() + { + // This is a function that gets used both by the API and by potentially + // multiple failing async operations at once. + + { + // Set the stopped_by_user_ flag to true, so that the async operations stop enqueuing new service calls. + std::lock_guard service_state_lock(service_state_mutex_); + stopped_by_user_ = true; + } + + { + close_socket(); + } + } + + void ClientSessionV1::close_socket() + { + // Close the socket, so all waiting async operations will fail with an + // error code. This will also cause the client to call all pending + // callbacks with an error. + const std::lock_guard socket_lock(socket_mutex_); + + if (socket_.is_open()) + { + { + asio::error_code ec; + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + } + + { + asio::error_code ec; + socket_.close(ec); + } + } + } + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/client_session_impl_v1.h b/src/service/ecal_service/src/client_session_impl_v1.h new file mode 100644 index 0000000..3f9de5b --- /dev/null +++ b/src/service/ecal_service/src/client_session_impl_v1.h @@ -0,0 +1,160 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include "client_session_impl_base.h" +#include + +#include +#include + +namespace eCAL +{ + namespace service + { + class ClientSessionV1 + : public ClientSessionBase + , public std::enable_shared_from_this + { + ////////////////////////////////////// + // Internal types + ////////////////////////////////////// + private: + struct ServiceCall + { + std::shared_ptr request; + ResponseCallbackT response_cb; + }; + + ///////////////////////////////////// + // Constructor, Destructor, Create + ///////////////////////////////////// + public: + static std::shared_ptr create(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger_ = default_logger("Service Client V1")); + + protected: + ClientSessionV1(const std::shared_ptr& io_context + , const std::string& address + , std::uint16_t port + , const EventCallbackT& event_callback + , const LoggerT& logger); + + public: + // Delete copy / move constructor and assignment operator + ClientSessionV1(const ClientSessionV1&) = delete; // Copy construct + ClientSessionV1(ClientSessionV1&&) = delete; // Move construct + + ClientSessionV1& operator=(const ClientSessionV1&) = delete; // Copy assign + ClientSessionV1& operator=(ClientSessionV1&&) = delete; // Move assign + + ~ClientSessionV1() override; + + ////////////////////////////////////// + // Connection establishement + ////////////////////////////////////// + private: + void resolve_endpoint(); + void connect_to_endpoint(const asio::ip::tcp::resolver::iterator& resolved_endpoints); + + void send_protocol_handshake_request(); + void receive_protocol_handshake_response(); + + ////////////////////////////////////// + // Service calls + ////////////////////////////////////// + public: + bool async_call_service(const std::shared_ptr& request, const ResponseCallbackT& response_callback) override; + + private: + void send_next_service_request(const std::shared_ptr& request, const ResponseCallbackT& response_cb); + void receive_service_response(const ResponseCallbackT& response_cb); + + ////////////////////////////////////// + // Status API + ////////////////////////////////////// + public: + std::string get_address() const override; + std::uint16_t get_port() const override; + State get_state() const override; + std::uint8_t get_accepted_protocol_version() const override; + int get_queue_size() const override; + + ////////////////////////////////////// + // Shutdown + ////////////////////////////////////// + public: + void peek_for_error(); + void handle_connection_loss_error(const std::string& message); + void call_all_callbacks_with_error(); + + /** + * @brief Stop the client. + * + * This will close the connection AND set the state to stopped. This + * means, that no further async service calls will be accepted. From now + * on, all async service calls will just return false and the callback + * will not be called any more in order to give the io_thread the chance + * of shutting down. + */ + void stop() override; + + private: + /** + * @brief Closes the internal socket + * + * This will close the connection without setting the state to stopped. + * This means, that further async service calls will still be accepted, + * but they will fail. The failure is still communicated by calling the + * callback. For a proper shutdown, where the plan is to stop the + * io_context thread as well, the stop() function must be used, or the + * client must be destroyed. + */ + void close_socket(); + + + ////////////////////////////////////// + // Member variables + ////////////////////////////////////// + private: + static constexpr std::uint8_t MIN_SUPPORTED_PROTOCOL_VERSION = 1; + static constexpr std::uint8_t MAX_SUPPORTED_PROTOCOL_VERSION = 1; + + const std::string address_; //!< The original address that this client was created with. + const std::uint16_t port_; //!< The original port that this client was created with. + + asio::io_context::strand service_call_queue_strand_; + asio::ip::tcp::resolver resolver_; + const LoggerT logger_; + + std::atomic accepted_protocol_version_; + + mutable std::mutex service_state_mutex_; + State state_; //!< The connection state of this client. Protected by service_state_mutex_. + bool stopped_by_user_; //!< Telling whether we actively stopped the client. Protected by service_state_mutex_. When set, the client will not accept any more async service calls. + + std::deque service_call_queue_; + bool service_call_in_progress_; + }; + } +} diff --git a/src/service/ecal_service/src/condition_variable_signaler.h b/src/service/ecal_service/src/condition_variable_signaler.h new file mode 100644 index 0000000..d6812c2 --- /dev/null +++ b/src/service/ecal_service/src/condition_variable_signaler.h @@ -0,0 +1,54 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include + +// Class that signals a condition variable when it is destroyed +class ConditionVariableSignaler +{ +public: + ConditionVariableSignaler(std::condition_variable& condition_variable, std::mutex& mutex, bool& is_signaled) + : mutex_ (mutex) + , condition_variable_(condition_variable) + , is_signaled_ (is_signaled) + {} + + ~ConditionVariableSignaler() + { + const std::lock_guard lock(mutex_); + is_signaled_ = true; + condition_variable_.notify_all(); + } + + // Delete copy constructor and assignment operator + ConditionVariableSignaler(const ConditionVariableSignaler&) = delete; + ConditionVariableSignaler& operator=(const ConditionVariableSignaler&) = delete; + + // Default move constructor and assignment operator + ConditionVariableSignaler(ConditionVariableSignaler&&) = default; + ConditionVariableSignaler& operator=(ConditionVariableSignaler&&) = default; + + private: + std::mutex& mutex_; + std::condition_variable& condition_variable_; + bool& is_signaled_; +}; diff --git a/src/service/ecal_service/src/log_defs.h b/src/service/ecal_service/src/log_defs.h new file mode 100644 index 0000000..1ca102e --- /dev/null +++ b/src/service/ecal_service/src/log_defs.h @@ -0,0 +1,84 @@ +/* ========================= 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 ================================= +*/ + + +#pragma once + +#include + +///////////////////////////////////////////// +// Enable / disable debug logging +///////////////////////////////////////////// + +// +// If DEBUG_VERBOSE is not set by the user, we enable it when compiling in +// Debug mode. The user can still omit those verbose messages by setting a +// logger function that drops the verbose messages. +// +#ifndef ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED + #ifdef NDEBUG + #define ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED 0 + #else + #define ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED 1 + #endif // NDEBUG +#endif // !ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED + +// +// If the DEBUG_VERBOSE is enabled, we also enable the normal DEBUG logging +// +#if (ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED) + #define ECAL_SERVICE_LOG_DEBUG_ENABLED 1 +#endif + +// +// If we haven't decided yet whether DEBUG logging shall be enabled, we enable +// it when compiling in Debug mode and disable it otherwise. +// +#ifndef ECAL_SERVICE_LOG_DEBUG_ENABLED + #ifdef NDEBUG + #define ECAL_SERVICE_LOG_DEBUG_ENABLED 0 + #else + #define ECAL_SERVICE_LOG_DEBUG_ENABLED 1 + #endif // NDEBUG +#endif // !ECAL_SERVICE_LOG_DEBUG_ENABLED + +///////////////////////////////////////////// +// Debug logging macros +///////////////////////////////////////////// + +// +// Debug logging macro that evaluates to nothing, if Debug logging is disabled. +// This lets the compiler better optimize the code. +// +#if ECAL_SERVICE_LOG_DEBUG_ENABLED + #define ECAL_SERVICE_LOG_DEBUG(logger, msg) logger(eCAL::service::LogLevel::Debug, msg) +#else + #define ECAL_SERVICE_LOG_DEBUG(...) /**/ +#endif + +// +// Debug Verbose logging macro that evaluates to nothing, if Debug Verbose logging is disabled. +// This lets the compiler better optimize the code. +// +#if ECAL_SERVICE_LOG_DEBUG_VERBOSE_ENABLED + #define ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger,msg) do { logger(eCAL::service::LogLevel::DebugVerbose, msg); } while (0) +#else + #define ECAL_SERVICE_LOG_DEBUG_VERBOSE(...) /**/ +#endif + diff --git a/src/service/ecal_service/src/log_helpers.h b/src/service/ecal_service/src/log_helpers.h new file mode 100644 index 0000000..e4409ab --- /dev/null +++ b/src/service/ecal_service/src/log_helpers.h @@ -0,0 +1,66 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace service + { + // Forward declarations + inline std::string get_connection_info_string(const asio::ip::tcp::socket& socket); + inline std::string endpoint_to_string(const asio::ip::tcp::endpoint& endpoint); + + inline std::string get_connection_info_string(const asio::ip::tcp::socket& socket) + { + std::string local_endpoint_string = "???"; + std::string remote_endpoint_string = "???"; + + // Form local endpoint string + { + asio::error_code ec; + const auto endpoint = socket.local_endpoint(ec); + if (!ec) + local_endpoint_string = endpoint_to_string(endpoint); + } + + // form remote endpoint string + { + asio::error_code ec; + const auto endpoint = socket.remote_endpoint(ec); + if (!ec) + remote_endpoint_string = endpoint_to_string(endpoint); + } + + return local_endpoint_string + " -> " + remote_endpoint_string; + } + + inline std::string endpoint_to_string(const asio::ip::tcp::endpoint& endpoint) + { + asio::error_code ec; + const std::string address_string = endpoint.address().to_string(ec); + if (!ec) + return address_string + ":" + std::to_string(endpoint.port()); + else + return "???"; + } + } // namespace service +}// namespace eCAL diff --git a/src/service/ecal_service/src/protocol_layout.h b/src/service/ecal_service/src/protocol_layout.h new file mode 100644 index 0000000..3197e05 --- /dev/null +++ b/src/service/ecal_service/src/protocol_layout.h @@ -0,0 +1,73 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace service + { + // Message type used since protocol version 1 + enum class MessageType: std::uint8_t + { + Undefined = 0, + ProtocolHandshakeRequest = 1, + ProtocolHandshakeResponse = 2, + ServiceRequest = 3, + ServiceResponse = 4, + }; + +#pragma pack(push, 1) + struct TcpHeaderV0 + { + std::uint32_t package_size_n = 0; // package size in network byte order + std::uint32_t reserved1 = 0; // reserved + std::uint64_t reserved2 = 0; // reserved + }; + + // TCP Header + // - Used for service request since protocol version 1 + // - Used for response since protocol version 0 + struct TcpHeaderV1 + { + std::uint32_t package_size_n = 0; // package size in network byte order + std::uint8_t version = 0; // protocol version (since protocol V1 / eCAL 5.12) + MessageType message_type = MessageType::Undefined; // message type (since protocol V1 / eCAL 5.12) + std::uint16_t header_size_n = 0; // header size in network byte order (since protocol V1 / eCAL 5.12) + std::uint64_t reserved = 0; // reserved + }; + + // Handshake Request Message, since protocol v1 + struct ProtocolHandshakeRequestMessage + { + std::uint8_t min_supported_protocol_version = 0; + std::uint8_t max_supported_protocol_version = 0; + }; + + // Handshake Response Message, since protocol v1 + struct ProtocolHandshakeResponseMessage + { + std::uint8_t accepted_protocol_version = 0; + }; +#pragma pack(pop) + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/protocol_v0.cpp b/src/service/ecal_service/src/protocol_v0.cpp new file mode 100644 index 0000000..05650db --- /dev/null +++ b/src/service/ecal_service/src/protocol_v0.cpp @@ -0,0 +1,127 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "protocol_v0.h" + +#include "protocol_layout.h" + +namespace eCAL +{ + namespace service + { + namespace ProtocolV0 + { + namespace + { + void read_header(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb); + void read_payload(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr& header_buffer, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb); + + /////////////////////////////////////////////////// + // Read and write implementation + /////////////////////////////////////////////////// + void read_header(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb) + { + // Allocate a buffer for the header as we know it. We can make it larger + // later, if necessary. We zero the buffer. + const auto header_buffer = std::make_shared(); + constexpr size_t header_buffer_size = sizeof(eCAL::service::TcpHeaderV0); + + // Receive data from the socket: + // - Maximum "bytes_to_read_now" + // - At least "bytes_to_read_now" + // => We read exactly the "bytes_to_read_now" amount of bytes + const std::lock_guard socket_lock(socket_mutex); + asio::async_read(socket + , asio::buffer(reinterpret_cast(header_buffer.get()), header_buffer_size) + , asio::transfer_at_least(header_buffer_size) + , [&socket, &socket_mutex, header_buffer, error_cb, success_cb](asio::error_code ec, std::size_t /*bytes_read*/) + { + if (ec) + { + // Call error callback + error_cb(ec); + return; + } + + // Read the payload that comes after the header + read_payload(socket, socket_mutex, header_buffer, error_cb, success_cb); + }); + } + + void read_payload(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr& header_buffer, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb) + { + // Read how many bytes we will get as payload + const uint32_t payload_size = ntohl(header_buffer->package_size_n); + + // Reserver enough memory for receiving the entire payload. The payload is + // represented as an std::string for legacy, reasons. It is not textual data. + const std::shared_ptr payload_buffer = std::make_shared(payload_size, '\0'); + + // Read all the payload data into the payload_buffer + const std::lock_guard socket_lock(socket_mutex); + asio::async_read(socket + , asio::buffer(const_cast(payload_buffer->data()), payload_buffer->size()) + , asio::transfer_at_least(payload_buffer->size()) + , [header_buffer, payload_buffer, error_cb, success_cb](asio::error_code ec, std::size_t /*bytes_read*/) + { + if (ec) + { + // Call error callback + error_cb(ec); + return; + } + + // Call success callback + success_cb(header_buffer, payload_buffer); + }); + + } + } + + /////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////// + void async_send_payload_with_header(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr& header_buffer, const std::shared_ptr& payload_buffer, const ErrorCallbackT& error_cb, const SendSuccessCallback& success_cb) + { + const std::vector buffer_list { asio::buffer(reinterpret_cast(header_buffer.get()), sizeof(eCAL::service::TcpHeaderV0)) + , asio::buffer(*payload_buffer)}; + + const std::lock_guard socket_lock(socket_mutex); + asio::async_write(socket + , buffer_list + , [header_buffer, payload_buffer, error_cb, success_cb](asio::error_code ec, std::size_t /*bytes_sent*/) + { + if (ec) + { + // Call error callback + error_cb(ec); + return; + } + success_cb(); + }); + + } + + void async_receive_payload_with_header(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb) + { + read_header(socket, socket_mutex, error_cb, success_cb); + } + } // namespace ProtocolV1 + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/protocol_v0.h b/src/service/ecal_service/src/protocol_v0.h new file mode 100644 index 0000000..e9a7c84 --- /dev/null +++ b/src/service/ecal_service/src/protocol_v0.h @@ -0,0 +1,46 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "protocol_layout.h" + +namespace eCAL +{ + namespace service + { + namespace ProtocolV0 + { + using ErrorCallbackT = std::function; + using SendSuccessCallback = std::function; + using ReceiveSuccessCallback = std::function& header_buffer, const std::shared_ptr& payload_buffer)>; + + void async_send_payload_with_header (asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr& header_buffer, const std::shared_ptr& payload_buffer, const ErrorCallbackT& error_cb, const SendSuccessCallback& success_cb); + void async_receive_payload_with_header(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb); + } + } +} diff --git a/src/service/ecal_service/src/protocol_v1.cpp b/src/service/ecal_service/src/protocol_v1.cpp new file mode 100644 index 0000000..fc00787 --- /dev/null +++ b/src/service/ecal_service/src/protocol_v1.cpp @@ -0,0 +1,191 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "protocol_v1.h" + +#include "protocol_layout.h" + +namespace eCAL +{ + namespace service + { + namespace ProtocolV1 + { + namespace + { + void read_header_start(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb); + void read_header_rest(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr>& header_buffer, size_t bytes_already_read, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb); + void read_payload(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr>& header_buffer, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb); + + /////////////////////////////////////////////////// + // Read and write implementation + /////////////////////////////////////////////////// + void read_header_start(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb) + { + // Get size of the entire header as it is currently known. What comes from + // the network may be larger or smaller. + constexpr size_t header_size = sizeof(eCAL::service::TcpHeaderV1); + + // We read the first 8 bytes of the header first. The header does not start + // with the header_size, but it contains it at byte 7+8, which is why we + // read the first 8 bytes here, do determine the header size. + constexpr size_t bytes_to_read_now = 8; + + // Allocate a buffer for the header as we know it. We can make it larger + // later, if necessary. We zero the buffer. + const std::shared_ptr> header_buffer = std::make_shared>(header_size, '\0'); + + // Receive data from the socket: + // - Maximum "bytes_to_read_now" + // - At least "bytes_to_read_now" + // => We read exactly the "bytes_to_read_now" amount of bytes + const std::lock_guard socket_lock(socket_mutex); + asio::async_read(socket + , asio::buffer(header_buffer->data(), bytes_to_read_now) + , asio::transfer_at_least(bytes_to_read_now) + , [&socket, &socket_mutex, header_buffer, error_cb, success_cb](asio::error_code ec, std::size_t bytes_read) + { + if (ec) + { + // Call error callback + error_cb(ec); + return; + } + + // Read the rest of the header! + read_header_rest(socket, socket_mutex, header_buffer, bytes_read, error_cb, success_cb); + }); + } + + void read_header_rest(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr>& header_buffer, size_t bytes_already_read, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb) + { + // Check how big the remote header claims to be + const size_t remote_header_size = ntohs(reinterpret_cast(header_buffer->data())->header_size_n); + + // Resize the header buffer if necessary. We will not be able to use those + // bytes, as we don't know what they mean, but we must receive them anyways. + if (remote_header_size > header_buffer->size()) + { + header_buffer->resize(remote_header_size, '\0'); + } + + // Calculate how many bytes we still need to read until we have received the + // entire header + const size_t bytes_still_to_read = header_buffer->size() - bytes_already_read; + + // If the header is finished, we directly go over to reading the payload + // Note: This should actually never happen, as after the already read + // 8 bytes should come at least 8 reserved bytes. + if (bytes_still_to_read <= 0) + { + read_payload(socket, socket_mutex, header_buffer, error_cb, success_cb); + return; + } + + // Read the remaining bytes from the header + const std::lock_guard socket_lock(socket_mutex); + asio::async_read(socket + , asio::buffer(&((*header_buffer)[bytes_already_read]), bytes_still_to_read) + , asio::transfer_at_least(bytes_still_to_read) + , [&socket, &socket_mutex, header_buffer, error_cb, success_cb](asio::error_code ec, std::size_t /*bytes_read*/) + { + if (ec) + { + // Call callback + error_cb(ec); + return; + } + + // Start reading the payload! + const uint32_t payload_size = ntohl(reinterpret_cast(header_buffer->data())->package_size_n); + + if (payload_size > 0) + { + // If there is a payload, read it + read_payload(socket, socket_mutex, header_buffer, error_cb, success_cb); + } + else + { + // If there is no payload, directly execute the callback with an empty string + success_cb(header_buffer, std::make_shared()); + } + }); + } + + void read_payload(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr>& header_buffer, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb) + { + // Read how many bytes we will get as payload + const uint32_t payload_size = ntohl(reinterpret_cast(header_buffer->data())->package_size_n); + + // Reserver enough memory for receiving the entire payload. The payload is + // represented as an std::string for legacy, reasons. It is not textual data. + const std::shared_ptr payload_buffer = std::make_shared(payload_size, '\0'); + + // Read all the payload data into the payload_buffer + const std::lock_guard socket_lock(socket_mutex); + asio::async_read(socket + , asio::buffer(const_cast(payload_buffer->data()), payload_buffer->size()) + , asio::transfer_at_least(payload_buffer->size()) + , [header_buffer, payload_buffer, error_cb, success_cb](asio::error_code ec, std::size_t /*bytes_read*/) + { + if (ec) + { + // Call error callback + error_cb(ec); + return; + } + + // Call success callback + success_cb(header_buffer, payload_buffer); + }); + + } + } + + /////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////// + void async_send_payload (asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr& header_buffer, const std::shared_ptr& payload_buffer, const ErrorCallbackT& error_cb, const SendSuccessCallback& success_cb) + { + const std::vector buffer_list { asio::buffer(reinterpret_cast(header_buffer.get()), sizeof(eCAL::service::TcpHeaderV1)) + , asio::buffer(*payload_buffer)}; + + const std::lock_guard socket_lock(socket_mutex); + asio::async_write(socket + , buffer_list + , [header_buffer, payload_buffer, error_cb, success_cb](asio::error_code ec, std::size_t /*bytes_sent*/) + { + if (ec) + { + // Call error callback + error_cb(ec); + return; + } + success_cb(); + }); + + } + + void async_receive_payload(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb) + { + read_header_start(socket, socket_mutex, error_cb, success_cb); + } + } // namespace ProtocolV1 + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/protocol_v1.h b/src/service/ecal_service/src/protocol_v1.h new file mode 100644 index 0000000..025a6fe --- /dev/null +++ b/src/service/ecal_service/src/protocol_v1.h @@ -0,0 +1,46 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "protocol_layout.h" + +namespace eCAL +{ + namespace service + { + namespace ProtocolV1 + { + using ErrorCallbackT = std::function; + using SendSuccessCallback = std::function; + using ReceiveSuccessCallback = std::function>& header_buffer, const std::shared_ptr& payload_buffer)>; + + void async_send_payload (asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const std::shared_ptr& header_buffer, const std::shared_ptr& payload_buffer, const ErrorCallbackT& error_cb, const SendSuccessCallback& success_cb); + void async_receive_payload(asio::ip::tcp::socket& socket, std::mutex& socket_mutex, const ErrorCallbackT& error_cb, const ReceiveSuccessCallback& success_cb); + } + } +} diff --git a/src/service/ecal_service/src/server.cpp b/src/service/ecal_service/src/server.cpp new file mode 100644 index 0000000..6972a2f --- /dev/null +++ b/src/service/ecal_service/src/server.cpp @@ -0,0 +1,94 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include + +#include "server_impl.h" + +namespace eCAL +{ + namespace service + { + /////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////// + std::shared_ptr Server::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const LoggerT& logger + , const DeleteCallbackT& delete_callback) + { + auto deleter = [delete_callback](Server* server) + { + delete_callback(server); + delete server; // NOLINT(cppcoreguidelines-owning-memory) + }; + + return std::shared_ptr(new Server(io_context, protocol_version, port, service_callback, parallel_service_calls_enabled, event_callback, logger), deleter); + } + + std::shared_ptr Server::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const LoggerT& logger) + { + return std::shared_ptr(new Server(io_context, protocol_version, port, service_callback, parallel_service_calls_enabled, event_callback, logger)); + } + + std::shared_ptr Server::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const DeleteCallbackT& delete_callback) + { + return Server::create(io_context, protocol_version, port, service_callback, parallel_service_calls_enabled, event_callback, default_logger("Service Server"), delete_callback); + } + + Server::Server(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const EventCallbackT& event_callback + , const LoggerT& logger) + { + impl_ = ServerImpl::create(io_context, protocol_version, port, service_callback, parallel_service_calls_enabled, event_callback, logger); + } + + /////////////////////////////////////////// + // API + /////////////////////////////////////////// + + bool Server::is_connected() const { return impl_->is_connected(); } + int Server::get_connection_count() const { return impl_->get_connection_count(); } + std::uint16_t Server::get_port() const { return impl_->get_port(); } + void Server::stop() { impl_->stop(); } + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/server_impl.cpp b/src/service/ecal_service/src/server_impl.cpp new file mode 100644 index 0000000..027316b --- /dev/null +++ b/src/service/ecal_service/src/server_impl.cpp @@ -0,0 +1,312 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "server_impl.h" + +#include + +#include "server_session_impl_v1.h" +#include "server_session_impl_v0.h" + +#include "log_defs.h" + +namespace eCAL +{ + namespace service + { + /////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////// + + std::shared_ptr ServerImpl::create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServerServiceCallbackT& service_callback // TODO: The service callback may block a long time. This may cause the entire network stack to wait for long running service callbacks. Maybe it is a good idea to have some kind of "future" object, that the user can hand to some differen io_context or to a custom thread. That thread will then work on the object and call some function / let it go out of scope, which will then trigger sending the response to the client. + , bool parallel_service_calls_enabled + , const ServerEventCallbackT& event_callback + , const LoggerT& logger) + { + // Create a new instance with the protected constructor + // Note: make_shared not possible, because constructor is protected + auto instance = std::shared_ptr(new ServerImpl(io_context, service_callback, parallel_service_calls_enabled, event_callback, logger)); + + // Directly Start accepting new connections + instance->start_accept(protocol_version, port); + + // Return the created and started instance + return instance; + } + + ServerImpl::ServerImpl(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const ServerEventCallbackT& event_callback + , const LoggerT& logger) + : io_context_ (io_context) + , acceptor_ (*io_context) + , parallel_service_calls_enabled_(parallel_service_calls_enabled) + , service_callback_common_strand_(std::make_shared(*io_context)) + , service_callback_ (service_callback) + , event_callback_ (event_callback) + , logger_ (logger) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Service Created"); + } + + ServerImpl::~ServerImpl() + { + stop(); + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Service Deleted"); + } + + /////////////////////////////////////////// + // API + /////////////////////////////////////////// + + bool ServerImpl::is_connected() const + { + return get_connection_count() > 0; + } + + int ServerImpl::get_connection_count() const + { + const std::lock_guard session_list_lock(session_list_mutex_); + int connection_count = 0; + + // Iterate over all sessions and count the ones that are still alive + for (const auto& session : session_list_) + { + auto session_ptr = session.lock(); + if (session_ptr && (session_ptr->get_state() != eCAL::service::State::FAILED)) + { + ++connection_count; + } + } + + return connection_count; + } + + std::uint16_t ServerImpl::get_port() const + { + asio::error_code ec; + + const std::lock_guard acceptor_lock(acceptor_mutex_); + + auto endpoint = acceptor_.local_endpoint(ec); + if (!ec) + { + return endpoint.port(); + } + else + { + return 0; + } + } + + void ServerImpl::start_accept(std::uint8_t protocol_version, std::uint16_t port) + { + ECAL_SERVICE_LOG_DEBUG(logger_, "Service starting to accept new connections..."); + + // set up the acceptor to listen on the tcp port + + const asio::ip::tcp::endpoint endpoint(asio::ip::address_v6(), port); + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Service Server: Opening acceptor..."); + { + asio::error_code ec; + + { + const std::lock_guard acceptor_lock(acceptor_mutex_); + acceptor_.open(endpoint.protocol(), ec); + } + if (ec) + { + logger_(eCAL::service::LogLevel::Error, "Service Server: Error opening acceptor:" + ec.message()); + // return false; What do do here? + } + } + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Service Server: Setting \"reuse_address\" option..."); + + + { + asio::error_code ec; + { + acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true), ec); + const std::lock_guard acceptor_lock(acceptor_mutex_); + } + if (ec) + { + logger_(eCAL::service::LogLevel::Error, "Service Server: Error setting \"reuse_address\" option:" + ec.message()); + // return false; What do do here? + } + } + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Service Server: Binding acceptor to endpoint..."); + + { + asio::error_code ec; + { + const std::lock_guard acceptor_lock(acceptor_mutex_); + acceptor_.bind(endpoint, ec); + } + if (ec) + { + logger_(eCAL::service::LogLevel::Error, "Service Server: Error binding acceptor:" + ec.message()); + // return false; What do do here? + } + } + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Service Server: Listening on acceptor..."); + + { + asio::error_code ec; + { + const std::lock_guard acceptor_lock(acceptor_mutex_); + acceptor_.listen(asio::socket_base::max_listen_connections, ec); + } + if (ec) + { + logger_(eCAL::service::LogLevel::Error, "Service Server: Error listening on acceptor: " + ec.message()); + // return false; What do do here? + } + } + + wait_for_next_client(protocol_version); + } + + void ServerImpl::wait_for_next_client(std::uint8_t protocol_version) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Service waiting for next client..."); + + const eCAL::service::ServerSessionBase::ShutdownCallbackT shutdown_callback + = [weak_me = std::weak_ptr(shared_from_this())](const std::shared_ptr& session_to_remove) -> void + { + // Create a shared_ptr to the class. If it doesn't exist + // anymore, we will get a nullpointer. In that case, we cannot + // execute the callback. + const std::shared_ptr me = weak_me.lock(); + if (me) + { + const std::lock_guard session_list_lock(me->session_list_mutex_); + + // Remove the session from the session list + const auto session_it = std::find_if(me->session_list_.begin(), me->session_list_.end() + , [session_to_remove](const auto& existing_session) { return session_to_remove == existing_session.lock(); }); + if (session_it != me->session_list_.end()) + { + me->session_list_.erase(session_it); + } + } + }; + + std::shared_ptr new_session; + + std::shared_ptr service_callback_strand; + if (parallel_service_calls_enabled_) + { + service_callback_strand = std::make_shared(*io_context_); + } + else + { + service_callback_strand = service_callback_common_strand_; + } + + if (protocol_version == 0) + { + new_session = eCAL::service::ServerSessionV0::create(io_context_, service_callback_, service_callback_strand, event_callback_, shutdown_callback, logger_); + } + else + { + new_session = eCAL::service::ServerSessionV1::create(io_context_, service_callback_, service_callback_strand, event_callback_, shutdown_callback, logger_); + } + + // Accept new session. + // By only storing a weak_ptr to this, we assure that the user can still + // delete the service from the outside. + const std::lock_guard acceptor_lock(acceptor_mutex_); + acceptor_.async_accept(new_session->socket() + , [weak_me = std::weak_ptr(shared_from_this()), new_session, protocol_version, logger_copy = logger_](auto ec) + { + if (ec) + { + logger_copy(LogLevel::Info, "Service shutting down: " + ec.message()); + return; + } + else + { + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_copy, "Service client initiated connection..."); + + const std::shared_ptr me = weak_me.lock(); + if (me) + { + // Add the new session to the session list. The session list is a list of weak_ptr. + // Therefore, the session list will not keep the session alive, they will do that themselves. + const std::lock_guard session_list_lock(me->session_list_mutex_); + me->session_list_.push_back(new_session); + + // Start the new session, that now has a connection + // The session will call the callback and increase the connection count + new_session->start(); + } + } + + // Continue creating and accepting the next session + const std::shared_ptr me = weak_me.lock(); + if (me) + { + me->wait_for_next_client(protocol_version); + } + else + { + logger_copy(LogLevel::Info, "Service shutting down."); + } + }); + + } + + void ServerImpl::stop() + { + { + // Lock mutex for making the stop function thread safe + const std::lock_guard acceptor_lock(acceptor_mutex_); + + // Close acceptor, if necessary + if (acceptor_.is_open()) + { + asio::error_code ec; + acceptor_.close(ec); + } + } + + // Stop all sessions to clients + { + const std::lock_guard session_list_lock(session_list_mutex_); + for(const auto& session_weak : session_list_) + { + const auto session = session_weak.lock(); + if (session) + session->stop(); + } + } + } + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/server_impl.h b/src/service/ecal_service/src/server_impl.h new file mode 100644 index 0000000..ba22554 --- /dev/null +++ b/src/service/ecal_service/src/server_impl.h @@ -0,0 +1,114 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4834) +#endif +#include +#include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include + +#include "server_session_impl_base.h" + +namespace eCAL +{ + namespace service + { + class ServerImpl : public std::enable_shared_from_this + { + /////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////// + + public: + static std::shared_ptr create(const std::shared_ptr& io_context + , std::uint8_t protocol_version + , std::uint16_t port + , const ServerServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const ServerEventCallbackT& event_callback + , const LoggerT& logger = default_logger("Service Server")); + + protected: + ServerImpl(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const ServerEventCallbackT& event_callback + , const LoggerT& logger); + + public: + ServerImpl(const ServerImpl&) = delete; // Copy construct + ServerImpl(ServerImpl&&) = delete; // Move construct + + ServerImpl& operator=(const ServerImpl&) = delete; // Copy assign + ServerImpl& operator=(ServerImpl&&) = delete; // Move assign + + ~ServerImpl(); + + /////////////////////////////////////////// + // API + /////////////////////////////////////////// + + public: + bool is_connected() const; + int get_connection_count() const; + std::uint16_t get_port() const; + + void stop(); + + private: + void start_accept(std::uint8_t protocol_version, std::uint16_t port); + void wait_for_next_client(std::uint8_t protocol_version); + + /////////////////////////////////////////// + // Member Variables + /////////////////////////////////////////// + + private: + const std::shared_ptr io_context_; + asio::ip::tcp::acceptor acceptor_; + mutable std::mutex acceptor_mutex_; //!< Mutex for stopping the server. The stop() function is both used externally (via API) and from within the server itself. Closing the acceptor is not thread-safe, so we need to protect it. + + const bool parallel_service_calls_enabled_; + const std::shared_ptr service_callback_common_strand_; + const ServerServiceCallbackT service_callback_; + const ServerEventCallbackT event_callback_; + + mutable std::mutex session_list_mutex_; + std::vector> session_list_; + + const LoggerT logger_; + + }; + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/server_manager.cpp b/src/service/ecal_service/src/server_manager.cpp new file mode 100644 index 0000000..ade0f1e --- /dev/null +++ b/src/service/ecal_service/src/server_manager.cpp @@ -0,0 +1,109 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +namespace eCAL +{ + namespace service + { + /////////////////////////////////////////////////////// + // Constructor, Destructor, Create + /////////////////////////////////////////////////////// + std::shared_ptr ServerManager::create(const std::shared_ptr& io_context, const LoggerT& logger) + { + return std::shared_ptr(new ServerManager(io_context, logger)); + } + + ServerManager::ServerManager(const std::shared_ptr& io_context, const LoggerT& logger) + : io_context_(io_context) + , logger_(logger) + , stopped_(false) + , work_(std::make_unique(*io_context)) + {} + + ServerManager::~ServerManager() + { + stop(); + } + + /////////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////////// + std::shared_ptr ServerManager::create_server(std::uint8_t protocol_version + , std::uint16_t port + , const Server::ServiceCallbackT& service_callback + , bool parallel_service_calls_enabled + , const Server::EventCallbackT& event_callback) + { + const std::lock_guard lock(server_manager_mutex_); + if (stopped_) + { + return nullptr; + } + + auto delete_callback = [weak_me = std::weak_ptr(shared_from_this())](Server* server) + { + auto me = weak_me.lock(); + if (me) + { + // Remove the session from the sessions_ map + const std::lock_guard lock(me->server_manager_mutex_); + me->sessions_.erase(server); + } + }; + auto server = Server::create(io_context_, protocol_version, port, service_callback, parallel_service_calls_enabled, event_callback, logger_, delete_callback); + sessions_.emplace(server.get(), server); + return server; + } + + size_t ServerManager::server_count() const + { + const std::lock_guard lock(server_manager_mutex_); + return sessions_.size(); + } + + void ServerManager::stop() + { + std::map> sessions_copy; + { + const std::lock_guard lock(server_manager_mutex_); + stopped_ = true; + sessions_copy = sessions_; + } + + // stop all dservers without having the mutex locked, so we don't crash, when this thread directly calls the delete callback, that itself needs to have the mutex locked. + for (auto& server_weak : sessions_copy) + { + auto server = server_weak.second.lock(); + if (server) + server->stop(); + } + + work_.reset(); + } + + bool ServerManager::is_stopped() const + { + const std::lock_guard lock(server_manager_mutex_); + return stopped_; + } + + } +} diff --git a/src/service/ecal_service/src/server_session_impl_base.h b/src/service/ecal_service/src/server_session_impl_base.h new file mode 100644 index 0000000..358e40a --- /dev/null +++ b/src/service/ecal_service/src/server_session_impl_base.h @@ -0,0 +1,107 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include + +#include +#include + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4834) +#endif + +#include +#include + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#include +#include + +namespace eCAL +{ + namespace service + { + class ServerSessionBase + { + ///////////////////////////////////// + // Custom types for API + ///////////////////////////////////// + public: + using ShutdownCallbackT = std::function&)>; + + ///////////////////////////////////// + // Constructor, Destructor, Create + ///////////////////////////////////// + public: + // Delete copy / move constructor and assignment operator + ServerSessionBase(const ServerSessionBase&) = delete; // Copy construct + ServerSessionBase(ServerSessionBase&&) = delete; // Move construct + + ServerSessionBase& operator=(const ServerSessionBase&) = delete; // Copy assign + ServerSessionBase& operator=(ServerSessionBase&&) = delete; // Move assign + + virtual ~ServerSessionBase() = default; + + protected: + ServerSessionBase(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback) + : io_context_ (io_context) + , socket_ (*io_context) + , service_callback_ (service_callback) + , service_callback_strand_(service_callback_strand) + , event_callback_ (event_callback) + , shutdown_callback_ (shutdown_callback) + {} + + ///////////////////////////////////// + // Public API + ///////////////////////////////////// + public: + asio::ip::tcp::socket& socket() { return socket_; } + virtual void start() = 0; + virtual void stop() = 0; + + virtual eCAL::service::State get_state() const = 0; + + ///////////////////////////////////// + // Member variables + ///////////////////////////////////// + protected: + const std::shared_ptr io_context_; + asio::ip::tcp::socket socket_; + mutable std::mutex socket_mutex_; + + const ServerServiceCallbackT service_callback_; + const std::shared_ptr service_callback_strand_; + const ServerEventCallbackT event_callback_; + const ShutdownCallbackT shutdown_callback_; + }; + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/server_session_impl_v0.cpp b/src/service/ecal_service/src/server_session_impl_v0.cpp new file mode 100644 index 0000000..1d971b4 --- /dev/null +++ b/src/service/ecal_service/src/server_session_impl_v0.cpp @@ -0,0 +1,239 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "server_session_impl_v0.h" + +#include "log_helpers.h" +#include "log_defs.h" + +#include "protocol_layout.h" + +/////////////////////////////////////////////// +// Create, Constructor, Destructor +/////////////////////////////////////////////// + +namespace eCAL +{ + namespace service + { + + std::shared_ptr ServerSessionV0::create(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger) + { + std::shared_ptr instance = std::shared_ptr(new ServerSessionV0(io_context, service_callback, service_callback_strand, event_callback, shutdown_callback, logger)); + return instance; + } + + ServerSessionV0::ServerSessionV0(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger) + : ServerSessionBase(io_context, service_callback, service_callback_strand, event_callback, shutdown_callback) + , logger_ (logger) + , state_ (State::NOT_CONNECTED) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Server Session Created"); + } + + // Destructor + ServerSessionV0::~ServerSessionV0() + { + ServerSessionV0::stop(); + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Server Session Deleted"); + } + + /////////////////////////////////////////////// + // Data receiving and sending + /////////////////////////////////////////////// + void ServerSessionV0::start() + { + // Go to handshake state + state_ = State::CONNECTED; + + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Starting..."); + + const std::string message = "Client has connected. Using protocol version 0."; + event_callback_(eCAL::service::ServerEventType::Connected, message); + logger_(LogLevel::Info, "[" + get_connection_info_string(socket_) + "] " + message); + + // Disable Nagle's algorithm. Nagles Algorithm will otherwise cause the + // Socket to wait for more data, if it encounters a frame that can still + // fit more data. Obviously, this is an awfull default behaviour, if we + // want to transmit our data in a timely fashion. + { + asio::error_code socket_option_ec; + { + const std::lock_guard socket_lock(socket_mutex_); + socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + } + if (socket_option_ec) + { + logger_(LogLevel::Warning, "[" + get_connection_info_string(socket_) + "] " + "Failed setting tcp::no_delay option: " + socket_option_ec.message()); + } + } + + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Waiting for service request..."); + const std::lock_guard socket_lock(socket_mutex_); + socket_.async_read_some(asio::buffer(data_, max_length) + , service_callback_strand_->wrap([me = shared_from_this()](asio::error_code ec, std::size_t bytes_read) + { + me->handle_read(ec, bytes_read, std::make_shared()); + })); + } + + void ServerSessionV0::stop() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Stopping..."); + + const std::lock_guard socket_lock(socket_mutex_); + if (socket_.is_open()) + { + { + // Shutdown the socket + asio::error_code ec; + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + } + + { + // Close the socket + asio::error_code ec; + socket_.close(ec); + } + } + } + + eCAL::service::State ServerSessionV0::get_state() const + { + return state_; + } + + void ServerSessionV0::handle_read(const asio::error_code& ec, size_t bytes_transferred, const std::shared_ptr& request) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "[" + get_connection_info_string(socket_) + "] " + "Received " + std::to_string(bytes_transferred) + " bytes."); + + if (!ec) + { + // collect request + *request += std::string(data_, bytes_transferred); + // are there some more data on the socket ? + + size_t bytes_available_on_socket(0); + { + asio::error_code socket_available_ec; + { + const std::lock_guard socket_lock(socket_mutex_); + bytes_available_on_socket = socket_.available(socket_available_ec); + } + + if (socket_available_ec) + { + // -- This code is a copy of the code in the else branch below. -- + state_ = State::FAILED; + const auto message = "Disconnected on read: " + socket_available_ec.message(); + logger_(eCAL::service::LogLevel::Info, "[" + get_connection_info_string(socket_) + "] " + message); + event_callback_(eCAL::service::ServerEventType::Disconnected, message); + shutdown_callback_(shared_from_this()); + return; + } + } + + if (bytes_available_on_socket != 0u) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "[" + get_connection_info_string(socket_) + "] " + "More data is available on socket! Reading more data..."); + + { + const std::lock_guard socket_lock(socket_mutex_); + socket_.async_read_some(asio::buffer(data_, max_length) + , service_callback_strand_->wrap([me = shared_from_this(), request](asio::error_code ec, std::size_t bytes_read) + { + me->handle_read(ec, bytes_read, request); + })); + } + } + // no more data + else + { + // execute service callback + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "[" + get_connection_info_string(socket_) + "] " + "Socket currently doesn't hold any more data."); + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "handle_read final request size: " + std::to_string(request->size()) + ". Executing callback..."); + + auto response = std::make_shared(); + service_callback_(request, response); + + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "[" + get_connection_info_string(socket_) + "] " + "Server callback executed. Reponse size: " + std::to_string(response->size()) + "."); + + const auto header = std::make_shared(); + header->package_size_n = htonl(static_cast(response->size())); + + const std::vector buffer_list { asio::buffer(reinterpret_cast(header.get()), sizeof(eCAL::service::TcpHeaderV0)) + , asio::buffer(*response)}; + + { + const std::lock_guard socket_lock(socket_mutex_); + asio::async_write(socket_ + , buffer_list + , [me = shared_from_this(), header, response](asio::error_code ec, std::size_t bytes_written) + { + me->handle_write(ec, bytes_written); + }); + } + } + } + else + { + state_ = State::FAILED; + const auto message = "Disconnected on read: " + ec.message(); + logger_(eCAL::service::LogLevel::Info, "[" + get_connection_info_string(socket_) + "] " + message); + event_callback_(eCAL::service::ServerEventType::Disconnected, message); + shutdown_callback_(shared_from_this()); + } + } + + void ServerSessionV0::handle_write(const asio::error_code& ec, std::size_t /*bytes_transferred*/) + { + if (!ec) + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Waiting for service request..."); + { + const std::lock_guard socket_lock(socket_mutex_); + socket_.async_read_some(asio::buffer(data_, max_length) + , service_callback_strand_->wrap([me = shared_from_this()](asio::error_code ec, std::size_t bytes_read) + { + me->handle_read(ec, bytes_read, std::make_shared()); + })); + } + } + else + { + state_ = State::FAILED; + const auto message = "Disconnected on write: " + ec.message(); + logger_(eCAL::service::LogLevel::Error, "[" + get_connection_info_string(socket_) + "] " + message); + event_callback_(eCAL::service::ServerEventType::Disconnected, message); + shutdown_callback_(shared_from_this()); + } + } + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/server_session_impl_v0.h b/src/service/ecal_service/src/server_session_impl_v0.h new file mode 100644 index 0000000..0c39e6b --- /dev/null +++ b/src/service/ecal_service/src/server_session_impl_v0.h @@ -0,0 +1,96 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include "server_session_impl_base.h" +#include +#include + +#include + +namespace eCAL +{ + namespace service + { + class ServerSessionV0 + : public ServerSessionBase + , public std::enable_shared_from_this + { + + /////////////////////////////////////////////// + // Create, Constructor, Destructor + /////////////////////////////////////////////// + + public: + static std::shared_ptr create(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger); + + protected: + ServerSessionV0(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger); + + public: + // Copy + ServerSessionV0(const ServerSessionV0&) = delete; + ServerSessionV0& operator=(const ServerSessionV0&) = delete; + + // Move + ServerSessionV0(ServerSessionV0&&) noexcept = delete; + ServerSessionV0& operator=(ServerSessionV0&&) noexcept = delete; + + // Destructor + ~ServerSessionV0() override; + + /////////////////////////////////////////////// + // Data receiving and sending + /////////////////////////////////////////////// + public: + void start() override; + void stop() override; + + eCAL::service::State get_state() const override; + + private: + void handle_read(const asio::error_code& ec, size_t bytes_transferred, const std::shared_ptr& request); + + void handle_write(const asio::error_code& ec, std::size_t /*bytes_transferred*/); + + ///////////////////////////////////// + // Member variables + ///////////////////////////////////// + private: + const LoggerT logger_; + + std::atomic state_; + + enum { max_length = 64 * 1024 }; + char data_[max_length]{}; + }; + + } +} diff --git a/src/service/ecal_service/src/server_session_impl_v1.cpp b/src/service/ecal_service/src/server_session_impl_v1.cpp new file mode 100644 index 0000000..981b7d0 --- /dev/null +++ b/src/service/ecal_service/src/server_session_impl_v1.cpp @@ -0,0 +1,324 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "server_session_impl_v1.h" + +#include "protocol_v1.h" + +#include "log_helpers.h" +#include "log_defs.h" + +/////////////////////////////////////////////// +// Create, Constructor, Destructor +/////////////////////////////////////////////// + +namespace eCAL +{ + namespace service + { + constexpr std::uint8_t ServerSessionV1::MIN_SUPPORTED_PROTOCOL_VERSION; + constexpr std::uint8_t ServerSessionV1::MAX_SUPPORTED_PROTOCOL_VERSION; + + std::shared_ptr ServerSessionV1::create(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger) + { + std::shared_ptr instance = std::shared_ptr(new ServerSessionV1(io_context, service_callback, service_callback_strand, event_callback, shutdown_callback, logger)); + return instance; + } + + ServerSessionV1::ServerSessionV1(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger) + : ServerSessionBase(io_context, service_callback, service_callback_strand, event_callback, shutdown_callback) + , state_ (State::NOT_CONNECTED) + , accepted_protocol_version_(0) + , logger_ (logger) + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Server Session Created"); + } + + // Destructor + ServerSessionV1::~ServerSessionV1() + { + ServerSessionV1::stop(); + ECAL_SERVICE_LOG_DEBUG_VERBOSE(logger_, "Server Session Deleted"); + } + + /////////////////////////////////////////////// + // Data receiving and sending + /////////////////////////////////////////////// + void ServerSessionV1::start() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Starting..."); + + // Disable Nagle's algorithm. Nagles Algorithm will otherwise cause the + // Socket to wait for more data, if it encounters a frame that can still + // fit more data. Obviously, this is an awfull default behaviour, if we + // want to transmit our data in a timely fashion. + { + asio::error_code socket_option_ec; + { + const std::lock_guard socket_lock(socket_mutex_); + socket_.set_option(asio::ip::tcp::no_delay(true), socket_option_ec); + } + if (socket_option_ec) + { + logger_(LogLevel::Warning, "[" + get_connection_info_string(socket_) + "] " + "Failed setting tcp::no_delay option: " + socket_option_ec.message()); + } + } + + receive_handshake_request(); + } + + void ServerSessionV1::stop() + { + const std::lock_guard socket_lock(socket_mutex_); + if (socket_.is_open()) + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Stopping..."); + { + // Shutdown the socket + asio::error_code ec; + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + } + { + // Close the socket + asio::error_code ec; + socket_.close(ec); + } + } + } + + eCAL::service::State ServerSessionV1::get_state() const + { + return state_; + } + + void ServerSessionV1::receive_handshake_request() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Waiting for protocol handshake request..."); + + // Go to handshake state + state_ = State::HANDSHAKE; + + eCAL::service::ProtocolV1::async_receive_payload(socket_, socket_mutex_ + , [me = shared_from_this()](asio::error_code ec) + { + me->state_ = State::FAILED; + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + "Failed receiving protocol handshake request: " + ec.message()); + me->shutdown_callback_(me); + } + , [me = shared_from_this()](const std::shared_ptr>& header_buffer, const std::shared_ptr& payload_buffer) + { + TcpHeaderV1* header = reinterpret_cast(header_buffer->data()); + if (header->message_type != eCAL::service::MessageType::ProtocolHandshakeRequest) + { + // The request is not a Handshake request. + const std::string message = "Received invalid handshake request from client. Expected message type " + + std::to_string(static_cast(eCAL::service::MessageType::ProtocolHandshakeRequest)) + + ", but received " + std::to_string(static_cast(header->message_type)); + me->logger_(LogLevel::Fatal, "[" + get_connection_info_string(me->socket_) + "] " + message); + + me->state_ = State::FAILED; + me->shutdown_callback_(me); + return; + } + else + { + // The request is a Handshake request + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Received a handshake request of " + std::to_string(payload_buffer->size()) + " bytes."); + + // Resize payload if necessary. Will probably never be necessary + if (payload_buffer->size() < sizeof(ProtocolHandshakeRequestMessage)) + { + payload_buffer->resize(sizeof(ProtocolHandshakeRequestMessage), '\0'); + } + const ProtocolHandshakeRequestMessage* handshake_request = reinterpret_cast(payload_buffer->data()); + + // Compute the maximum supported protocol version by this server and the remote client + const std::uint8_t both_supported_max_protocol_version = std::min(handshake_request->max_supported_protocol_version, MAX_SUPPORTED_PROTOCOL_VERSION); + const std::uint8_t both_supported_min_protocol_version = std::max(handshake_request->min_supported_protocol_version, MIN_SUPPORTED_PROTOCOL_VERSION); + + if (both_supported_max_protocol_version >= both_supported_min_protocol_version) + { + // We have a protocol version that is supported by both sides. We choose the maximum supported version + me->accepted_protocol_version_ = both_supported_max_protocol_version; + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Choosing protocol version " + std::to_string(me->accepted_protocol_version_)); + + // Send the handshake response to the client, telling him the protocol version we will use + me->send_handshake_response(); + } + else + { + const std::string message = std::string("Error while accepting connection from client. No common protocol version is found. ") + + "Client supports [min: " + std::to_string(handshake_request->min_supported_protocol_version) + ", max: " + std::to_string(handshake_request->max_supported_protocol_version) + "]. " + + "Server supports [min: " + std::to_string(MIN_SUPPORTED_PROTOCOL_VERSION) + ", max: " + std::to_string(MAX_SUPPORTED_PROTOCOL_VERSION) + "]."; + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + + //const auto message = get_log_string("ERROR", "Error connecting to server. Server reported an un-supported protocol version: " + std::to_string(handshake_response->accepted_protocol_version)); + //std::cerr << message << std::endl; + me->state_ = State::FAILED; + me->shutdown_callback_(me); + return; + } + + } + }); + + } + + void ServerSessionV1::send_handshake_response() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Sending protocol handshake response..."); + + // Go to handshake state + state_ = State::HANDSHAKE; + + // Create buffers + const std::shared_ptr header_buffer = std::make_shared(); + const std::shared_ptr payload_buffer = std::make_shared(); + + // Fill Handshake Response Message + payload_buffer->resize(sizeof(ProtocolHandshakeResponseMessage), '\0'); + ProtocolHandshakeResponseMessage* handshake_response_message = reinterpret_cast(const_cast(payload_buffer->data())); + handshake_response_message->accepted_protocol_version = accepted_protocol_version_; + + // Fill TCP Header + header_buffer->package_size_n = htonl(static_cast(payload_buffer->size())); + header_buffer->version = accepted_protocol_version_; + header_buffer->message_type = MessageType::ProtocolHandshakeResponse; + header_buffer->header_size_n = htons(sizeof(TcpHeaderV1)); + + eCAL::service::ProtocolV1::async_send_payload(socket_, socket_mutex_,header_buffer, payload_buffer + , [me = shared_from_this()](asio::error_code ec) + { + me->state_ = State::FAILED; + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + "Failed sending protocol handshake response: " + ec.message()); + me->shutdown_callback_(me); + } + , [me = shared_from_this()]() + { + me->state_ = State::CONNECTED; + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Successfully sent protocol handshake response."); + + const std::string message = "Client has connected. Using protocol version " + std::to_string(me->accepted_protocol_version_) + "."; + me->logger_(LogLevel::Info, "[" + get_connection_info_string(me->socket_) + "] " + message); + + me->state_ = State::CONNECTED; + + // call event callback + me->event_callback_(eCAL::service::ServerEventType::Connected, message); + + me->receive_service_request(); + }); + } + + void ServerSessionV1::receive_service_request() + { + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Waiting for service request..."); + + eCAL::service::ProtocolV1::async_receive_payload(socket_, socket_mutex_ + , [me = shared_from_this()](asio::error_code ec) + { + const std::string message = "Server session disconnected while waiting for request: " + ec.message(); + me->logger_(LogLevel::Info, "[" + get_connection_info_string(me->socket_) + "] " + message); + + me->state_ = State::FAILED; + + // call event callback + me->event_callback_(eCAL::service::ServerEventType::Disconnected, message); + me->shutdown_callback_(me); + } + , service_callback_strand_->wrap([me = shared_from_this()](const std::shared_ptr>& header_buffer, const std::shared_ptr& payload_buffer) + { + TcpHeaderV1* header = reinterpret_cast(header_buffer->data()); + if (header->message_type != eCAL::service::MessageType::ServiceRequest) + { + const std::string message = "Received invalid service request from client. Expected message type " + + std::to_string(static_cast(eCAL::service::MessageType::ServiceRequest)) + + ", but received " + std::to_string(static_cast(header->message_type)); + me->logger_(LogLevel::Fatal, "[" + get_connection_info_string(me->socket_) + "] " + message); + + // The request is not a Service request. + me->state_ = State::FAILED; + + // call event callback + me->event_callback_(eCAL::service::ServerEventType::Disconnected, message); + + me->shutdown_callback_(me); + return; + } + else + { + // The request is a Service request + + ECAL_SERVICE_LOG_DEBUG(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Received service request of " + std::to_string(payload_buffer->size()) + " bytes"); + + // Call the service callback + const std::shared_ptr response_buffer = std::make_shared(); + me->service_callback_(payload_buffer, response_buffer); + + // Send the response to the client + me->send_service_response(response_buffer); + } + })); + + } + + void ServerSessionV1::send_service_response(const std::shared_ptr& response_buffer) + { + // Create header_buffer + const std::shared_ptr header_buffer = std::make_shared(); + header_buffer->package_size_n = htonl(static_cast(response_buffer->size())); + header_buffer->version = accepted_protocol_version_; + header_buffer->message_type = MessageType::ServiceResponse; + header_buffer->header_size_n = htons(sizeof(TcpHeaderV1)); + + ECAL_SERVICE_LOG_DEBUG(logger_, "[" + get_connection_info_string(socket_) + "] " + "Sending service response..."); + + eCAL::service::ProtocolV1::async_send_payload(socket_, socket_mutex_, header_buffer, response_buffer + , [me = shared_from_this()](asio::error_code ec) + { + const std::string message = "Failed sending service response: " + ec.message(); + me->logger_(LogLevel::Error, "[" + get_connection_info_string(me->socket_) + "] " + message); + + me->state_ = State::FAILED; + + // call event callback + me->event_callback_(eCAL::service::ServerEventType::Disconnected, message); + me->shutdown_callback_(me); + } + , [me = shared_from_this()]() + { + ECAL_SERVICE_LOG_DEBUG_VERBOSE(me->logger_, "[" + get_connection_info_string(me->socket_) + "] " + "Successfully sent service response."); + + // Wait for next request + me->receive_service_request(); + }); + } + + } // namespace service +} // namespace eCAL diff --git a/src/service/ecal_service/src/server_session_impl_v1.h b/src/service/ecal_service/src/server_session_impl_v1.h new file mode 100644 index 0000000..87164ba --- /dev/null +++ b/src/service/ecal_service/src/server_session_impl_v1.h @@ -0,0 +1,98 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include "server_session_impl_base.h" +#include +#include + +#include + +namespace eCAL +{ + namespace service + { + class ServerSessionV1 + : public ServerSessionBase + , public std::enable_shared_from_this + { + + /////////////////////////////////////////////// + // Create, Constructor, Destructor + /////////////////////////////////////////////// + + public: + static std::shared_ptr create(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger); + + protected: + ServerSessionV1(const std::shared_ptr& io_context + , const ServerServiceCallbackT& service_callback + , const std::shared_ptr& service_callback_strand + , const ServerEventCallbackT& event_callback + , const ShutdownCallbackT& shutdown_callback + , const LoggerT& logger); + + public: + // Copy + ServerSessionV1(const ServerSessionV1&) = delete; + ServerSessionV1& operator=(const ServerSessionV1&) = delete; + + // Move + ServerSessionV1(ServerSessionV1&&) noexcept = delete; + ServerSessionV1& operator=(ServerSessionV1&&) noexcept = delete; + + // Destructor + ~ServerSessionV1() override; + + /////////////////////////////////////////////// + // Data receiving and sending + /////////////////////////////////////////////// + public: + void start() override; + void stop() override; + + eCAL::service::State get_state() const override; + + private: + void receive_handshake_request(); + void send_handshake_response(); + + void receive_service_request(); + void send_service_response(const std::shared_ptr& response_buffer); + + ///////////////////////////////////// + // Member variables + ///////////////////////////////////// + private: + static constexpr std::uint8_t MIN_SUPPORTED_PROTOCOL_VERSION = 1; + static constexpr std::uint8_t MAX_SUPPORTED_PROTOCOL_VERSION = 1; + + std::atomic state_; + std::uint8_t accepted_protocol_version_; + + const LoggerT logger_; + }; + } +} diff --git a/src/service/sample/CMakeLists.txt b/src/service/sample/CMakeLists.txt new file mode 100644 index 0000000..0296bbc --- /dev/null +++ b/src/service/sample/CMakeLists.txt @@ -0,0 +1,39 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2023 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(service_sample) + +find_package(Threads REQUIRED) + +set(sources + src/main.cpp +) + +add_executable(${PROJECT_NAME} ${sources}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ecal_service) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core/service) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${sources} +) diff --git a/src/service/sample/src/main.cpp b/src/service/sample/src/main.cpp new file mode 100644 index 0000000..be38fbc --- /dev/null +++ b/src/service/sample/src/main.cpp @@ -0,0 +1,92 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2023 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 + +int main(int /*argc*/, char** /*argv*/) +{ + // Create an io_context + auto io_context = std::make_shared(); + + // Create a server and client manager + auto server_manager = eCAL::service::ServerManager::create(io_context); + auto client_manager = eCAL::service::ClientManager::create(io_context); + + // Create and start an io_context thread. + // The io_context will be stopped, when the server_manager and client_manager are stopped. + std::thread io_context_thread([&io_context]() { io_context->run(); }); + + // Server Service callback + // + // This callback will be called, when a client calls the service. + // It is responsible for filling the response object. + auto server_service_callback + = [](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + *response = "Response on \"" + *request + "\""; + }; + + // Client Callback + // + // This callback will be called, when the service call is finished. + auto client_response_callback + = [](const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + if (error) + std::cerr << "Error calling service: " << error.ToString() << std::endl; + else + std::cout << "Received response: " << *response << std::endl; + }; + + // Event callbacks (empty) + auto server_event_callback = [](eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) {}; + auto client_event_callback = [](eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) {}; + + // Create server + // The server will choose a free port automatically. + auto server = server_manager->create_server(1, 0, server_service_callback, true, server_event_callback); + + // Create client + // The client will connect to the server on the given port. + auto client = client_manager->create_client(1, "127.0.0.1", server->get_port(), client_event_callback); + + // Call the service non-blocking. The response will be passed to the callback. + for (int i = 1; i <= 10; i++) + { + const auto request = std::make_shared("Hello World " + std::to_string(i)); + client->async_call_service(request, client_response_callback); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + std::cout << "Shutting down :)" << std::endl; + + // Use managers to stop servers and clients + server_manager->stop(); + client_manager->stop(); + + // Join the io_context thread + io_context_thread.join(); + +} diff --git a/src/service/test/CMakeLists.txt b/src/service/test/CMakeLists.txt new file mode 100644 index 0000000..8e6e652 --- /dev/null +++ b/src/service/test/CMakeLists.txt @@ -0,0 +1,43 @@ +# ========================= 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 ================================= + +project(test_service) + +find_package(Threads REQUIRED) +find_package(GTest REQUIRED) + +set(sources + src/ecal_tcp_service_test.cpp + src/atomic_signalable.h +) + +ecal_add_gtest(${PROJECT_NAME} ${sources}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ecal_service) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_gtest(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/service) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${sources} +) diff --git a/src/service/test/src/atomic_signalable.h b/src/service/test/src/atomic_signalable.h new file mode 100644 index 0000000..4ce17e6 --- /dev/null +++ b/src/service/test/src/atomic_signalable.h @@ -0,0 +1,208 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2023 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 + +template +class atomic_signalable +{ +public: + atomic_signalable(T initial_value) : value(initial_value) {} + + atomic_signalable& operator=(const T new_value) + { + std::lock_guard lock(mutex); + value = new_value; + cv.notify_all(); + return *this; + } + + T operator++() + { + std::lock_guard lock(mutex); + T newValue = ++value; + cv.notify_all(); + return newValue; + } + + T operator++(T) + { + std::lock_guard lock(mutex); + T oldValue = value++; + cv.notify_all(); + return oldValue; + } + + T operator--() + { + std::lock_guard lock(mutex); + T newValue = --value; + cv.notify_all(); + return newValue; + } + + T operator--(T) + { + std::lock_guard lock(mutex); + T oldValue = value--; + cv.notify_all(); + return oldValue; + } + + T operator+=(const T& other) + { + std::lock_guard lock(mutex); + value += other; + cv.notify_all(); + return value; + } + + T operator-=(const T& other) + { + std::lock_guard lock(mutex); + value -= other; + cv.notify_all(); + return value; + } + + T operator*=(const T& other) + { + std::lock_guard lock(mutex); + value *= other; + cv.notify_all(); + return value; + } + + T operator/=(const T& other) + { + std::lock_guard lock(mutex); + value /= other; + cv.notify_all(); + return value; + } + + T operator%=(const T& other) + { + std::lock_guard lock(mutex); + value %= other; + cv.notify_all(); + return value; + } + + template + bool wait_for(Predicate predicate, std::chrono::milliseconds timeout) + { + std::unique_lock lock(mutex); + return cv.wait_for(lock, timeout, [&]() { return predicate(value); }); + } + + T get() const + { + std::lock_guard lock(mutex); + return value; + } + + bool operator==(T other) const + { + std::lock_guard lock(mutex); + return value == other; + } + + bool operator==(const atomic_signalable& other) const + { + std::lock_guard lock_this(mutex); + std::lock_guard lock_other(other.mutex); + return value == other.value; + } + + bool operator!=(T other) const + { + std::lock_guard lock(mutex); + return value != other; + } + + bool operator<(T other) const + { + std::lock_guard lock(mutex); + return value < other; + } + + bool operator<=(T other) const + { + std::lock_guard lock(mutex); + return value <= other; + } + + bool operator>(T other) const + { + std::lock_guard lock(mutex); + return value > other; + } + + bool operator>=(T other) const + { + std::lock_guard lock(mutex); + return value >= other; + } + +private: + T value; + std::condition_variable cv; + mutable std::mutex mutex; +}; + + +template +bool operator==(const T& other, const atomic_signalable& atomic) +{ + return atomic == other; +} + +template +bool operator!=(const T& other, const atomic_signalable& atomic) +{ + return atomic != other; +} + +template +bool operator<(const T& other, const atomic_signalable& atomic) +{ + return atomic > other; +} + +template +bool operator<=(const T& other, const atomic_signalable& atomic) +{ + return atomic >= other; +} + +template +bool operator>(const T& other, const atomic_signalable& atomic) +{ + return atomic < other; +} + +template +bool operator>=(const T& other, const atomic_signalable& atomic) +{ + return atomic <= other; +} diff --git a/src/service/test/src/ecal_tcp_service_test.cpp b/src/service/test/src/ecal_tcp_service_test.cpp new file mode 100644 index 0000000..6b44026 --- /dev/null +++ b/src/service/test/src/ecal_tcp_service_test.cpp @@ -0,0 +1,2596 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include + +#include +#include +#include +#include + +#include // Should not be needed, when I use the server manager / client manager +#include // Should not be needed, when I use the server manager / client manager + +#include +#include + +#include "atomic_signalable.h" + +eCAL::service::LoggerT critical_logger(const std::string& node_name) +{ + return [node_name](const eCAL::service::LogLevel log_level, const std::string& message) + { + switch (log_level) + { + case eCAL::service::LogLevel::Warning: + std::cerr << "[" + node_name + "] [Warning] " + message + "\n"; + break; + case eCAL::service::LogLevel::Error: + std::cerr << "[" + node_name + "] [Error] " + message + "\n"; + break; + case eCAL::service::LogLevel::Fatal: + std::cerr << "[" + node_name + "] [Fatal] " + message + "\n"; + break; + default: + break; + } + }; +} + +constexpr std::uint8_t min_protocol_version = 0; +constexpr std::uint8_t max_protocol_version = 1; + + + +#if 1 +TEST(RAII, TcpServiceServer) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + const eCAL::service::Server::ServiceCallbackT service_callback + = [](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::cout << "Server got request: " << *request << std::endl; + *response = "Response on \"" + *request + "\""; + }; + + + const eCAL::service::Server::EventCallbackT event_callback + = [](eCAL::service::ServerEventType event, const std::string& message) -> void + { + std::cout << "Event " << static_cast(event) << ": " << message << std::endl; + }; + + std::unique_ptr io_thread; + + // Test auto-destruction when the shared_ptr goes out of scope + + { + std::weak_ptr tcp_server_weak; + + { + const std::shared_ptr tcp_server = eCAL::service::Server::create(io_context, protocol_version, 0, service_callback, true, event_callback); + tcp_server_weak = tcp_server; + + EXPECT_NE(nullptr, tcp_server); + EXPECT_NE(nullptr, tcp_server_weak.lock()); + + io_thread = std::make_unique([&io_context]() + { + io_context->run(); + }); + } + + EXPECT_EQ(nullptr, tcp_server_weak.lock()); + } + + io_context->stop(); + io_thread->join(); + } +} +#endif + +#if 1 +TEST(RAII, TcpServiceClient) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + + std::unique_ptr io_thread; + + // Test auto-destruction when the shared_ptr goes out of scope + + io_thread = std::make_unique([&io_context]() + { + io_context->run(); + }); + + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, "127.0.0.1", 12345, client_event_callback); + + io_context->stop(); + io_thread->join(); + } +} +#endif + +#if 1 +TEST(RAII, TcpServiceServerAndClient) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic response_callback_called(false); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::cout << "Server got request: " << *request << std::endl; + *response = "Response on \"" + *request + "\""; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [](eCAL::service::ServerEventType event, const std::string& message) -> void + { + std::cout << "Event " << static_cast(event) << ": " << message << std::endl; + }; + + const eCAL::service::ClientSession::ResponseCallbackT client_slow_response_callback + = [&response_callback_called](const eCAL::service::Error& /*error*/, const std::shared_ptr& /*response*/) -> void + { + // This callback just wastes some time + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + response_callback_called = true; + }; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + + std::unique_ptr io_thread; + + // Test auto-destruction when the shared_ptr goes out of scope + + { + std::weak_ptr tcp_server_weak; + std::weak_ptr tcp_client_weak; + + std::chrono::steady_clock::time_point start_time; + + { + const std::shared_ptr tcp_server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + tcp_server_weak = tcp_server; + + io_thread = std::make_unique([&io_context]() + { + io_context->run(); + }); + + EXPECT_EQ(tcp_server->get_connection_count(), 0); + + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", tcp_server->get_port(), client_event_callback); + tcp_client_weak = client_v1; + + client_v1->async_call_service(std::make_shared("Hello World"), client_slow_response_callback); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + EXPECT_EQ(tcp_server->get_connection_count(), 1); + + // Start the "timer", when we are starting to delete the server and client + start_time = std::chrono::steady_clock::now(); + } + + // The Client and server should both be deleted now. + EXPECT_EQ(nullptr, tcp_server_weak.lock()); + EXPECT_EQ(nullptr, tcp_client_weak.lock()); + + // The response callback should be finished, yet + EXPECT_FALSE(response_callback_called); + } + + io_context->stop(); + io_thread->join(); + + // Now the response callback should be finished + EXPECT_TRUE(response_callback_called); + } +} +#endif + +#if 1 +TEST(RAII, StopDuringServiceCall) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic response_callback_called(false); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::cout << "Server got request: " << *request << std::endl; + *response = "Response on \"" + *request + "\""; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [](eCAL::service::ServerEventType event, const std::string& message) -> void + { + std::cout << "Event " << static_cast(event) << ": " << message << std::endl; + }; + + const eCAL::service::ClientSession::ResponseCallbackT client_slow_response_callback + = [&response_callback_called](const eCAL::service::Error& /*error*/, const std::shared_ptr& /*response*/) -> void + { + // This callback just wastes some time + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + response_callback_called = true; + }; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + std::unique_ptr io_thread; + + // Test auto-destruction when the shared_ptr goes out of scope + + { + std::weak_ptr tcp_server_weak; + std::weak_ptr tcp_client_weak; + + { + const std::shared_ptr tcp_server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + tcp_server_weak = tcp_server; + + io_thread = std::make_unique([&io_context]() + { + io_context->run(); + }); + + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", tcp_server->get_port(), client_event_callback); + tcp_client_weak = client_v1; + + client_v1->async_call_service(std::make_shared("Hello World"), client_slow_response_callback); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + io_context->stop(); + + // The client and server should be deleted already + EXPECT_EQ(nullptr, tcp_server_weak.lock()); + EXPECT_EQ(nullptr, tcp_client_weak.lock()); + + // The callback is probably still running + EXPECT_FALSE(response_callback_called); + + // The callback should be finished by now and the session should be deleted + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_TRUE(response_callback_called); + } + + io_thread->join(); + } +} +#endif + +#if 1 +TEST(Communication, SlowCommunication) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic num_server_service_callback_called (0); + std::atomic num_client_response_callback_called (0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called] + (const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + num_server_service_callback_called++; + std::cout << "Server got request: " << *request << std::endl; + *response = "Response on \"" + *request + "\""; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [] + (eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [&num_client_response_callback_called] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + EXPECT_FALSE(bool(error)); + num_client_response_callback_called++; + std::cout << "Client got Response: " << *response << std::endl; + }; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + + EXPECT_NE(server->get_port(), 0); + + { + EXPECT_EQ(num_server_service_callback_called , 0); + EXPECT_EQ(num_client_response_callback_called , 0); + + EXPECT_EQ(server->get_connection_count(), 0); + } + + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait a short time for the client to connect + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called , 0); + EXPECT_EQ(num_client_response_callback_called , 0); + + EXPECT_EQ(server->get_connection_count(), 1); + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + + // Call service and wait a short time + client_v1->async_call_service(std::make_shared("Hello World"), client_response_callback); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_client_response_callback_called , 1); + + EXPECT_EQ(server->get_connection_count(), 1); + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + // Call service again and wait a short time + client_v1->async_call_service(std::make_shared("Called again"), client_response_callback); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called , 2); + EXPECT_EQ(num_client_response_callback_called , 2); + + EXPECT_EQ(server->get_connection_count(), 1); + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + // delete all objects + client_v1 = nullptr; + server = nullptr; + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(CallbacksConnectDisconnect, ClientDisconnectsFirst) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + std::atomic num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected(0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [] + (const std::shared_ptr& /*request*/, const std::shared_ptr& /*response*/) -> void + {}; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + + num_server_event_callback_called++; + }; + + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [] + (const eCAL::service::Error& /*error*/, const std::shared_ptr& /*response*/) -> void + {}; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait a short time for the client to connect + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + { + EXPECT_EQ(num_server_event_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_event_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + } + + // Client goes away + client = nullptr; + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + { + EXPECT_EQ(num_server_event_callback_called , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_event_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + } + + server = nullptr; + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + { + EXPECT_EQ(num_server_event_callback_called , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_event_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + } + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(CommunicationAndCallbacks, ClientsDisconnectFirst) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + atomic_signalable num_server_service_callback_called (0); + atomic_signalable num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + atomic_signalable num_client_response_callback_called (0); + atomic_signalable num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected(0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called] + (const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::cout << "Server got request: " << *request << std::endl; + *response = "Response on \"" + *request + "\""; + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + + num_server_event_callback_called++; + }; + + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [&num_client_response_callback_called] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + EXPECT_FALSE(bool(error)); + std::cout << "Client got Response: " << *response << std::endl; + num_client_response_callback_called++; + }; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + + EXPECT_NE(server->get_port(), 0); + + { + EXPECT_EQ(num_server_service_callback_called.get() , 0); + EXPECT_EQ(num_server_event_callback_called.get() , 0); + EXPECT_EQ(num_server_event_callback_called_connected , 0); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called_connected , 0); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), 0); + } + + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait a short time for the client to connect + num_server_event_callback_called.wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(100)); + num_client_event_callback_called.wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , 0); + EXPECT_EQ(num_server_event_callback_called.get() , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called.get() , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), 1); + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + + // Call service and wait a short time + client_v1->async_call_service(std::make_shared("Hello World"), client_response_callback); + num_server_service_callback_called .wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(100)); + num_client_response_callback_called.wait_for([](int v) { return v >= 1; }, std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , 1); + EXPECT_EQ(num_server_event_callback_called.get() , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , 1); + EXPECT_EQ(num_client_event_callback_called.get() , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), 1); + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + // Call service again and wait a short time + client_v1->async_call_service(std::make_shared("Called again"), client_response_callback); + num_server_service_callback_called .wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(100)); + num_client_response_callback_called.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , 2); + EXPECT_EQ(num_server_event_callback_called.get() , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , 2); + EXPECT_EQ(num_client_event_callback_called.get() , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), 1); + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + // delete all objects + client_v1 = nullptr; + + num_server_event_callback_called.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(100)); + num_client_event_callback_called.wait_for([](int v) { return v >= 2; }, std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , 2); + EXPECT_EQ(num_server_event_callback_called.get() , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_response_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + + EXPECT_EQ(server->get_connection_count(), 0); + EXPECT_EQ(server->is_connected() , false); + } + + server = nullptr; + + { + EXPECT_EQ(num_server_service_callback_called.get() , 2); + EXPECT_EQ(num_server_event_callback_called.get() , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_response_callback_called.get() , 2); + EXPECT_EQ(num_client_event_callback_called.get() , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + } + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(CommunicationAndCallbacks, ServerDisconnectsFirst) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic num_server_service_callback_called (0); + std::atomic num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + std::atomic num_client_response_callback_called (0); + std::atomic num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected(0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called] + (const std::shared_ptr& /*request*/, const std::shared_ptr& /*response*/) -> void + { + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + + num_server_event_callback_called++; + }; + + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [&num_client_response_callback_called] + (const eCAL::service::Error& error, const std::shared_ptr& /*response*/) -> void + { + EXPECT_FALSE(bool(error)); + num_client_response_callback_called++; + }; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait a short time for the client to connect + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called , 0); + EXPECT_EQ(num_server_event_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called , 0); + EXPECT_EQ(num_client_event_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), 1); + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + + // Call service and wait a short time + client_v1->async_call_service(std::make_shared("Hello World"), client_response_callback); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + // Server goes away + server = nullptr; + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_response_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + + EXPECT_EQ(client_v1->get_state(), eCAL::service::State::FAILED); + EXPECT_EQ(client_v1->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client_v1->get_queue_size(), 0); + } + + client_v1 = nullptr; + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_response_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + } + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(CommunicationAndCallbacks, StressfulCommunication) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr int num_io_threads = 10; + constexpr int num_clients = 10; + constexpr int num_calls_per_client = 15; + + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + atomic_signalable num_server_service_callback_called (0); + atomic_signalable num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + atomic_signalable num_client_response_callback_called (0); + atomic_signalable num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected(0); + + const eCAL::service::Server::ServiceCallbackT service_callback + = [&num_server_service_callback_called](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + num_server_service_callback_called++; + *response = "Response on \"" + *request + "\""; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + + num_server_event_callback_called++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, service_callback, true, server_event_callback, critical_logger("Server")); + + { + EXPECT_EQ(num_server_service_callback_called.get() , 0); + EXPECT_EQ(num_server_event_callback_called.get() , 0); + EXPECT_EQ(num_server_event_callback_called_connected , 0); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called_connected , 0); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + } + + // Run the io service a bunch of times, so we hopefully trigger any race condition that may exist + std::vector> io_threads; + io_threads.reserve(num_io_threads); + for (int i = 0; i < num_io_threads; i++) + { + io_threads.emplace_back(std::make_unique([&io_context]() { io_context->run(); })); + } + + // Create all the clients + std::vector> client_list; + client_list.reserve(num_clients); + for (int c = 0; c < num_clients; c++) + { + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + } + + // Directly run a bunch of clients and call each client a bunch of times + for (size_t c = 0; c < client_list.size(); c++) + { + for (int i = 0; i < num_calls_per_client; i++) + { + const std::shared_ptr request_string = std::make_shared("Client " + std::to_string(c) + ", Call " + std::to_string(i)); + + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback_called, request_string] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + ASSERT_FALSE(error); + num_client_response_callback_called++; + const std::string expected_response = "Response on \"" + *request_string + "\""; + ASSERT_EQ(*response, expected_response); + }; + + client_list[c]->async_call_service(request_string, response_callback); + } + } + + // Now wait a short time for the clients to connect to the server and all requests getting executed + num_client_response_callback_called.wait_for([num_clients, num_calls_per_client](int v) { return v == num_clients * num_calls_per_client; }, std::chrono::milliseconds(5000)); + num_server_service_callback_called .wait_for([num_clients, num_calls_per_client](int v) { return v == num_clients * num_calls_per_client; }, std::chrono::milliseconds(500)); + num_server_event_callback_called .wait_for([num_clients](int v) { return v == num_clients; }, std::chrono::milliseconds(500)); + num_client_event_callback_called .wait_for([num_clients](int v) { return v == num_clients; }, std::chrono::milliseconds(500)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , num_clients * num_calls_per_client); + EXPECT_EQ(num_server_event_callback_called.get() , num_clients); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , num_clients * num_calls_per_client); + EXPECT_EQ(num_client_event_callback_called.get() , num_clients); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), num_clients); + } + + // delete all objects + server = nullptr; + + // Wait for disconnection callbacks + num_server_event_callback_called.wait_for([num_clients](int v) { return v == (2 * num_clients); }, std::chrono::milliseconds(5000)); + num_client_event_callback_called.wait_for([num_clients](int v) { return v == (2 * num_clients); }, std::chrono::milliseconds(5000)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , num_clients* num_calls_per_client); + EXPECT_EQ(num_server_event_callback_called.get() , num_clients * 2); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, num_clients); + + EXPECT_EQ(num_client_response_callback_called.get() , num_clients * num_calls_per_client); + EXPECT_EQ(num_client_event_callback_called.get() , num_clients * 2); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, num_clients); + } + + client_list.clear(); + + // join all io_threads + io_context->stop(); + for (const auto& io_thread : io_threads) + { + io_thread->join(); + } + io_threads.clear(); + } +} +#endif + +#if 1 +TEST(CommunicationAndCallbacks, StressfulCommunicationNoParallelCalls) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr int num_io_threads = 50; + constexpr int num_clients = 10; + constexpr int num_calls_per_client = 2; + constexpr std::chrono::milliseconds server_time_to_waste(50); + + const auto io_context = std::make_shared(); + auto dummy_work = std::make_unique(*io_context); + + std::atomic num_server_service_callback_started (0); + std::atomic num_server_service_callback_finished (0); + + atomic_signalable num_client_response_callback_called (0); + atomic_signalable num_clients_connected (0); + + const eCAL::service::Server::ServiceCallbackT service_callback + = [&num_server_service_callback_started, &num_server_service_callback_finished, server_time_to_waste] + (const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + EXPECT_EQ(num_server_service_callback_started, num_server_service_callback_finished); + num_server_service_callback_started++; + std::this_thread::sleep_for(server_time_to_waste); + *response = "Response on \"" + *request + "\""; + num_server_service_callback_finished++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [] + (eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, service_callback, false, server_event_callback, critical_logger("Server")); + + { + EXPECT_EQ(num_server_service_callback_started , 0); + EXPECT_EQ(num_server_service_callback_finished , 0); + EXPECT_EQ(num_client_response_callback_called , 0); + } + + // Run the io service a bunch of times, so we hopefully trigger any race condition that may exist + std::vector> io_threads; + io_threads.reserve(num_io_threads); + for (int i = 0; i < num_io_threads; i++) + { + io_threads.emplace_back(std::make_unique([&io_context]() { io_context->run(); })); + } + + // Create all the clients + std::vector> client_list; + client_list.reserve(num_clients); + for (int c = 0; c < num_clients; c++) + { + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_clients_connected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + { + num_clients_connected++; + } + }; + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + } + + // wait for the clients to connect + num_clients_connected.wait_for([num_clients](int v) { return v >= num_clients; }, std::chrono::seconds(5)); + { + EXPECT_EQ (num_clients_connected, num_clients); + } + + auto start = std::chrono::steady_clock::now(); + + // Directly run a bunch of clients and call each client a bunch of times + for (size_t c = 0; c < client_list.size(); c++) + { + for (int i = 0; i < num_calls_per_client; i++) + { + const std::shared_ptr request_string = std::make_shared("Client " + std::to_string(c) + ", Call " + std::to_string(i)); + + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback_called, request_string] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + ASSERT_FALSE(error); + num_client_response_callback_called++; + const std::string expected_response = "Response on \"" + *request_string + "\""; + ASSERT_EQ(*response, expected_response); + }; + + client_list[c]->async_call_service(request_string, response_callback); + } + } + + // Now wait until all calls are finished + const int num_total_calls = num_clients * num_calls_per_client; + const std::chrono::milliseconds expected_time = num_total_calls * server_time_to_waste; + num_client_response_callback_called.wait_for([num_total_calls](int v) { return v >= num_total_calls; }, expected_time * 2); + + auto end = std::chrono::steady_clock::now(); + + auto total_time = end - start; + + { + EXPECT_EQ(num_server_service_callback_started , num_total_calls); + EXPECT_EQ(num_server_service_callback_finished , num_total_calls); + EXPECT_EQ(num_client_response_callback_called , num_total_calls); + EXPECT_TRUE(total_time >= expected_time); + } + + // delete all objects + server = nullptr; + client_list.clear(); + + dummy_work.reset(); + // join all io_threads + for (const auto& io_thread : io_threads) + { + io_thread->join(); + } + } +} +#endif + +#if 1 +TEST(CommunicationAndCallbacks, StressfulCommunicationMassivePayload) // NOLINT +{ + // This test does not work for Protocol version 0 and there is no way to fix that (which is the reason why we invented protocol version 1) + + constexpr std::uint8_t protocol_version = 1; + + constexpr int num_io_threads = 5; + constexpr int num_clients = 3; + constexpr int num_calls_per_client = 5; + constexpr int payload_size_bytes = 32 * 1024 * 1024; + + const auto payload = std::make_shared(payload_size_bytes, 'e'); + + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + atomic_signalable num_server_service_callback_called (0); + atomic_signalable num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + atomic_signalable num_client_response_callback_called (0); + atomic_signalable num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected(0); + + const eCAL::service::Server::ServiceCallbackT service_callback + = [&num_server_service_callback_called, payload_size_bytes](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + EXPECT_EQ(request->size(), payload_size_bytes); + num_server_service_callback_called++; + *response = *request; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + num_server_event_callback_called++; + + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, service_callback, true, server_event_callback, critical_logger("Server")); + + { + EXPECT_EQ(num_server_service_callback_called.get() , 0); + EXPECT_EQ(num_server_event_callback_called.get() , 0); + EXPECT_EQ(num_server_event_callback_called_connected , 0); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called_connected , 0); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + } + + // Run the io service a bunch of times, so we hopefully trigger any race condition that may exist + std::vector> io_threads; + io_threads.reserve(num_io_threads); + for (int i = 0; i < num_io_threads; i++) + { + io_threads.emplace_back(std::make_unique([&io_context]() { io_context->run(); })); + } + + // Create all the clients + std::vector> client_list; + client_list.reserve(num_clients); + for (int c = 0; c < num_clients; c++) + { + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + } + + // Directly run a bunch of clients and call each client a bunch of times + for (auto & client : client_list) + { + for (int i = 0; i < num_calls_per_client; i++) + { + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback_called, payload_size_bytes] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + ASSERT_FALSE(error); + EXPECT_EQ(response->size(), payload_size_bytes); + num_client_response_callback_called++; + }; + + client->async_call_service(payload, response_callback); + } + } + + // Now wait a short time for the clients to connect to the server and all requests getting executed + auto num_total_calls = num_clients * num_calls_per_client; + num_client_response_callback_called.wait_for([num_total_calls](int v) { return v >= num_total_calls; }, std::chrono::milliseconds(5000)); + num_server_service_callback_called .wait_for([num_total_calls](int v) { return v >= num_total_calls; }, std::chrono::milliseconds(500)); + num_client_event_callback_called .wait_for([num_clients](int v) { return v >= num_clients; }, std::chrono::milliseconds(500)); + num_server_event_callback_called .wait_for([num_clients](int v) { return v >= num_clients; }, std::chrono::milliseconds(500)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , num_clients * num_calls_per_client); + EXPECT_EQ(num_server_event_callback_called.get() , num_clients); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called.get() , num_clients * num_calls_per_client); + EXPECT_EQ(num_client_event_callback_called.get() , num_clients); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), num_clients); + } + + // delete all objects + server = nullptr; + + num_client_event_callback_called .wait_for([num_clients](int v) { return v >= (2 * num_clients); }, std::chrono::milliseconds(5000)); + num_server_event_callback_called .wait_for([num_clients](int v) { return v >= (2 * num_clients); }, std::chrono::milliseconds(500)); + + { + EXPECT_EQ(num_server_service_callback_called.get() , num_clients * num_calls_per_client); + EXPECT_EQ(num_server_event_callback_called.get() , num_clients * 2); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, num_clients); + + EXPECT_EQ(num_client_response_callback_called.get() , num_clients * num_calls_per_client); + EXPECT_EQ(num_client_event_callback_called.get() , num_clients * 2); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, num_clients); + } + + client_list.clear(); + + // join all io_threads + io_context->stop(); + for (const auto& io_thread : io_threads) + { + io_thread->join(); + } + io_threads.clear(); +} +#endif + +#if 1 +TEST(Callback, ServerAndClientManagers) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr int num_io_threads = 5; + std::vector> io_threads; + io_threads.reserve(num_io_threads); + + const auto io_context = std::make_shared(); + auto client_manager = eCAL::service::ClientManager::create(io_context); + auto server_manager = eCAL::service::ServerManager::create(io_context); + + atomic_signalable server1_event_callback_called(0); + atomic_signalable server2_event_callback_called(0); + + atomic_signalable client1_1_event_callback_called(0); + atomic_signalable client1_2_event_callback_called(0); + atomic_signalable client2_1_event_callback_called(0); + atomic_signalable client2_2_event_callback_called(0); + + for (int i = 0; i < num_io_threads; ++i) + { + io_threads.emplace_back(std::make_unique([&io_context]() + { + io_context->run(); + })); + } + + const eCAL::service::Server::ServiceCallbackT server_service_callback = [](auto, auto) -> void {}; + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback = [](auto, auto) -> void {}; + + // Lambda function that on call returns another lambda function that will increment the given atomic_signalable + auto increment_atomic_signalable = [](auto& atomic_signalable) -> auto + { + return [&atomic_signalable](auto, auto) -> void + { + atomic_signalable++; + }; + }; + + + auto server1 = server_manager->create_server(protocol_version, 0, server_service_callback, true, increment_atomic_signalable(server1_event_callback_called)); + auto server2 = server_manager->create_server(protocol_version, 0, server_service_callback, true, increment_atomic_signalable(server2_event_callback_called)); + + auto client1_1 = client_manager->create_client(protocol_version, "127.0.0.1", server1->get_port(), increment_atomic_signalable(client1_1_event_callback_called)); + auto client1_2 = client_manager->create_client(protocol_version, "127.0.0.1", server1->get_port(), increment_atomic_signalable(client1_2_event_callback_called)); + auto client2_1 = client_manager->create_client(protocol_version, "127.0.0.1", server2->get_port(), increment_atomic_signalable(client2_1_event_callback_called)); + auto client2_2 = client_manager->create_client(protocol_version, "127.0.0.1", server2->get_port(), increment_atomic_signalable(client2_2_event_callback_called)); + + // Wait for the clients to be connected + client1_1_event_callback_called.wait_for([&](int value) { return value >= 1; }, std::chrono::seconds(5)); + client1_2_event_callback_called.wait_for([&](int value) { return value >= 1; }, std::chrono::seconds(1)); + client2_1_event_callback_called.wait_for([&](int value) { return value >= 1; }, std::chrono::seconds(1)); + client2_2_event_callback_called.wait_for([&](int value) { return value >= 1; }, std::chrono::seconds(1)); + + server1_event_callback_called.wait_for([&](int value) { return value >= 2; }, std::chrono::seconds(1)); + server2_event_callback_called.wait_for([&](int value) { return value >= 2; }, std::chrono::seconds(1)); + + { + EXPECT_EQ(server1_event_callback_called, 2); + EXPECT_EQ(server2_event_callback_called, 2); + EXPECT_EQ(client1_1_event_callback_called, 1); + EXPECT_EQ(client1_2_event_callback_called, 1); + EXPECT_EQ(client2_1_event_callback_called, 1); + EXPECT_EQ(client2_2_event_callback_called, 1); + + EXPECT_EQ(server_manager->server_count(), 2); + EXPECT_EQ(client_manager->client_count(), 4); + } + + // Test what happens when the user deletes the client on his own + client1_1.reset(); + client1_1_event_callback_called.wait_for([&](int value) { return value >= 2; }, std::chrono::seconds(1)); + server1_event_callback_called.wait_for([&](int value) { return value >= 3; }, std::chrono::seconds(1)); + + { + EXPECT_EQ(server1_event_callback_called, 3); + EXPECT_EQ(server2_event_callback_called, 2); + EXPECT_EQ(client1_1_event_callback_called, 2); + EXPECT_EQ(client1_2_event_callback_called, 1); + EXPECT_EQ(client2_1_event_callback_called, 1); + EXPECT_EQ(client2_2_event_callback_called, 1); + + EXPECT_EQ(server_manager->server_count(), 2); + EXPECT_EQ(client_manager->client_count(), 3); + } + + // Test what happens when the user deletes the server on his own + server1.reset(); + server1_event_callback_called.wait_for([&](int value) { return value >= 4; }, std::chrono::seconds(1)); + client1_2_event_callback_called.wait_for([&](int value) { return value >= 2; }, std::chrono::seconds(1)); + + { + EXPECT_EQ(server1_event_callback_called, 4); + EXPECT_EQ(server2_event_callback_called, 2); + EXPECT_EQ(client1_1_event_callback_called, 2); + EXPECT_EQ(client1_2_event_callback_called, 2); + EXPECT_EQ(client2_1_event_callback_called, 1); + EXPECT_EQ(client2_2_event_callback_called, 1); + + EXPECT_EQ(server_manager->server_count(), 1); + EXPECT_EQ(client_manager->client_count(), 3); + } + + // Test what happens when the system stops all clients and servers + + server_manager->stop(); + client_manager->stop(); + + server2_event_callback_called.wait_for([&](int value) { return value >= 4; }, std::chrono::seconds(1)); + client2_1_event_callback_called.wait_for([&](int value) { return value >= 2; }, std::chrono::seconds(1)); + client2_2_event_callback_called.wait_for([&](int value) { return value >= 2; }, std::chrono::seconds(1)); + + { + EXPECT_EQ(server1_event_callback_called, 4); + EXPECT_EQ(server2_event_callback_called, 4); + EXPECT_EQ(client1_1_event_callback_called, 2); + EXPECT_EQ(client1_2_event_callback_called, 2); + EXPECT_EQ(client2_1_event_callback_called, 2); + EXPECT_EQ(client2_2_event_callback_called, 2); + + // The servers may be stopped, but they are still in the server manager + EXPECT_EQ(server_manager->server_count(), 1); + EXPECT_EQ(client_manager->client_count(), 3); + } + + for (const auto& io_thread : io_threads) + { + io_thread->join(); + } + } +} +#endif + +#if 1 +TEST(Callback, ServiceCallFromCallback) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic num_server_service_callback_called(0); + std::atomic num_client_response_callback1_called(0); + std::atomic num_client_response_callback2_called(0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called](const std::shared_ptr& /*request*/, const std::shared_ptr& /*response*/) -> void + { + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [](eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + + EXPECT_EQ(num_server_service_callback_called, 0); + EXPECT_EQ(num_client_response_callback1_called, 0); + EXPECT_EQ(num_client_response_callback2_called, 0); + + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback1_called, &num_client_response_callback2_called, client_v1] + (const eCAL::service::Error& error, const std::shared_ptr& /*response*/) -> void + { + EXPECT_FALSE(bool(error)); + num_client_response_callback1_called++; + client_v1->async_call_service(std::make_shared("2") + , [&num_client_response_callback2_called](const eCAL::service::Error& error, const auto& /*response*/) -> void + { + EXPECT_FALSE(bool(error)); + num_client_response_callback2_called++; + }); + }; + + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Call service and wait a short time + client_v1->async_call_service(std::make_shared("1"), response_callback); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + EXPECT_EQ(num_server_service_callback_called, 2); + EXPECT_EQ(num_client_response_callback1_called, 1); + EXPECT_EQ(num_client_response_callback2_called, 1); + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(Callback, SerializedServiceCallbacks) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr std::chrono::milliseconds server_callback_wait_time(50); + constexpr int num_clients = 5; + constexpr int num_threads = 5; + + const auto io_context = std::make_shared(); + auto server_manager = eCAL::service::ServerManager::create(io_context); + auto client_manager = eCAL::service::ClientManager::create(io_context); + + std::vector threads; + threads.reserve(num_threads); + + for (int i = 0; i < num_clients; i++) + { + threads.emplace_back([&io_context]() + { + io_context->run(); + }); + } + + atomic_signalable num_server_service_callback_called (0); + atomic_signalable num_client_response_callback_called (0); + atomic_signalable num_client_event_callback_called (0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called, server_callback_wait_time] + (const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::this_thread::sleep_for(server_callback_wait_time); + *response = "Response on \"" + *request + "\""; + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [] + (eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + { + num_client_event_callback_called++; + }; + + auto server = server_manager->create_server(protocol_version, 0, server_service_callback, false, server_event_callback); + std::vector> clients; + clients.reserve(num_clients); + for (int i = 0; i < num_clients; i++) + { + clients.push_back(client_manager->create_client(protocol_version, "127.0.0.1", server->get_port(), client_event_callback)); + } + + num_client_event_callback_called.wait_for([&num_clients](int value) -> bool { return value >= num_clients; }, std::chrono::milliseconds(500)); + + auto start = std::chrono::steady_clock::now(); + for (const auto& client : clients) + { + const auto request = std::make_shared("Request"); + auto response = std::make_shared(); + + auto client_response_callback = [&num_client_response_callback_called, response] + (const eCAL::service::Error& error, const std::shared_ptr& /*response*/) -> void + { + EXPECT_FALSE(bool(error)); + num_client_response_callback_called++; + }; + + client->async_call_service(request, client_response_callback); + } + + num_client_response_callback_called.wait_for([num_clients](int v) {return v >= num_clients;}, num_clients * server_callback_wait_time * 2); + + auto end = std::chrono::steady_clock::now(); + auto duration = end - start; + + EXPECT_GE(duration, num_clients * server_callback_wait_time); + + server_manager->stop(); + client_manager->stop(); + + // join all threads + for (auto& thread : threads) + { + thread.join(); + } + } +} +#endif + +#if 1 +TEST(ErrorCallback, ErrorCallbackNoServer) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + atomic_signalable num_client_response_callback_called(0); + std::atomic num_client_event_callback_called (0); + + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback_called](const eCAL::service::Error& error, const std::shared_ptr& /*response*/) -> void + { + EXPECT_TRUE(bool(error)); + num_client_response_callback_called++; + }; + + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + { + num_client_event_callback_called++; + }; + + + EXPECT_EQ(num_client_response_callback_called, 0); + + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version, "NonExistingEndpoint", 12345, client_event_callback); + + // Run the io_service + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Call service and wait a short time + client_v1->async_call_service(std::make_shared("Hello World"), response_callback); + + num_client_response_callback_called.wait_for([](int v) { return v >= 1;} , std::chrono::milliseconds(5000)); + + EXPECT_EQ(num_client_response_callback_called, 1); + EXPECT_EQ(num_client_event_callback_called, 0); + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(ErrorCallback, ErrorCallbackServerHasDisconnected) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + auto dummy_work = std::make_unique(*io_context); + + std::atomic num_server_service_callback_called (0); + std::atomic num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + std::atomic num_client_response_callback_called (0); + std::atomic num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected(0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called] + (const std::shared_ptr& /*request*/, const std::shared_ptr& response) -> void + { + *response = "Server running!"; + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + + num_server_event_callback_called++; + }; + + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait a short time for the client to connect + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + { + EXPECT_EQ(num_server_service_callback_called , 0); + EXPECT_EQ(num_server_event_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called , 0); + EXPECT_EQ(num_client_event_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + } + + // First service call. Everything should be fine + { + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [&num_client_response_callback_called] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + EXPECT_FALSE(error); + EXPECT_EQ(*response, "Server running!"); + num_client_response_callback_called++; + }; + client->async_call_service(std::make_shared("Everything fine?"), client_response_callback); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(client->get_state(), eCAL::service::State::CONNECTED); + EXPECT_EQ(client->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client->get_queue_size(), 0); + } + + // Server goes away + server = nullptr; + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_response_callback_called , 1); + EXPECT_EQ(num_client_event_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + + EXPECT_EQ(client->get_state(), eCAL::service::State::FAILED); + EXPECT_EQ(client->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client->get_queue_size(), 0); + } + + // Service call on the dead server. + { + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [&num_client_response_callback_called] + (const eCAL::service::Error& error, const std::shared_ptr& /*response*/) -> void + { + EXPECT_TRUE(error); + num_client_response_callback_called++; + }; + client->async_call_service(std::make_shared("Everything fine?"), client_response_callback); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // The client callback should be called another time, just with an error + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_server_event_callback_called , 2); + EXPECT_EQ(num_server_event_callback_called_connected , 1); + EXPECT_EQ(num_server_event_callback_called_disconnected, 1); + + EXPECT_EQ(num_client_response_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called , 2); + EXPECT_EQ(num_client_event_callback_called_connected , 1); + EXPECT_EQ(num_client_event_callback_called_disconnected, 1); + + EXPECT_EQ(client->get_state(), eCAL::service::State::FAILED); + EXPECT_EQ(client->get_accepted_protocol_version(), protocol_version); + EXPECT_EQ(client->get_queue_size(), 0); + } + + dummy_work.reset(); + client->stop(); + + // join the io_thread + io_thread.join(); + } +} +#endif + +#if 1 +TEST(ErrorCallback, ErrorCallbackClientDisconnects) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic num_server_service_callback_called (0); + std::atomic num_client_response_callback_called (0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called] + (const std::shared_ptr& /*request*/, const std::shared_ptr& response) -> void + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + *response = "Server running!"; + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [] + (eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + auto client_v1 = eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait a short time for the client to connect + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + { + EXPECT_EQ(num_server_service_callback_called , 0); + EXPECT_EQ(num_client_response_callback_called , 0); + } + + // First service call. Everything should be fine + { + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [&num_client_response_callback_called] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + EXPECT_FALSE(error); + EXPECT_EQ(*response, "Server running!"); + num_client_response_callback_called++; + }; + client_v1->async_call_service(std::make_shared("Everything fine?"), client_response_callback); + } + // Second and third service call that should fail, as we let the client go out of scope, before the server can answer on it. + for (int i = 0; i < 2; i++) + { + const eCAL::service::ClientSession::ResponseCallbackT client_response_callback + = [&num_client_response_callback_called] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + EXPECT_TRUE(error); + EXPECT_EQ(response, nullptr); + num_client_response_callback_called++; + }; + client_v1->async_call_service(std::make_shared("Everything fine?"), client_response_callback); + } + + // The first service call should be executed by now. + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + + { + EXPECT_EQ(num_server_service_callback_called , 1); + EXPECT_EQ(num_client_response_callback_called , 1); + } + + // Client goes away + client_v1 = nullptr; + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + // Both service calls should have failed by now. The second should have reached the server, but the client is already gone. + { + EXPECT_EQ(num_server_service_callback_called , 2); + EXPECT_EQ(num_client_response_callback_called , 3); + } + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(ErrorCallback, StressfulErrorsHalfwayThrough) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr int num_io_threads = 50; + constexpr int num_clients = 50; + constexpr int num_calls_per_client = 50; + constexpr std::chrono::milliseconds server_time_to_waste(10); + + //constexpr std::chrono::milliseconds wait_time_for_destroying_server = server_time_to_waste * total_calls / 2; + constexpr std::chrono::milliseconds wait_time_for_destroying_server = server_time_to_waste * num_calls_per_client / 2; + + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic num_server_service_callback_called (0); + atomic_signalable num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + atomic_signalable num_client_response_callback_called (0); + std::atomic num_client_response_callback_called_with_error (0); + std::atomic num_client_response_callback_called_without_error(0); + atomic_signalable num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected (0); + + const eCAL::service::Server::ServiceCallbackT service_callback + = [server_time_to_waste, &num_server_service_callback_called](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + *response = "Response on \"" + *request + "\""; + std::this_thread::sleep_for(server_time_to_waste); + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + + num_server_event_callback_called++; + }; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, service_callback, true, server_event_callback, critical_logger("Server")); + + { + EXPECT_EQ(num_server_service_callback_called , 0); + EXPECT_EQ(num_server_event_callback_called , 0); + EXPECT_EQ(num_server_event_callback_called_connected , 0); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called , 0); + EXPECT_EQ(num_client_response_callback_called_with_error , 0); + EXPECT_EQ(num_client_response_callback_called_without_error, 0); + EXPECT_EQ(num_client_event_callback_called.get() , 0); + EXPECT_EQ(num_client_event_callback_called_connected , 0); + EXPECT_EQ(num_client_event_callback_called_disconnected , 0); + } + + // Run the io service a bunch of times, so we hopefully trigger any race condition that may exist + std::vector> io_threads; + io_threads.reserve(num_io_threads); + for (int i = 0; i < num_io_threads; i++) + { + io_threads.emplace_back(std::make_unique([&io_context]() { io_context->run(); })); + } + + // Create all the clients + std::vector> client_list; + client_list.reserve(num_clients); + for (int c = 0; c < num_clients; c++) + { + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + client_list.push_back(eCAL::service::ClientSession::create(io_context, protocol_version,"127.0.0.1", server->get_port(), client_event_callback, critical_logger("Client " + std::to_string(c)))); + } + + // Directly run a bunch of clients and call each client a bunch of times + for (size_t c = 0; c < client_list.size(); c++) + { + for (int i = 0; i < num_calls_per_client; i++) + { + const std::shared_ptr request_string = std::make_shared("Client " + std::to_string(c) + ", Call " + std::to_string(i)); + + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback_called, &num_client_response_callback_called_with_error, &num_client_response_callback_called_without_error, request_string] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + if (error) + { + num_client_response_callback_called_with_error++; + ASSERT_EQ(response, nullptr); + } + else + { + num_client_response_callback_called_without_error++; + const std::string expected_response = "Response on \"" + *request_string + "\""; + ASSERT_EQ(*response, expected_response); + } + num_client_response_callback_called++; + }; + + client_list[c]->async_call_service(request_string, response_callback); + } + } + + // Now wait a short time for some clients having executed their calls. It is not enough time for every call. + std::this_thread::sleep_for(wait_time_for_destroying_server); + + { + EXPECT_TRUE(num_server_service_callback_called > 0); + EXPECT_TRUE(num_server_service_callback_called < (num_clients * num_calls_per_client)); + + EXPECT_EQ(num_server_event_callback_called , num_clients); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_TRUE(num_client_response_callback_called > 0); + EXPECT_TRUE(num_client_response_callback_called < (num_clients* num_calls_per_client)); + + EXPECT_TRUE(num_client_response_callback_called_with_error == 0); + EXPECT_TRUE(num_client_response_callback_called_without_error > 0); + + EXPECT_EQ(num_client_event_callback_called.get() , num_clients); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), num_clients); + } + + // delete server + server->stop(); + server = nullptr; + + num_server_event_callback_called .wait_for([num_clients](int v) { return v >= (2 * num_clients); }, std::chrono::milliseconds(5000)); + num_client_event_callback_called .wait_for([num_clients](int v) { return v >= (2 * num_clients); }, std::chrono::milliseconds(500)); + num_client_response_callback_called.wait_for([num_clients, num_calls_per_client](int v) { return v >= (num_clients* num_calls_per_client); }, std::chrono::milliseconds(500)); + + { + EXPECT_TRUE(num_server_service_callback_called > 0); + EXPECT_TRUE(num_server_service_callback_called < (num_clients * num_calls_per_client)); + + EXPECT_EQ(num_server_event_callback_called.get() , num_clients * 2); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, num_clients); + + EXPECT_EQ(num_client_response_callback_called.get(), num_clients* num_calls_per_client); + + EXPECT_TRUE(num_client_response_callback_called_with_error > 0); + EXPECT_TRUE(num_client_response_callback_called_without_error > 0); + + EXPECT_EQ(num_client_event_callback_called , num_clients * 2); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, num_clients); + } + + client_list.clear(); + + // join all io_threads + io_context->stop(); + for (const auto& io_thread : io_threads) + { + io_thread->join(); + } + io_threads.clear(); + } +} +#endif + +#if 1 +TEST(ErrorCallback, StressfulErrorsHalfwayThroughWithManagers) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr int num_io_threads = 50; + constexpr int num_clients = 50; + constexpr int num_calls_per_client = 50; + constexpr std::chrono::milliseconds server_time_to_waste(10); + + constexpr std::chrono::milliseconds wait_time_for_destroying_server = server_time_to_waste * num_calls_per_client / 2; + + const auto io_context = std::make_shared(); + + auto client_manager = eCAL::service::ClientManager::create(io_context, critical_logger("Client")); + auto server_manager = eCAL::service::ServerManager::create(io_context, critical_logger("Server")); + + std::atomic num_server_service_callback_called (0); + atomic_signalable num_server_event_callback_called (0); + std::atomic num_server_event_callback_called_connected (0); + std::atomic num_server_event_callback_called_disconnected(0); + + atomic_signalable num_client_response_callback_called (0); + std::atomic num_client_response_callback_called_with_error (0); + std::atomic num_client_response_callback_called_without_error(0); + atomic_signalable num_client_event_callback_called (0); + std::atomic num_client_event_callback_called_connected (0); + std::atomic num_client_event_callback_called_disconnected (0); + + const eCAL::service::Server::ServiceCallbackT service_callback + = [server_time_to_waste, &num_server_service_callback_called](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + *response = "Response on \"" + *request + "\""; + std::this_thread::sleep_for(server_time_to_waste); + num_server_service_callback_called++; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [&num_server_event_callback_called, &num_server_event_callback_called_connected, &num_server_event_callback_called_disconnected] + (eCAL::service::ServerEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ServerEventType::Connected) + num_server_event_callback_called_connected++; + else if (event == eCAL::service::ServerEventType::Disconnected) + num_server_event_callback_called_disconnected++; + + num_server_event_callback_called++; + }; + + auto server = server_manager->create_server(protocol_version, 0, service_callback, true, server_event_callback); + + { + EXPECT_EQ(num_server_service_callback_called , 0); + EXPECT_EQ(num_server_event_callback_called , 0); + EXPECT_EQ(num_server_event_callback_called_connected , 0); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_EQ(num_client_response_callback_called , 0); + EXPECT_EQ(num_client_response_callback_called_with_error , 0); + EXPECT_EQ(num_client_response_callback_called_without_error, 0); + EXPECT_EQ(num_client_event_callback_called , 0); + EXPECT_EQ(num_client_event_callback_called_connected , 0); + EXPECT_EQ(num_client_event_callback_called_disconnected , 0); + } + + // Run the io service a bunch of times, so we hopefully trigger any race condition that may exist + std::vector> io_threads; + io_threads.reserve(num_io_threads); + for (int i = 0; i < num_io_threads; i++) + { + io_threads.emplace_back(std::make_unique([&io_context]() { io_context->run(); })); + } + + // Create all the clients + std::vector> client_list; + client_list.reserve(num_clients); + for (int c = 0; c < num_clients; c++) + { + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [&num_client_event_callback_called, &num_client_event_callback_called_connected, &num_client_event_callback_called_disconnected] + (eCAL::service::ClientEventType event, const std::string& /*message*/) -> void + { + if (event == eCAL::service::ClientEventType::Connected) + num_client_event_callback_called_connected++; + else if (event == eCAL::service::ClientEventType::Disconnected) + num_client_event_callback_called_disconnected++; + + num_client_event_callback_called++; + }; + client_list.push_back(client_manager->create_client(protocol_version,"127.0.0.1", server->get_port(), client_event_callback)); + } + + // Directly run a bunch of clients and call each client a bunch of times + for (size_t c = 0; c < client_list.size(); c++) + { + for (int i = 0; i < num_calls_per_client; i++) + { + const std::shared_ptr request_string = std::make_shared("Client " + std::to_string(c) + ", Call " + std::to_string(i)); + + const eCAL::service::ClientSession::ResponseCallbackT response_callback + = [&num_client_response_callback_called, &num_client_response_callback_called_with_error, &num_client_response_callback_called_without_error, request_string] + (const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + num_client_response_callback_called++; + if (error) + { + num_client_response_callback_called_with_error++; + ASSERT_EQ(response, nullptr); + } + else + { + num_client_response_callback_called_without_error++; + const std::string expected_response = "Response on \"" + *request_string + "\""; + ASSERT_EQ(*response, expected_response); + } + }; + + client_list[c]->async_call_service(request_string, response_callback); + } + } + + // Now wait a short time for some clients having executed their calls. It is not enough time for every call. + std::this_thread::sleep_for(wait_time_for_destroying_server); + + { + EXPECT_TRUE(num_server_service_callback_called > 0); + EXPECT_TRUE(num_server_service_callback_called < (num_clients * num_calls_per_client)); + + EXPECT_EQ(num_server_event_callback_called , num_clients); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, 0); + + EXPECT_TRUE(num_client_response_callback_called > 0); + EXPECT_TRUE(num_client_response_callback_called < (num_clients* num_calls_per_client)); + + EXPECT_TRUE(num_client_response_callback_called_with_error == 0); + EXPECT_TRUE(num_client_response_callback_called_without_error > 0); + + EXPECT_EQ(num_client_event_callback_called , num_clients); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, 0); + + EXPECT_EQ(server->get_connection_count(), num_clients); + } + + // delete server + server_manager->stop(); + + num_client_event_callback_called.wait_for([num_clients](int v) { return v >= num_clients * 2; }, std::chrono::seconds(5)); + num_server_event_callback_called.wait_for([num_clients](int v) { return v >= num_clients * 2; }, std::chrono::seconds(5)); + num_client_response_callback_called.wait_for([num_clients, num_calls_per_client](int v) { return v >= num_clients * num_calls_per_client; }, std::chrono::seconds(5)); + + { + EXPECT_TRUE(num_server_service_callback_called > 0); + EXPECT_TRUE(num_server_service_callback_called < (num_clients * num_calls_per_client)); + + EXPECT_EQ(num_server_event_callback_called , num_clients * 2); + EXPECT_EQ(num_server_event_callback_called_connected , num_clients); + EXPECT_EQ(num_server_event_callback_called_disconnected, num_clients); + + EXPECT_EQ(num_client_response_callback_called, num_clients* num_calls_per_client); + + EXPECT_TRUE(num_client_response_callback_called_with_error > 0); + EXPECT_TRUE(num_client_response_callback_called_without_error > 0); + + EXPECT_EQ(num_client_event_callback_called , num_clients * 2); + EXPECT_EQ(num_client_event_callback_called_connected , num_clients); + EXPECT_EQ(num_client_event_callback_called_disconnected, num_clients); + } + + client_list.clear(); + client_manager->stop(); + + // join all io_threads + for (const auto& io_thread : io_threads) + { + io_thread->join(); + } + } +} +#endif + +#if 1 +TEST(BlockingCall, RegularBlockingCall) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr std::chrono::milliseconds server_callback_wait_time(50); + constexpr int num_calls = 3; + + const auto io_context = std::make_shared(); + const asio::io_context::work dummy_work(*io_context); + + std::atomic num_server_service_callback_called (0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called, server_callback_wait_time] + (const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::this_thread::sleep_for(server_callback_wait_time); + num_server_service_callback_called++; + *response = "Response on \"" + *request + "\""; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [] + (eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version, "127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + for (int i = 0; i < num_calls; i++) + { + auto start = std::chrono::steady_clock::now(); + + // Call the service blocking + const auto request = std::make_shared("Request " + std::to_string(i)); + auto response = std::make_shared(); + + auto error = client->call_service(request, response); + + { + auto time_elapsed = (std::chrono::steady_clock::now() - start); + + EXPECT_FALSE(bool(error)); + EXPECT_TRUE (time_elapsed >= server_callback_wait_time); + EXPECT_EQ (num_server_service_callback_called, i + 1); + EXPECT_EQ (*response, "Response on \"" + *request + "\""); + } + } + + // delete all objects + client = nullptr; + server = nullptr; + + // join the io_thread + io_context->stop(); + io_thread.join(); + } +} +#endif + +#if 1 +TEST(BlockingCall, BlockingCallWithErrorHalfwayThrough) // NOLINT +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr std::chrono::milliseconds server_callback_wait_time(100); + constexpr int num_calls_before_shutdown = 3; + constexpr int num_calls_after_shutdown = 3; + + const auto io_context = std::make_shared(); + auto dummy_work = std::make_unique(*io_context); + + std::atomic num_server_service_callback_called (0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called, server_callback_wait_time] + (const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::this_thread::sleep_for(server_callback_wait_time); + num_server_service_callback_called++; + *response = "Response on \"" + *request + "\""; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [] + (eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + auto server = eCAL::service::Server::create(io_context, protocol_version, 0, server_service_callback, true, server_event_callback); + auto client = eCAL::service::ClientSession::create(io_context, protocol_version, "127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait shortly for the client to connects + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // Successful calls + for (int i = 0; i < num_calls_before_shutdown; i++) + { + auto start = std::chrono::steady_clock::now(); + + // Call the service blocking + const auto request = std::make_shared("Request " + std::to_string(i)); + auto response = std::make_shared(); + + auto error = client->call_service(request, response); + + { + auto time_elapsed = (std::chrono::steady_clock::now() - start); + + EXPECT_FALSE(bool(error)); + EXPECT_TRUE (time_elapsed >= server_callback_wait_time); + EXPECT_EQ (num_server_service_callback_called, i + 1); + EXPECT_EQ (*response, "Response on \"" + *request + "\""); + } + } + + // Shutdown server in a few milliseconds (i.e. during the next call) + std::thread stop_thread([&server, num_calls_before_shutdown, server_callback_wait_time]() + { + auto sleep_time = 0.5 * server_callback_wait_time; + std::this_thread::sleep_for(sleep_time); + server->stop(); + }); + + // First failed call + // This call will reach the server, but the server will shut down before it + // can return a response. So the client has to wait for the server callback to + // finish, but then the server just closes the connection. + { + auto start = std::chrono::steady_clock::now(); + + // Call the service blocking + const auto request = std::make_shared("Request"); + auto response = std::make_shared(); + + auto error = client->call_service(request, response); + + { + auto time_elapsed = (std::chrono::steady_clock::now() - start); + EXPECT_TRUE(bool(error)); + EXPECT_TRUE (time_elapsed >= server_callback_wait_time); // We had to wait for the server callback, but didn't get any result :( + EXPECT_EQ (num_server_service_callback_called, num_calls_before_shutdown + 1); // The first call did reach the server, but it didn't return, as the server was shut down before it was able to return a response + EXPECT_EQ (response, nullptr); + } + } + + // failed calls + // These calls will not reach the server, as the server is already shut down, + // Therefore, the client will not wait for the server callback to finish. + for (int i = 0; i < num_calls_after_shutdown; i++) + { + auto start = std::chrono::steady_clock::now(); + + // Call the service blocking + const auto request = std::make_shared("Request " + std::to_string(i)); + auto response = std::make_shared(); + + auto error = client->call_service(request, response); + + { + auto time_elapsed = (std::chrono::steady_clock::now() - start); + + EXPECT_TRUE(bool(error)); + EXPECT_TRUE (time_elapsed < server_callback_wait_time); + EXPECT_EQ (num_server_service_callback_called, num_calls_before_shutdown + 1); + EXPECT_EQ (response, nullptr); + } + } + + client->stop(); + server->stop(); + dummy_work.reset(); + + // join the io_thread + io_thread.join(); + + stop_thread.join(); + + // delete all objects + client = nullptr; + server = nullptr; + } +} +#endif + +#if 1 +TEST(BlockingCall, Stopped) // NOLINT // This test shows the proper way to stop everything. I should adapt all other tests, too +{ + for (std::uint8_t protocol_version = min_protocol_version; protocol_version <= max_protocol_version; protocol_version++) + { + constexpr std::chrono::milliseconds server_callback_wait_time(500); + + const auto io_context = std::make_shared(); + auto server_manager = eCAL::service::ServerManager::create(io_context); + auto client_manager = eCAL::service::ClientManager::create(io_context); + + std::atomic num_server_service_callback_called (0); + + const eCAL::service::Server::ServiceCallbackT server_service_callback + = [&num_server_service_callback_called, server_callback_wait_time] + (const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + std::this_thread::sleep_for(server_callback_wait_time); + num_server_service_callback_called++; + *response = "Response on \"" + *request + "\""; + }; + + const eCAL::service::Server::EventCallbackT server_event_callback + = [] + (eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) -> void + {}; + + const eCAL::service::ClientSession::EventCallbackT client_event_callback + = [] + (eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) -> void + {}; + + auto server = server_manager->create_server(protocol_version, 0, server_service_callback, true, server_event_callback); + auto client = client_manager->create_client(protocol_version, "127.0.0.1", server->get_port(), client_event_callback); + + std::thread io_thread([&io_context]() + { + io_context->run(); + }); + + // Wait shortly for the client to connects + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // Shutdown server in a few milliseconds (i.e. during the next call) + std::thread stop_thread([&client_manager, &server_manager, &io_thread, server_callback_wait_time]() + { + auto sleep_time = 0.5 * server_callback_wait_time; + std::this_thread::sleep_for(sleep_time); + + client_manager->stop(); + server_manager->stop(); + + io_thread.join(); + + std::cerr << "io_context stopped" << std::endl; + }); + + constexpr int num_calls = 5; + + std::vector> call_threads; + call_threads.reserve(num_calls); + std::atomic callbacks_completed(0); + + for (int i = 0; i < num_calls; i++) + { + call_threads.push_back(std::make_unique([&client, &callbacks_completed, i]() + { + const auto request = std::make_shared("Request"); + auto response = std::make_shared(); + auto error = client->call_service(request, response); + + EXPECT_TRUE(bool(error)); + callbacks_completed++; + std::cerr << "Callback " + std::to_string(i) + " completed\n"; + })); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // Join all call threads + for (auto& call_thread : call_threads) + { + call_thread->join(); + } + + EXPECT_EQ(callbacks_completed, num_calls); + + stop_thread.join(); + + // Call the client again! it must not block... + { + auto request = std::make_shared("Request"); + auto response = std::make_shared(); + + auto error = client->call_service(request, response); + + EXPECT_TRUE(bool(error)); + EXPECT_EQ(*response.get(), ""); + } + } +} +#endif \ No newline at end of file diff --git a/samples/cpp/misc/time/CMakeLists.txt b/src/time/CMakeLists.txt similarity index 64% rename from samples/cpp/misc/time/CMakeLists.txt rename to src/time/CMakeLists.txt index 57b20d4..06bdf26 100644 --- a/samples/cpp/misc/time/CMakeLists.txt +++ b/src/time/CMakeLists.txt @@ -16,24 +16,8 @@ # # ========================= eCAL LICENSE ================================= -cmake_minimum_required(VERSION 3.10) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_subdirectory(linuxptp) +endif() -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - -project(time) - -find_package(eCAL REQUIRED) - -set(time_src - src/time.cpp -) - -ecal_add_sample(${PROJECT_NAME} ${time_src}) - -target_link_libraries(${PROJECT_NAME} eCAL::core) - -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) - -ecal_install_sample(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/misc) +add_subdirectory(localtime) diff --git a/src/time/include/dynamic_sleeper.h b/src/time/include/dynamic_sleeper.h new file mode 100644 index 0000000..e63bb81 --- /dev/null +++ b/src/time/include/dynamic_sleeper.h @@ -0,0 +1,185 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include +#include +#include + +/** +* @brief A class that is capable of sleeping the (approximately) correct amount of time while the rate of time is changing +*/ +class CDynamicSleeper +{ +public: + + /** + * @brief Creates an instance with a rate of 1 + * + * The rate is the relative speed of time based on the system time. + */ + CDynamicSleeper() : + rate(1.0), + lastSimTime(0) + { + lastSimTimeLocalTimestamp = std::chrono::steady_clock::now(); + } + + /** + * @brief Blocks for the given amount of nanoseconds. + * + * How long the function actually blocks depends on the current rate at which + * the time is supposed to run + * + * @param durationNsecs_ the duration in nanoseconds + */ + void sleepFor(long long durationNsecs_) { + auto nowReal = std::chrono::steady_clock::now(); + long long nowSim; + { + std::unique_lock modifyTimeLock(modifyTimeMutex); + nowSim = (long long)((double)((nowReal - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime; + } + sleepUntil(nowSim + durationNsecs_); + } + + /** + * @brief Blocks until the given point in sim time is reached + * + * How long the function actually blocks depends on the current rate at which + * the time is supposed to run + * + * @param sleepUntilTimeNsecs_ the simulation time in nanoseconds + */ + void sleepUntil(long long sleepUntilTimeNsecs_) { + auto startRealtime = std::chrono::steady_clock::now(); + long long loopStartSimTime; + double originalRate; + long long originalLastSimtime; + + { + std::unique_lock modifyTimeLock(modifyTimeMutex); + loopStartSimTime = (long long)((double)((startRealtime - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime; + originalRate = rate; + originalLastSimtime = lastSimTime; + } + + while (loopStartSimTime < sleepUntilTimeNsecs_) { + std::unique_lock waitLck(waitMutex); + if (originalRate > 0) { + std::chrono::duration realTimeToSleep((long long)((double)(sleepUntilTimeNsecs_ - loopStartSimTime) / originalRate)); + if (waitCv.wait_for(waitLck, realTimeToSleep) == std::cv_status::timeout) { + return; + } + } + else { + waitCv.wait(waitLck); + } + + { + std::unique_lock modifyTimeLock(modifyTimeMutex); + if (lastSimTime < originalLastSimtime) { + return; + } + startRealtime = std::chrono::steady_clock::now(); + loopStartSimTime = (long long)((double)((startRealtime - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime; + originalLastSimtime = lastSimTime; + originalRate = rate; + } + } + } + + /** + * @brief sets the rate at which the time is supposed to run. + * + * All threads that are waiting in the sleep function will automatically + * adjust their actual time to sleep based on the new rate. + * + * @param rate_ the rate of time relative to the system time + */ + void setRate(double rate_) { + auto now = std::chrono::steady_clock::now(); + std::unique_lock modifyTimeLock(modifyTimeMutex); + long long passedSystemNsecs = (now - lastSimTimeLocalTimestamp).count(); + long long passedSimtimeNsecs = (long long)((double)passedSystemNsecs * rate); + lastSimTimeLocalTimestamp = now; + lastSimTime += passedSimtimeNsecs; + rate = rate_; + waitCv.notify_all(); + } + + /** + * @brief Sets the sim time to the given value. + * + * If the given time is lower than the time that was given the last time or + * lower than the time that was computed the last time the rate was changed, + * all waiting threads will abort waiting and return. + * + * @param nsecs_ the new simulation time in nanoseconds + */ + void setTime(long long nsecs_) { + auto now = std::chrono::steady_clock::now(); + std::unique_lock modifyTimeLock(modifyTimeMutex); + lastSimTimeLocalTimestamp = now; + lastSimTime = nsecs_; + waitCv.notify_all(); + } + + /** + * @brief Sets the sim time and the rate of time to the given values. + * + * If the given time is lower than the time that was given the last time or + * lower than the time that was computed the last time the rate was changed, + * all waiting threads will abort waiting and return. + * + * @param time_nsecs_ the new simulatoin time in nanoseconds + * @param rate_ the rate of the simulation time relative to the system clock + */ + void setTimeAndRate(long long time_nsecs_, double rate_) { + auto now = std::chrono::steady_clock::now(); + std::unique_lock modifyTimeLock(modifyTimeMutex); + lastSimTimeLocalTimestamp = now; + lastSimTime = time_nsecs_; + rate = rate_; + waitCv.notify_all(); + } + + /** + * @brief getter for the internal simulation time used for computing how long to sleep + * @return the current simulation time + */ + long long getCurrentInternalSimTime() { + std::unique_lock modifyTimeLock(modifyTimeMutex); + auto now = std::chrono::steady_clock::now(); + return (long long)((double)((now - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime; + } + + ~CDynamicSleeper() {} + +private: + std::mutex waitMutex; /**< The mutex used for the condition variable that is used for sleeping*/ + std::condition_variable waitCv; /**< The condition variable that is used for sleeping and notifying the sleeping thread of a rate-change*/ + + std::mutex modifyTimeMutex; /**< The Mutex for protecting variables while modifying the time point or the rate of time*/ + double rate; /**< The current rate at which the time proceeds*/ + long long lastSimTime; /**< The last know time in the simulation*/ + std::chrono::time_point lastSimTimeLocalTimestamp; /**< The local time when we received the last simulation time*/ +}; diff --git a/src/time/include/ecaltime.h b/src/time/include/ecaltime.h new file mode 100644 index 0000000..822b6f9 --- /dev/null +++ b/src/time/include/ecaltime.h @@ -0,0 +1,114 @@ +/* ========================= 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 ================================= +*/ + +/** + * @brief eCAL time synchronization interface +**/ + +#ifndef ecaltime_h_included +#define ecaltime_h_included + +#ifdef _MSC_VER + #ifdef ECAL_TIME_PLUGIN_API_EXPORT + #define ECALTIME_API __declspec(dllexport) + #else + #define ECALTIME_API + #endif +#else + #define ECALTIME_API +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif /*__cplusplus*/ + /** + * @brief Initialize time sync interface. + * + * @return Zero if succeeded. + **/ + ECALTIME_API int etime_initialize(void); + + /** + * @brief Finalize time sync interface. + * + * @return Zero if succeeded. + **/ + ECALTIME_API int etime_finalize(void); + + /** + * @brief Get current time + * + * @return current time in ns. + **/ + ECALTIME_API long long etime_get_nanoseconds(); + + /** + * @brief Set current time in nano seconds if host is time master. + * + * @param time_ Current time in ns. + * + * @return Zero if succeeded non zero otherwise. + **/ + ECALTIME_API int etime_set_nanoseconds(long long time_); + + /** + * @brief Returns time synchronization state. + * + * @return Non zero if process is time synchronized. + **/ + ECALTIME_API int etime_is_synchronized(); + + /** + * @brief Checks wether this host is time master. + * + * @return Non zero if host is time master. + **/ + ECALTIME_API int etime_is_master(); + + /** + * @brief Blocks for the given amount of nanoseconds. + * + * The actual amount of (real-) time is influenced by the current rate at + * which the time is proceeding. + * It is not guaranteed, that the precision of this function actually is in + * nanoseconds. Limitations of the operating system might reduce the accuracy. + * + * @param duration_nsecs_ the duration in nanoseconds + */ + ECALTIME_API void etime_sleep_for_nanoseconds(long long duration_nsecs_); + + /** + * @brief Get the current error code and status message + * + * Adapters must return 0 if everything is OK. Each adapter can use it's own + * set of error codes. Everything non-zero is considered to indicate a + * problem. Adapters may provide aditional information with a status message. + * + * @param error_ [out] the current error code. 0 indicates that everything is OK + * @param status_message_ [in, out] a pre-allocated char array. The status message will be written to this memory. May be null, if the caller is not interested in the message. + * @param max_len_ [in] length of the allocated memory + */ + ECALTIME_API void etime_get_status(int* error_, char* status_message_, int max_len_); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*ecaltime_h_included*/ diff --git a/src/time/linuxptp/CMakeLists.txt b/src/time/linuxptp/CMakeLists.txt new file mode 100644 index 0000000..84aebd3 --- /dev/null +++ b/src/time/linuxptp/CMakeLists.txt @@ -0,0 +1,57 @@ +# ========================= 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 ================================= + +project(ecaltime-linuxptp) + +find_package(simpleini REQUIRED) + +set(ecal_time_linuxptp_src + src/ecal_time_linuxptp.cpp + src/ecaltime.cpp + src/convert_utf.cpp +) + +set(ecal_time_linuxptp_header + ../include/ecaltime.h + src/ecal_time_linuxptp.h + src/config/config.h +) + +if(UNIX) + set_source_files_properties(src/convert_utf.cpp PROPERTIES COMPILE_FLAGS -Wno-implicit-fallthrough) +endif() + +ecal_add_time_plugin(${PROJECT_NAME} SHARED ${ecal_time_linuxptp_src} ${ecal_time_linuxptp_header}) + +target_link_libraries(${PROJECT_NAME} eCAL::core simpleini::simpleini) + +target_include_directories(${PROJECT_NAME} + PRIVATE + $ + $) + +ecal_install_time_plugin(${PROJECT_NAME}) + +set(ECALTIME_CONFIG_FILES + ${ECALTIME_CONFIG_FILES} + ${CMAKE_CURRENT_SOURCE_DIR}/src/config/ecaltime.ini + PARENT_SCOPE) + +set_target_properties(${PROJECT_NAME} PROPERTIES + FOLDER core/time + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/${ECAL_TIME_PLUGIN_DIR}) diff --git a/src/time/linuxptp/src/clock.h b/src/time/linuxptp/src/clock.h new file mode 100644 index 0000000..13e8deb --- /dev/null +++ b/src/time/linuxptp/src/clock.h @@ -0,0 +1,74 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include +#include +#include + +#define CLOCKFD 3 +#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) +#define CLOCKID_TO_FD(clk) ((unsigned int) ~((clk) >> 3)) + +namespace linuxptp{ +/** + * @brief Queries the Capabilities of the given PTP Clock + * @param[in] clockId The PTP Clock of interest + * @param[out] caps The capabilities of the PTP Clock + * @return 0 if succeeded or -1 if failed. If the function fails, errno is set accordingly. + */ +static int getPtpClockCaps(const clockid_t clockId, struct ptp_clock_caps *caps){ + int err = ioctl(CLOCKID_TO_FD(clockId), PTP_CLOCK_GETCAPS, caps); + return err; +} + +/** + * @brief opens the (PTP) hardware clock + * @param[in] device A path to the clock to open (e.g. /dev/ptp0) + * @param[out] clockId The ID to access the clock. Only valid if the function succeeded. + * @return 0 if succeeded or -1 if failed. If the function fails, errno is set accordingly. + */ +int openClock(const char *device, clockid_t *clockId){ + + struct ptp_clock_caps caps; + int file_descriptor = open(device, O_RDONLY); + + if (file_descriptor == -1){ + return -1; + }else{ + *clockId = FD_TO_CLOCKID(file_descriptor); + // Check if the 'thing' we just opened actually is a PTP clock + if(getPtpClockCaps(*clockId, &caps)){ + close(file_descriptor); + return -1; + }else{ + return 0; + } + } +} + +/** + * @brief closes the (PTP) clock + * @param[in] clockId the clock ID to close + * @return 0 on success or -1 if failed. If the function fails, errno is set accordingly. + */ +int closeClock(clockid_t clockId){ + return close(CLOCKID_TO_FD(clockId)); +} +} diff --git a/samples/cpp/misc/timer/src/timer.cpp b/src/time/linuxptp/src/config/config.h similarity index 53% rename from samples/cpp/misc/timer/src/timer.cpp rename to src/time/linuxptp/src/config/config.h index e7a385b..096686b 100644 --- a/samples/cpp/misc/timer/src/timer.cpp +++ b/src/time/linuxptp/src/config/config.h @@ -17,40 +17,35 @@ * ========================= eCAL LICENSE ================================= */ -#include -#include +#pragma once + +#include #include -#include - -const int timout_ms = 10; - -int main(int argc, char **argv) -{ - // initialize eCAL API - eCAL::Initialize(argc, argv, "person publisher"); - // set process state - eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); - - eCAL::string::CPublisher pub("hello"); - eCAL::CTimer timer; - - timer.Start(timout_ms, [&pub](){ - pub.Send("Hello world"); - std::cout << "Sent: Hello world" << std::endl; - }); - - // enter main loop - while(eCAL::Ok()) - { - // sleep 500 ms - eCAL::Process::SleepMS(500); - } +#include +#include +#include - timer.Stop(); - // finalize eCAL API - eCAL::Finalize(); +namespace LinuxPtpConfig { - return(0); -} + /** + * @brief reads the file ~/.ecal/ecaltime.ini to get the device + * @return the device value from the linuxptp section + */ + std::string getDevice() { + CSimpleIniA ini; + + std::string path_to_ini = eCAL::Util::GeteCALConfigPath(); + path_to_ini += "ecaltime.ini"; + int err = ini.LoadFile(path_to_ini.c_str()); + if (err != SI_OK){ + std::cerr << "Error reading ecaltime config file" << std::endl; + } + + const char * pVal = ini.GetValue("linuxptp", "device", "/dev/ptp0"); + + std::string device(pVal); + return device; + } +} diff --git a/src/time/linuxptp/src/config/ecaltime.ini b/src/time/linuxptp/src/config/ecaltime.ini new file mode 100644 index 0000000..31c0dc0 --- /dev/null +++ b/src/time/linuxptp/src/config/ecaltime.ini @@ -0,0 +1,15 @@ +; --------------------------------------------- +; ecaltime-linuxptp Settings +; --------------------------------------------- +; +; device = /dev/ptp0 The device can be any ptp clock. +; Alternatively, you can use: +; - CLOCK_MONOTONIC (a steady clock with undefined epoche) +; - CLOCK_REALTIME (the current system time) +; - CLOCK_TAI (Like CLOCK_REALTIME but in International Atomic Time) +; +; --------------------------------------------- +[linuxptp] +device = /dev/ptp0 + + diff --git a/ecal/core/src/convert_utf.cpp b/src/time/linuxptp/src/convert_utf.cpp similarity index 100% rename from ecal/core/src/convert_utf.cpp rename to src/time/linuxptp/src/convert_utf.cpp diff --git a/ecal/core/src/convert_utf.h b/src/time/linuxptp/src/convert_utf.h similarity index 100% rename from ecal/core/src/convert_utf.h rename to src/time/linuxptp/src/convert_utf.h diff --git a/src/time/linuxptp/src/ecal_time_linuxptp.cpp b/src/time/linuxptp/src/ecal_time_linuxptp.cpp new file mode 100644 index 0000000..0d36e56 --- /dev/null +++ b/src/time/linuxptp/src/ecal_time_linuxptp.cpp @@ -0,0 +1,146 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "ecal_time_linuxptp.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +Linuxptp::Linuxptp(): + is_initialized(false), + status_message("linuxptp adapter has not been initialized"), + error_code(-1) +{} + +int Linuxptp::initialize(std::string device){ + std::unique_lock lk (clock_mutex); + + if(is_initialized){ + return 0; + } + + int err; + + if (device == "CLOCK_REALTIME"){ + clock_id = CLOCK_REALTIME; + err = 0; + } else if (device == "CLOCK_MONOTONIC"){ + clock_id = CLOCK_MONOTONIC; + err = 0; + } +#ifdef CLOCK_TAI + else if (device == "CLOCK_TAI"){ + clock_id = CLOCK_TAI; + err = 0; + } +#endif /* CLOCK_TAI */ + else { + err = openClock(device.c_str(), &clock_id); + } + + { + std::unique_lock status_lk(status_mutex); + if (err){ + error_code = errno; + }else{ + error_code = 0; + } + + if(err){ + status_message.assign("Failed to open clock "); + status_message += "\""; + status_message += device; + status_message += "\": "; + status_message += strerror(errno); + std::cerr << status_message << std::endl; + + is_initialized = false; + } else { + status_message.assign("Linux PTP Adapter is OK. Using device \""); + status_message += device + "\"."; + is_initialized = true; + } + } + return err; +} + +int Linuxptp::finalize(){ + std::unique_lock lk (clock_mutex); + is_initialized = false; + int err = close(CLOCKID_TO_FD(clock_id)); + if (err){ + perror("Error closing clock"); + } + return err; +} + +long long Linuxptp::getCurrentNsecs(){ + std::unique_lock lk (clock_mutex); + if(is_initialized){ + timespec ts; + int err = clock_gettime(clock_id, &ts); + long long nanoseconds = (long long)ts.tv_nsec + (long long)ts.tv_sec * 1000000000LL; + if(err){ + error_code = errno; + status_message.assign("An error occured getting the current time: "); + status_message += strerror(errno); + } + return nanoseconds; + } else{ + return 0; + } +} + +int Linuxptp::openClock(const char *device, clockid_t *clockId){ + + struct ptp_clock_caps caps; + int file_descriptor = open(device, O_RDONLY); + + if (file_descriptor == -1){ + return -1; + }else{ + *clockId = FD_TO_CLOCKID(file_descriptor); + // Check if the 'thing' we just opened actually is a PTP clock + if(getPtpClockCaps(*clockId, &caps)){ + close(file_descriptor); + return -1; + }else{ + return 0; + } + } +} + +void Linuxptp::getStatus(int& error_, std::string* status_message_){ + std::unique_lock status_lk(status_mutex); + error_ = error_code; + if(status_message_){ + status_message_->assign(status_message.c_str()); + } +} + +int Linuxptp::getPtpClockCaps(const clockid_t clockId, struct ptp_clock_caps *caps){ + int err = ioctl(CLOCKID_TO_FD(clockId), PTP_CLOCK_GETCAPS, caps); + return err; +} diff --git a/src/time/linuxptp/src/ecal_time_linuxptp.h b/src/time/linuxptp/src/ecal_time_linuxptp.h new file mode 100644 index 0000000..d70c24a --- /dev/null +++ b/src/time/linuxptp/src/ecal_time_linuxptp.h @@ -0,0 +1,90 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include +#include + +#define CLOCKFD 3 +#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) +#define CLOCKID_TO_FD(clk) ((unsigned int) ~((clk) >> 3)) + +class Linuxptp +{ +public: + Linuxptp(); + + /** + * @brief opens the (PTP) hardware clock + * @param[in] device A path to the clock to open (e.g. /dev/ptp0) + * @return 0 if succeeded or -1 if failed. If the function fails, the error is written as stderr. + */ + int initialize(std::string device); + + /** + * @brief closes the (PTP) clock + * @return 0 on success or -1 if failed. If the function fails, the error is written as stderr + */ + int finalize(); + + /** + * @brief queries the clock and returns it's duration since the epoch in nanoseconds + * @return the current time (or 0, if an error occurred) + */ + long long getCurrentNsecs(); + + /** + * @brief Get the current error code and status message + * + * An error code of 0 is considered to be OK. Any other error code is + * considered to indicate a problem. + * The Status message may be a nullpointer. + * + * @param error_ [out] the error code + * @param status_message_ [out] a human-readable status message. May be nullptr. + */ + void getStatus(int& error_, std::string* status_message_); + +private: + clockid_t clock_id; /** < A handle to the current clock */ + bool is_initialized; /** < Wether this time adapter has been successfully initialized */ + std::mutex clock_mutex; /** < Mutex to prevent different threads from simultaniously initializing or using the clock */ + + std::string status_message; /** < The last status message */ + int error_code; /** < The last error code */ + std::mutex status_mutex; /** < Mutex for protecting the status message and error code*/ + + /** + * @brief Queries the Capabilities of the given PTP Clock + * @param[in] clockId The PTP Clock of interest + * @param[out] caps The capabilities of the PTP Clock + * @return 0 if succeeded or -1 if failed. If the function fails, errno is set accordingly. + */ + static int getPtpClockCaps(const clockid_t clockId, struct ptp_clock_caps *caps); + + /** + * @brief opens the (PTP) hardware clock + * @param[in] device A path to the clock to open (e.g. /dev/ptp0) + * @param[out] clockId The ID to access the clock. Only valid if the function succeeded. + * @return 0 if succeeded or -1 if failed. If the function fails, errno is set accordingly. + */ + int openClock(const char *device, clockid_t *clockId); +}; diff --git a/src/time/linuxptp/src/ecaltime.cpp b/src/time/linuxptp/src/ecaltime.cpp new file mode 100644 index 0000000..cc249b8 --- /dev/null +++ b/src/time/linuxptp/src/ecaltime.cpp @@ -0,0 +1,79 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include +#include + +#include "ecal_time_linuxptp.h" +#include "config/config.h" + +Linuxptp linuxptp_adapter; + +ECALTIME_API int etime_initialize(void) +{ + std::string device = LinuxPtpConfig::getDevice(); + return linuxptp_adapter.initialize(device); +} + +ECALTIME_API int etime_finalize(void) +{ + return linuxptp_adapter.finalize(); +} + +ECALTIME_API long long etime_get_nanoseconds() +{ + return linuxptp_adapter.getCurrentNsecs(); +} + +ECALTIME_API int etime_set_nanoseconds(long long /*time_*/) +{ + return -1; +} + +ECALTIME_API int etime_is_synchronized() +{ + return 1; +} + +ECALTIME_API int etime_is_master() +{ + return 0; +} + +ECALTIME_API void etime_sleep_for_nanoseconds(long long duration_nsecs_) { + std::chrono::nanoseconds duration(duration_nsecs_); + std::this_thread::sleep_for(duration); +} + +ECALTIME_API void etime_get_status(int* error_, char* status_message_, int max_len_) { + int error; + if (status_message_ && max_len_ > 0) { + std::string status_message; + linuxptp_adapter.getStatus(error, &status_message); + strncpy(status_message_, status_message.c_str(), max_len_ - 1); + status_message_[max_len_ - 1] = (char)0x0; + } + else { + linuxptp_adapter.getStatus(error, nullptr); + } + if (error_) { + *error_ = error; + } +} diff --git a/src/time/localtime/CMakeLists.txt b/src/time/localtime/CMakeLists.txt new file mode 100644 index 0000000..1416d90 --- /dev/null +++ b/src/time/localtime/CMakeLists.txt @@ -0,0 +1,56 @@ +# ========================= 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 ================================= + +project(ecaltime-localtime) + +find_package(Threads REQUIRED) + +set(ecal_time_localtime_src + src/ecaltime.cpp +) + +if(WIN32) + set(ecal_time_localtime_dll_src + src/dllmain.cpp + ) +endif() + +set(ecal_time_localtime_header + ../include/ecaltime.h +) + +ecal_add_time_plugin(${PROJECT_NAME} SHARED ${ecal_time_localtime_src} ${ecal_time_localtime_header} ${ecal_time_localtime_dll_src}) + +target_compile_definitions(${PROJECT_NAME} PRIVATE ECAL_TIME_PLUGIN_API_EXPORT) + +target_include_directories(${PROJECT_NAME} + PRIVATE + $ + $) + +target_link_libraries(${PROJECT_NAME} + Threads::Threads + $<$,$>,$>>:rt> + ) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_time_plugin(${PROJECT_NAME}) + +set_target_properties(${PROJECT_NAME} PROPERTIES + FOLDER core/time + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/${ECAL_TIME_PLUGIN_DIR}) diff --git a/src/time/localtime/src/dllmain.cpp b/src/time/localtime/src/dllmain.cpp new file mode 100644 index 0000000..f0c88b1 --- /dev/null +++ b/src/time/localtime/src/dllmain.cpp @@ -0,0 +1,18 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "windows.h" + +BOOL APIENTRY DllMain( HMODULE /*hModule*/, + DWORD ul_reason_for_call, + LPVOID /*lpReserved*/ + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/src/time/localtime/src/ecaltime.cpp b/src/time/localtime/src/ecaltime.cpp new file mode 100644 index 0000000..825a98a --- /dev/null +++ b/src/time/localtime/src/ecaltime.cpp @@ -0,0 +1,70 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include +#include +#include + +ECALTIME_API int etime_initialize(void) +{ + return 0; +} + +ECALTIME_API int etime_finalize(void) +{ + return 0; +} + +ECALTIME_API long long etime_get_nanoseconds() +{ + auto now = std::chrono::system_clock::now(); + return std::chrono::duration_cast(now.time_since_epoch()).count(); +} + +ECALTIME_API int etime_set_nanoseconds(long long /*time_*/) +{ + return -1; +} + +ECALTIME_API int etime_is_synchronized() +{ + return 1; +} + +ECALTIME_API int etime_is_master() +{ + return 1; +} + +ECALTIME_API void etime_sleep_for_nanoseconds(long long duration_nsecs_) { + std::chrono::nanoseconds duration(duration_nsecs_); + std::this_thread::sleep_for(duration); +} + +ECALTIME_API void etime_get_status(int* error_, char* status_message_, int max_len_) { + if (error_) { + *error_ = 0; + } + if (status_message_ && max_len_ > 0) { + static const char* status{ "everything is fine."}; + strncpy(status_message_, status, max_len_ - 1); + status_message_[max_len_ -1] = (char)0x0; + } +} diff --git a/lib/ecal_protobuf/CMakeLists.txt b/src/utils/CMakeLists.txt similarity index 50% rename from lib/ecal_protobuf/CMakeLists.txt rename to src/utils/CMakeLists.txt index 19293c6..d1f6e19 100644 --- a/lib/ecal_protobuf/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -15,43 +15,47 @@ # limitations under the License. # # ========================= eCAL LICENSE ================================= +project(ecal-utils) -find_package(Protobuf REQUIRED) +find_package(Threads REQUIRED) -project(proto) +set (ecal_utils_includes + include/ecal_utils/command_line.h + include/ecal_utils/filesystem.h + include/ecal_utils/ecal_utils.h + include/ecal_utils/str_convert.h + include/ecal_utils/string.h + include/ecal_utils/portable_endian.h +) -set(ecal_protobuf_src - src/ecal_proto_decoder.cpp - src/ecal_proto_dyn.cpp - src/ecal_proto_maximum_array_dimensions.cpp - src/ecal_proto_message_filter.cpp - src/ecal_proto_visitor.cpp +set(ecal_utils_src + src/command_line.cpp + src/filesystem.cpp + src/str_convert.cpp ) -set(ecal_protobuf_header - include/ecal/protobuf/ecal_proto_decoder.h - include/ecal/protobuf/ecal_proto_dyn.h - include/ecal/protobuf/ecal_proto_hlp.h - include/ecal/protobuf/ecal_proto_maximum_array_dimensions.h - include/ecal/protobuf/ecal_proto_message_filter.h - include/ecal/protobuf/ecal_proto_visitor.h +ecal_add_static_library(${PROJECT_NAME} + ${ecal_utils_includes} + ${ecal_utils_src} ) -ecal_add_library(${PROJECT_NAME} ${ecal_protobuf_src} ${ecal_protobuf_header}) add_library(eCAL::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) -target_include_directories(${PROJECT_NAME} PUBLIC + +target_include_directories(${PROJECT_NAME} PRIVATE src/) +target_include_directories(${PROJECT_NAME} PUBLIC $ $ ) -target_link_libraries(${PROJECT_NAME} PUBLIC protobuf::libprotobuf) - -ecal_install_library(${PROJECT_NAME}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) -install( - FILES ${ecal_protobuf_header} - DESTINATION "${INSTALL_INCLUDE_DIR}/ecal/protobuf" - COMPONENT sdk +# Create a source tree that mirrors the filesystem +source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" + FILES + ${ecal_utils_includes} + ${ecal_utils_src} ) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER lib) +ecal_install_library(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core/utils) diff --git a/src/utils/include/ecal_utils/command_line.h b/src/utils/include/ecal_utils/command_line.h new file mode 100644 index 0000000..6367d83 --- /dev/null +++ b/src/utils/include/ecal_utils/command_line.h @@ -0,0 +1,33 @@ +/* ========================= eCAL LICENSE ================================= +* +* Copyright (C) 2016 - 2019 Continental Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ========================= eCAL LICENSE ================================= +*/ + +#include +#include + +namespace EcalUtils +{ + namespace CommandLine + { +#ifdef WIN32 + std::vector GetUtf8Argv(); + + std::string GetUtf8CommandLine(); +#endif // WIN32 + } +} \ No newline at end of file diff --git a/src/utils/include/ecal_utils/ecal_utils.h b/src/utils/include/ecal_utils/ecal_utils.h new file mode 100644 index 0000000..191c071 --- /dev/null +++ b/src/utils/include/ecal_utils/ecal_utils.h @@ -0,0 +1,677 @@ +/* ========================= 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 ================================= +*/ + +/** + * eCALSysCore utilities +**/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#if defined(_MSC_VER) && defined(__clang__) && !defined(CINTERFACE) +#define CINTERFACE +#endif +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#else +#include +#include +#include +#include +#include +#endif // _WIN32 + +#include "string.h" + +namespace EcalUtils +{ + namespace Filesystem + { + /** + * @brief Get file extension from path + * + * @param path file path + * + * @return file extension + **/ + inline std::string GetExtension(const std::string& path) + { + size_t idx_ext = path.find_last_of('.'); + if (idx_ext != std::string::npos) + { + return path.substr(idx_ext + 1); + } + return ""; + } + + /** + * @brief Get file name without extension from path + * + * @param path file path + * + * @return file name without extension + **/ + inline std::string GetBaseName(const std::string& path) + { + size_t idx_base = path.find_last_of('\\'); + if (idx_base == std::string::npos) + { + idx_base = path.find_last_of('/'); + } + + if (idx_base != std::string::npos) + { + std::string base = path.substr(idx_base + 1); + size_t idx_ext = base.find_last_of('.'); + if (idx_ext != std::string::npos) + { + return base.erase(idx_ext); + } + else + { + return base; + } + } + return path; + } + + /** + * @brief Get file name with extension from path + * + * @param path file path + * + * @return file name with extension + **/ + inline std::string GetFileName(const std::string& path) + { + size_t idx_name = path.find_last_of('\\'); + if (idx_name == std::string::npos) + { + idx_name = path.find_last_of('/'); + } + + if (idx_name != std::string::npos) + { + return path.substr(idx_name + 1); + } + return path; + } + + /** + * @brief Check if file path is relative + * + * @param path file path + * + * @return true if the path is relative, false otherwise + **/ + inline bool IsRelative(const std::string &path) + { + return (path.find("..") != std::string::npos || + path.find("./") != std::string::npos); + } + + /** + * @brief Make path absolute using a base path + * + * @param path file path + * @param base base path + **/ + inline void MakeAbsolute(std::string& path, const std::string& base) + { + std::string tmp = base; + size_t idx_path = path.find(".."); + + if (idx_path == std::string::npos) + { + idx_path = path.find("./"); + if (idx_path != std::string::npos) + { + path = tmp + path.substr(idx_path + 1); + } + } + else + { + while (idx_path != std::string::npos) + { + path = path.substr(idx_path + 2); + size_t idx_base = tmp.find_last_of('\\'); + if (idx_base == std::string::npos) + { + idx_base = tmp.find_last_of('/'); + } + tmp = tmp.substr(0, idx_base); + path = tmp + path; + + idx_path = path.find(".."); + } + } + } + + /** + * @brief Make path absolute from a relative path + * + * @param path the path + **/ + inline void MakeAbsolute(std::string& path) + { +#ifdef _WIN32 + // make the absolute path to the executable + char abs_path[MAX_PATH]; + if (_fullpath(abs_path, path.c_str(), MAX_PATH) != nullptr) + { + path = abs_path; + } +#else + // absolute path is created only by cutting "." and ".." from the path + char abs_path[PATH_MAX]; + if (realpath(path.c_str(), abs_path) != nullptr) + { + path = abs_path; + } +#endif + } + + #ifdef _WIN32 + const std::string separator = "\\"; + const std::string last_folder = "..\\"; + #else + const std::string separator = "/"; + const std::string last_folder = "../"; + #endif // _WIN32 + + inline std::string GetRelativePath(const std::string& path, const std::string& base) + { + std::vector path_vector, base_vector; + String::Split(path, separator, path_vector); + String::Split(base, separator, base_vector); + + size_t size = (path_vector.size() < base_vector.size()) ? path_vector.size() : base_vector.size(); + unsigned int same_size(0); + for (unsigned int i = 0; i < size; ++i) + { + if (path_vector[i] != base_vector[i]) + { + same_size = i; + break; + } + } + + std::string str_relative_path = ""; + if (same_size > 0) + { + for (unsigned int i = 0; i < base_vector.size() - same_size; ++i) + { + str_relative_path += last_folder; + } + } + + for (unsigned int i = same_size; i < path_vector.size(); ++i) + { + str_relative_path += path_vector[i]; + if (i < path_vector.size() - 1) + { + str_relative_path += separator; + } + } + + return str_relative_path; + } + + inline std::string ExpandEnvVars(const std::string& input) + { + std::string output; + + size_t m; + for (size_t n = 0; n < input.length(); n++) + { + switch (input[n]) + { +#ifdef _WIN32 + case '%': +#endif + case '$': + { + enum eBracket + { + Bracket_None, + Bracket_Normal = ')', + Bracket_Curly = '}', +#ifdef _WIN32 + Bracket_Windows = '%', +#endif + Bracket_Max + }; + eBracket bracket; + +#ifdef _WIN32 + if (input[n] == '%') + { + bracket = Bracket_Windows; + } + else +#endif + if (n == input.length() - 1) + { + bracket = Bracket_None; + } + else + { + switch (input[n + 1]) + { + case '(': + bracket = Bracket_Normal; + n++; // skip the bracket + break; + + case '{': + bracket = Bracket_Curly; + n++; // skip the bracket + break; + + default: + bracket = Bracket_None; + } + } + + m = n + 1; + + while (m < input.length() && (isalnum(input[m]) || input[m] == '_')) + m++; + + std::string var_name = input.substr(n + 1, m - n - 1); + bool expanded = false; + char* tmp = getenv(var_name.c_str()); + if (tmp != nullptr) + { + output += tmp; + expanded = true; + } + else + { + // variable doesn't exist => don't change anything +#ifdef _WIN32 + if (bracket != Bracket_Windows) +#endif + if (bracket != Bracket_None) + { + output += input[n - 1]; + } + output += input[n] + var_name; + } + + // check the closing bracket + if (bracket != Bracket_None) { + if (m == input.length() || input[m] != bracket) + { + // under MSW it's common to have '%' characters in the registry + // and it's annoying to have warnings about them each time, so + // ignore them silently if they are not used for env vars + // + // under Unix, OTOH, this warning could be useful for the user to + // understand why isn't the variable expanded as intended +//#ifndef _WIN32 + //EcalSysLogger::Log(std::string("Environment variables expansion failed: missing '") + static_cast(bracket) + std::string("' at position ") + std::to_string((unsigned int)(m + 1)) + " in '" + input + "'.", spdlog::level::info); +//#endif + } + else + { + // skip closing bracket unless the variables wasn't expanded + if (!expanded) + { + output += (char)bracket; + } + m++; + } + } + + n = m - 1; // skip variable name + } + break; + + case '\\': + // backslash can be used to suppress special meaning of % and $ + if (n != input.length() - 1 && (input[n + 1] == '%' || input[n + 1] == '$')) + { + output += input[++n]; + break; + } + else + { + output += input[n]; + } + break; + + default: + output += input[n]; + } + } + return output; + } + + } // namespace Path + + namespace CommandLine + { + /** + * @brief Searches the string for the start of the next argument by skipping all spaces + * + * @param arg_string The command line as string + * @param start_at Where to start looking for the next non-space character + * + * @return The start of the next argument or std::string::npos, if there is no further argument. + */ + inline size_t GetStartOfNextArgument(const std::string& arg_string, size_t start_at) + { + size_t pos = start_at; + + // Find beginning of arguments, if there are whitespaces at the front (which probably will never happen anyway) + while (pos < arg_string.size()) + { + if (std::isspace(static_cast(arg_string.at(pos)))) + { + pos++; + } + else + { + break; + } + } + + // If there are no further arguments, we return std::string::npos (we cannot return -1, as we are using size_t) + if (pos == arg_string.size()) + { + return std::string::npos; + } + + return pos; + } + + /** + * @brief Searches the given argument string for the end of argument that starts at the given start point + * + * Note: This function is intended for working with Windows and Linux style + * coomand lines. There are however some differences between those two + * styles that might hurt compatibility: + * + * - On Windows: + * - only double quote (") qualifies as quote (to enclose a string) + * - quotes can be escaped by a backslash (\" will result in a quote) + * - The backslash is otherwise used as path separator (it does not escape a space) + * + * - On Linux: + * - quotes can be double or single quotes (" or ') to enclose a string + * - quotes can be escaped by a backslash (\" will result in a quote) + * - The backslash is used as separator only (it can also escape spaces) + * + * - Combined rules: + * - A backslash will escape whatever comes after it (=> like Linux. A backslash as the last character might happen for paths to diretories on Windows however, which would lead to errors) + * - strings can be in double or single quotes (=> like Linux, hoping that nobody will use single quotes on Windows) + * + * @param arg_string the command line string + * @param start_at the start position of the current argument + * + * @return The end of the next argument + */ + inline size_t GetEndOfNextArgument(const std::string& arg_string, size_t start_at) + { + size_t pos = start_at; + bool double_quote = false; + bool single_quote = false; + // Find the end of the argument + while (pos < arg_string.size()) + { + if (arg_string.at(pos) == '\\') + { + // Escape / skip the next char + pos += 2; + continue; + } + else if (arg_string.at(pos) == ' ' && !double_quote && !single_quote) + { + // First space outside a string terminates the argument + // As what we have actually done is finding the next space, we have to decrement the position again in order to get the argument end + pos--; + break; + } + else if (arg_string.at(pos) == '\"') + { + if (!single_quote) { + // Start or end a double quoted string + double_quote = !double_quote; + } + pos++; + } + else if (arg_string.at(pos) == '\'') + { + if (!double_quote) { + // Start or end a single quoted string + single_quote = !single_quote; + } + pos++; + } + else + { + pos++; + } + } + + // If the command line is not well-formed, the backslash might have gotten us outside the string + if (pos > arg_string.size() - 1) + { + pos = arg_string.size() - 1; + } + + return pos; + } + + /** + * @brief Splits the given command line into its arguments. + * + * The maximum number of splits can be set with the max_number_of_arguments + * parameter. If set, the function will stop splitting the command line + * and return all remaining arguments in the last element of the list. + * + * If the command line does not contain as many arguments as the + * max_number_of_arguments parameter would indicate, the returned argument + * list will be shorter! + * + * For the rules of splitting see @see{GetEndOfNextArgument()} + * + * @param input_command_line The command line to split + * @param max_number_of_arguments The maximum desired number of splits. Can be 0, in order to not stop splitting prematurely. + * + * @return A list of arguments + */ + inline std::vector splitCommandLine(const std::string& input_command_line, size_t max_number_of_arguments = 0) + { + std::vector argument_list; + + size_t next_part_start = 0; + + for (size_t i = 0; (max_number_of_arguments == 0) || (i < max_number_of_arguments); i++) + { + // Check if we are already outside the string + if (next_part_start >(input_command_line.size() - 1)) + { + break; + } + + // Get the actual start of the next argument + size_t argument_start = GetStartOfNextArgument(input_command_line, next_part_start); + + if (argument_start != std::string::npos) + { + // Get the end of the next argument + size_t argument_end = GetEndOfNextArgument(input_command_line, argument_start); + + if (argument_end != std::string::npos) + { + if ((max_number_of_arguments == 0) + || (i != max_number_of_arguments - 1)) + { + // Add the argument to the argument list + argument_list.emplace_back(input_command_line.substr(argument_start, argument_end - argument_start + 1)); + next_part_start = argument_end + 1; + } + else + { + // Add all remaining arguments as one big block + argument_list.emplace_back(input_command_line.substr(argument_start)); + } + } + else + { + break; + } + } + else + { + // The Command line might not contain enough arguments, especiall if the user did not set a maximum number of arguments + break; + } + } + return argument_list; + } + + inline std::vector ToArgv(const std::string& command_line) + { + std::vector argv; + + bool quote = false; + bool squote = false; + bool inside_arg = false; + + std::string current_arg; + + size_t pos = 0; + while (pos < command_line.size()) + { + char current_char = command_line.at(pos); + + if (inside_arg && !quote && !squote && std::isspace(static_cast(current_char))) + { + // first space outside a quoted string terminates the argument + argv.insert(argv.end(), current_arg); + current_arg.clear(); + inside_arg = false; + } + else if (current_char == '\'' && !quote) + { + // Only evaluate single-quotes if we are not currently in a double-quoted string + inside_arg = true; + squote = !squote; + } + else if (current_char == '\"' && !squote) + { + // Only evaluate double-quotes if we are not currently in a single-quoted string + inside_arg = true; + quote = !quote; + } + else if (current_char == '\\') + { + // Copy the next character and skip its evaluation + if ((pos + 1) < command_line.size()) + { + current_arg += command_line.at(pos + 1); + } + inside_arg = true; + pos++; + } + else if (quote || squote) + { + // as long as we are in a quoted string we simply copy everything + current_arg += current_char; + } + else if (!std::isspace(static_cast(current_char))) + { + // when we are not in a quoted string and get a non-space character we copy it + current_arg += current_char; + inside_arg = true; + } + + pos++; + } + + // Add the last arg + if (inside_arg) + { + argv.insert(argv.end(), current_arg); + } + return argv; + } + + inline std::string ToCommandLine(const std::vector& argument_vector) + { + std::vector escaped_arguments; + escaped_arguments.reserve(argument_vector.size()); + + size_t complete_char_num(0); + for (const std::string& argument : argument_vector) + { + std::string escaped_arg; + escaped_arg.reserve(argument.size() + 2); + + bool constains_space = (argument.find(' ') != std::string::npos); + + // Escape special characters + if (constains_space) escaped_arg += '\"'; + for (char c : argument) + { + if (c == '\\') // Escape [\] + escaped_arg += "\\\\"; + else if (c == '\"') // Escape ["] + escaped_arg += "\\\""; + else if (c == '\'') // Escape ['] + escaped_arg += "\\\'"; + else + escaped_arg += c; + } + if (constains_space) escaped_arg += '\"'; + + if(escaped_arg.empty()) + escaped_arg = "\"\""; + + complete_char_num += escaped_arg.size(); + + escaped_arguments.push_back(escaped_arg); + } + + std::string command_line_string; + command_line_string.reserve(complete_char_num + escaped_arguments.size()); + + for (auto arg_it = escaped_arguments.begin(); arg_it != escaped_arguments.end(); arg_it++) + { + if (arg_it != escaped_arguments.begin()) + command_line_string += ' '; + command_line_string += *arg_it; + } + + return command_line_string; + } + } +} // namespace Utility diff --git a/src/utils/include/ecal_utils/filesystem.h b/src/utils/include/ecal_utils/filesystem.h new file mode 100644 index 0000000..42aebc8 --- /dev/null +++ b/src/utils/include/ecal_utils/filesystem.h @@ -0,0 +1,142 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include +#include +#include + +#include + +namespace EcalUtils +{ + namespace Filesystem + { + enum OsStyle : int + { + Combined = 0, + Windows = 1, + Unix = 2, + +#if defined _WIN32 + Current = Windows, +#else + Current = Unix, +#endif + }; + + enum Type : int + { + Unknown, + RegularFile, + Dir, + CharacterDevice, + BlockDevice, + Fifo, + SymbolicLink, + Socket + }; + + class FileStatus + { + public: + FileStatus(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + ~FileStatus(); + + bool IsOk() const; + Type GetType() const; + + int64_t FileSize() const; + + bool PermissionRootRead() const; + bool PermissionRootWrite() const; + bool PermissionRootExecute() const; + bool PermissionGroupRead() const; + bool PermissionGroupWrite() const; + bool PermissionGroupExecute() const; + bool PermissionOwnerRead() const; + bool PermissionOwnerWrite() const; + bool PermissionOwnerExecute() const; + + bool CanOpenDir() const; + + private: + std::string path_; + bool is_ok_; +#ifdef WIN32 + struct __stat64 file_status_; +#else // WIN32 + struct stat file_status_; +#endif + }; + + Type GetType(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + bool IsDir(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + bool IsFile(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + std::map DirContent(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + bool MkDir(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + bool MkPath(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + bool CopyFile(const std::string& source, const std::string& destination, OsStyle input_path_style = OsStyle::Combined); + + bool DeleteDir(const std::string& source, OsStyle input_path_style = OsStyle::Combined); + + std::string GetAbsoluteRoot(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + bool IsAbsolute(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + bool IsRelative(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + std::string CleanPath(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + std::vector CleanPathComponentList(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + std::string AbsolutePath(const std::string& base_path, const std::string& relative_path, OsStyle input_path_style = OsStyle::Combined); + std::string AbsolutePath(const std::string& relative_path, OsStyle input_path_style = OsStyle::Combined); + + std::string RelativePath(const std::string& base_path, const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + std::string CurrentWorkingDir(); + std::string ApplicationDir(); + + std::string ChangeSeperators(const std::string& path, OsStyle output_path_style, OsStyle input_path_style = OsStyle::Combined); + + std::string ToUnixSeperators(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + std::string ToNativeSeperators(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + inline char NativeSeparator(OsStyle seperator_style = OsStyle::Current) + { + switch (seperator_style) + { + case OsStyle::Windows: + return '\\'; + default: + return '/'; + } + } + + bool IsEqual(const std::string& path1, const std::string& path2, OsStyle compare_for = OsStyle::Current); + + std::string FileName(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + + std::string BaseName(const std::string& path, OsStyle input_path_style = OsStyle::Combined); + } +} diff --git a/src/utils/include/ecal_utils/portable_endian.h b/src/utils/include/ecal_utils/portable_endian.h new file mode 100644 index 0000000..d4bb45e --- /dev/null +++ b/src/utils/include/ecal_utils/portable_endian.h @@ -0,0 +1,134 @@ +// This file has been taken from: +// https://gist.github.com/panzi/6856583#file-portable_endian-h +// It includes manual changes for the QNX platform +// +// "License": Public Domain +// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. +// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to +// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it +// an example on how to get the endian conversion functions on different platforms. + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# include +# ifdef __GNUC__ +# include +# endif + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) htonll(x) +# define htole64(x) (x) +# define be64toh(x) ntohll(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__QNXNTO__) + +# include +# include + +#else + +# error platform not supported + +#endif + +#endif \ No newline at end of file diff --git a/src/utils/include/ecal_utils/str_convert.h b/src/utils/include/ecal_utils/str_convert.h new file mode 100644 index 0000000..02feca4 --- /dev/null +++ b/src/utils/include/ecal_utils/str_convert.h @@ -0,0 +1,40 @@ +/* ========================= eCAL LICENSE ================================= +* +* Copyright (C) 2016 - 2019 Continental Corporation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* ========================= eCAL LICENSE ================================= +*/ + +#include + +namespace EcalUtils +{ + namespace StrConvert + { +#ifdef WIN32 + std::string WideToAnsi(const std::wstring& wstr, unsigned int ansi_code_page); + + std::wstring AnsiToWide(const std::string& str, unsigned int ansi_code_page); + + std::string WideToUtf8(const std::wstring& wstr); + + std::wstring Utf8ToWide(const std::string& str); + + std::string AnsiToUtf8(const std::string& str, unsigned int ansi_code_page); + + std::string Utf8ToAnsi(const std::string& str, unsigned int ansi_code_page); +#endif // WIN32 + } +} \ No newline at end of file diff --git a/src/utils/include/ecal_utils/string.h b/src/utils/include/ecal_utils/string.h new file mode 100644 index 0000000..401e284 --- /dev/null +++ b/src/utils/include/ecal_utils/string.h @@ -0,0 +1,269 @@ +/* ========================= 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 ================================= +*/ + +#pragma once + +#include + +#include +#include +#include // std::isspace + +namespace EcalUtils +{ + namespace String + { + /// Replace all occurrences of from (which must not be the empty string) + /// in str with to, starting at position start. + template + S Replace(const S& str, const S& from, const S& to, typename S::size_type start = 0) + { + S result; + typename S::size_type pos = 0; + result.append(str, 0, start); + do + { + pos = str.find(from, start); + if (pos != S::npos) + { + result.append(str, start, pos - start); + result.append(to); + start = pos + from.length(); + } + else result.append(str, start, str.size() - start); + } while (pos != S::npos); + + return result; + } + + inline bool Icharcompare(char a, char b) + { + return(toupper(a) == toupper(b)); + } + + inline bool Icompare(const std::string& s1, const std::string& s2) + { + return((s1.size() == s2.size()) && + equal(s1.begin(), s1.end(), s2.begin(), Icharcompare)); + } + + template + auto FindCaseInsensitive(const Container& container, const std::string& value) + { + typename Container::iterator it = container.begin(); + while (it != container.end()) + { + if (Icompare(*it, value)) + { + break; + } + ++it; + } + return it; + } + + template + void Split(const std::string& str, const std::string& delim, Container& parts) + { + size_t end = 0; + while (end < str.size()) + { + size_t start = end; + while (start < str.size() && (delim.find(str[start]) != std::string::npos)) + { + start++; + } + end = start; + while (end < str.size() && (delim.find(str[end]) == std::string::npos)) + { + end++; + } + if (end - start != 0) + { + parts.insert(parts.end(), std::string(str, start, end - start)); + } + } + } + + template + void SplitQuotedString(const std::string& str, Container& parts, char escape_char = '\\', bool remove_quotes = true, bool keep_empty_strings = false) + { + enum State + { + NONE, + STRING, + QUOTED_STRING + }; + + State current_state = State::NONE; + size_t pos = 0; + + std::string current_string; + current_string.reserve(str.size()); // Reserve enough memory so we never need to resize the string + + while (pos < str.size()) + { + if (current_state == State::NONE) + { + if (!std::isspace(static_cast(str[pos]))) + { + if (str[pos] == '\"') + { + // start quoted string + current_state = State::QUOTED_STRING; + if (!remove_quotes) + { + // Only copy the quote if the user wants it to remain part of the output + current_string += str[pos]; + } + } + else if ((pos < (str.size() - 1) && (str[pos] == escape_char)) + && ((str[pos + 1] == '\"') || (str[pos + 1] == escape_char))) + { + current_state = State::STRING; + pos++; // skip escape character and copy the next char + current_string += str[pos]; + } + else + { + current_state = State::STRING; + // copy the current char + current_string += str[pos]; + } + } + } + else if (current_state == State::STRING) + { + if (std::isspace(static_cast(str[pos]))) + { + current_state = State::NONE; + parts.insert(parts.end(), current_string); + current_string.clear(); + } + else if (str[pos] == '\"') + { + // start quoted string + current_state = State::QUOTED_STRING; + if (!remove_quotes) + { + // Only copy the quote if the user wants it to remain part of the output + current_string += str[pos]; + } + } + else if ((pos < (str.size() - 1) && (str[pos] == escape_char)) + && ((str[pos + 1] == '\"') || (str[pos + 1] == escape_char))) + { + pos++; // skip escape character and copy the next char + current_string += str[pos]; + } + else + { + // copy the current char + current_string += str[pos]; + } + } + else if (current_state == State::QUOTED_STRING) + { + if (str[pos] == '\"') + { + // end the quoted string + current_state = State::STRING; + if (!remove_quotes) + { + // Only copy the quote if the user wants it to remain part of the output + current_string += str[pos]; + } + } + else if ((pos < (str.size() - 1) && (str[pos] == escape_char)) + && ((str[pos + 1] == '\"') || (str[pos + 1] == escape_char))) + { + pos++; // skip escape character and copy the next char + current_string += str[pos]; + } + else + { + // copy the current char + current_string += str[pos]; + } + } + + pos++; + } + + if (!current_string.empty() || keep_empty_strings) + { + // Insert the last element + parts.insert(parts.end(), current_string); + } + } + + inline std::string Trim(const std::string &s) + { + std::string sCopy(s); + sCopy.erase(sCopy.begin(), std::find_if_not(sCopy.begin(), sCopy.end(), [](char c) { return std::isspace(static_cast(c)); })); + sCopy.erase(std::find_if_not(sCopy.rbegin(), sCopy.rend(), [](char c) { return std::isspace(static_cast(c)); }).base(), sCopy.end()); + return sCopy; + } + + template + inline std::string Join(const std::string &delim, const Container& parts) + { + std::stringstream ss; + for (auto i = parts.begin(); i != parts.end(); i++) + { + if (i != parts.begin()) + { + ss << delim; + } + ss << *i; + } + return ss.str(); + } + + inline bool CenterString(std::string& str, char padding_char, size_t max_size) + { + if (str.length() >= max_size) return false; + + size_t empty_space = max_size - str.length(); + size_t left_padding = empty_space / 2; + + str.insert(0, left_padding, padding_char); + str.insert(str.length(), empty_space - left_padding, padding_char); + + return true; + } + + inline std::string htmlEscape(const std::string& str) + { + std::string buffer; + buffer.reserve(str.size()); + for (size_t pos = 0; pos != str.size(); ++pos) { + switch (str[pos]) { + case '&': buffer.append("&"); break; + case '\"': buffer.append("""); break; + case '\'': buffer.append("'"); break; + case '<': buffer.append("<"); break; + case '>': buffer.append(">"); break; + default: buffer.append(&str[pos], 1); break; + } + } + return buffer; + } + } +} diff --git a/src/utils/src/command_line.cpp b/src/utils/src/command_line.cpp new file mode 100644 index 0000000..18ac3e5 --- /dev/null +++ b/src/utils/src/command_line.cpp @@ -0,0 +1,70 @@ +/* ========================= eCAL LICENSE ================================= +* +* Copyright (C) 2021 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 "ecal_utils/command_line.h" + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include + #include +#endif // WIN32 + +#include +#include + +namespace EcalUtils +{ + namespace CommandLine + { + +#ifdef WIN32 + std::vector GetUtf8Argv() + { + int commandline_w_argc(0); + + std::vector utf8_argv_vector; + + LPWSTR* wstring_arg_list_ptr = CommandLineToArgvW(GetCommandLineW(), &commandline_w_argc); + if (wstring_arg_list_ptr == nullptr) + { + std::cerr << "CommandLineToArgvW failed" << std::endl; + } + else + { + // Fill the vector with UTF8 conversions of Windows wide-strings + utf8_argv_vector.reserve(commandline_w_argc); + for (int i = 0; i < commandline_w_argc; i++) + { + utf8_argv_vector.push_back(EcalUtils::StrConvert::WideToUtf8(std::wstring(wstring_arg_list_ptr[i]))); + } + LocalFree(wstring_arg_list_ptr); + } + + return utf8_argv_vector; + } + + std::string GetUtf8CommandLine() + { + return EcalUtils::StrConvert::WideToUtf8(GetCommandLineW()); + } +#endif + + } +} \ No newline at end of file diff --git a/src/utils/src/filesystem.cpp b/src/utils/src/filesystem.cpp new file mode 100644 index 0000000..2c845df --- /dev/null +++ b/src/utils/src/filesystem.cpp @@ -0,0 +1,876 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include + #include + #include // SHFileOperation + + #include // ANSI/Wide/UTF8 conversion +#else // _WIN32 + #include // strerror() + #include + #include // O_RDONLY + #ifndef __QNXNTO__ + #include // File-tree traversal + #endif + #include + + #if defined (__APPLE__) + #include + #elif defined (__linux__) + #include + #elif defined (__FreeBSD__) + #include + #include + #include + #else + #include + #endif + +#endif // _WIN32 + +#include // stat +#include // errno, ENOENT, EEXIST + +#include +#include +#include + +#include + +namespace EcalUtils +{ + namespace Filesystem + { + //////////////////////////////////////// + // FileStatus Class + //////////////////////////////////////// + + FileStatus::FileStatus(const std::string& path, OsStyle input_path_style) + { +#ifdef WIN32 + std::wstring w_native_path_ = StrConvert::Utf8ToWide(ToNativeSeperators(path, input_path_style)); + const int error_code = _wstat64(w_native_path_.c_str(), &file_status_); +#else // WIN32 + const int error_code = stat(ToNativeSeperators(path, input_path_style).c_str(), &file_status_); +#endif // WIN32 + is_ok_ = (error_code == 0); + } + + FileStatus::~FileStatus() {} + + bool FileStatus::IsOk() const + { + return is_ok_; + } + + Type FileStatus::GetType() const + { + if (!is_ok_) + return Type::Unknown; + + switch (file_status_.st_mode & S_IFMT) { + case S_IFREG: return Type::RegularFile; + case S_IFDIR: return Type::Dir; + case S_IFCHR: return Type::CharacterDevice; +#ifndef WIN32 + case S_IFBLK: return Type::BlockDevice; + case S_IFIFO: return Type::Fifo; + case S_IFLNK: return Type::SymbolicLink; + case S_IFSOCK: return Type::Socket; +#endif // !WIN32 + default: return Type::Unknown; + } + + } + + int64_t FileStatus::FileSize() const + { + if (!is_ok_) + return 0; + + return file_status_.st_size; + } + +#ifdef WIN32 + bool FileStatus::PermissionRootRead() const { return 0 != (file_status_.st_mode & S_IREAD); } + bool FileStatus::PermissionRootWrite() const { return 0 != (file_status_.st_mode & S_IWRITE); } + bool FileStatus::PermissionRootExecute() const { return 0 != (file_status_.st_mode & S_IEXEC); } + bool FileStatus::PermissionGroupRead() const { return 0 != (file_status_.st_mode & S_IREAD); } + bool FileStatus::PermissionGroupWrite() const { return 0 != (file_status_.st_mode & S_IWRITE); } + bool FileStatus::PermissionGroupExecute() const { return 0 != (file_status_.st_mode & S_IEXEC); } + bool FileStatus::PermissionOwnerRead() const { return 0 != (file_status_.st_mode & S_IREAD); } + bool FileStatus::PermissionOwnerWrite() const { return 0 != (file_status_.st_mode & S_IWRITE); } + bool FileStatus::PermissionOwnerExecute() const { return 0 != (file_status_.st_mode & S_IEXEC); } +#else // WIN32 + bool FileStatus::PermissionRootRead() const { return 0 != (file_status_.st_mode & S_IRUSR); } + bool FileStatus::PermissionRootWrite() const { return 0 != (file_status_.st_mode & S_IWUSR); } + bool FileStatus::PermissionRootExecute() const { return 0 != (file_status_.st_mode & S_IXUSR); } + bool FileStatus::PermissionGroupRead() const { return 0 != (file_status_.st_mode & S_IRGRP); } + bool FileStatus::PermissionGroupWrite() const { return 0 != (file_status_.st_mode & S_IWGRP); } + bool FileStatus::PermissionGroupExecute() const { return 0 != (file_status_.st_mode & S_IXGRP); } + bool FileStatus::PermissionOwnerRead() const { return 0 != (file_status_.st_mode & S_IROTH); } + bool FileStatus::PermissionOwnerWrite() const { return 0 != (file_status_.st_mode & S_IWOTH); } + bool FileStatus::PermissionOwnerExecute() const { return 0 != (file_status_.st_mode & S_IXOTH); } +#endif // WIN32 + + bool FileStatus::CanOpenDir() const + { + if (!is_ok_) + return false; + + if (GetType() != Type::Dir) + return false; + + bool can_open_dir(false); +#ifdef WIN32 + std::string find_file_path = path_ + "\\*"; + std::replace(find_file_path.begin(), find_file_path.end(), '/', '\\'); + + std::wstring w_find_file_path = StrConvert::Utf8ToWide(find_file_path); + + HANDLE hFind; + WIN32_FIND_DATAW ffd; + hFind = FindFirstFileW(w_find_file_path.c_str(), &ffd); + if (hFind != INVALID_HANDLE_VALUE) + { + can_open_dir = true; + } + FindClose(hFind); +#else // WIN32 + DIR *dp = opendir(path_.c_str()); + if (dp != NULL) + { + can_open_dir = true; + closedir(dp); + } +#endif // WIN32 + + return can_open_dir; + } + + //////////////////////////////////////// + // Path Functions + //////////////////////////////////////// + + Type GetType(const std::string& path, OsStyle input_path_style) + { + FileStatus file_status(path, input_path_style); + return file_status.GetType(); + } + + bool IsDir(const std::string& path, OsStyle input_path_style) + { + return GetType(path, input_path_style) == Type::Dir; + } + + bool IsFile(const std::string& path, OsStyle input_path_style) + { + return GetType(path, input_path_style) == Type::RegularFile; + } + + std::map DirContent(const std::string& path, OsStyle input_path_style) + { + std::string clean_path = ToNativeSeperators(CleanPath(path, input_path_style), input_path_style); + + std::map content; +#ifdef WIN32 + std::string find_file_path = clean_path + "\\*"; + std::replace(find_file_path.begin(), find_file_path.end(), '/', '\\'); + + std::wstring w_find_file_path = StrConvert::Utf8ToWide(find_file_path); + + HANDLE hFind; + WIN32_FIND_DATAW ffd; + hFind = FindFirstFileW(w_find_file_path.c_str(), &ffd); + if (hFind == INVALID_HANDLE_VALUE) + { + std::cerr << "FindFirstFile Error" << std::endl; + return content; + } + + do + { + std::string file_name = StrConvert::WideToUtf8(std::wstring(ffd.cFileName)); + if ((file_name != ".") && (file_name != "..")) + content.emplace(file_name, FileStatus(clean_path + "\\" + file_name)); + } while (FindNextFileW(hFind, &ffd) != 0); + FindClose(hFind); +#else // WIN32 + DIR *dp; + struct dirent *dirp; + if ((dp = opendir(clean_path.c_str())) == NULL) + { + std::cerr << "Error opening directory: " << strerror(errno) << std::endl; + return content; + } + + while ((dirp = readdir(dp)) != NULL) + { + std::string file_name(dirp->d_name); + if ((file_name != ".") && (file_name != "..")) + content.emplace(file_name, FileStatus(clean_path + "/" + std::string(dirp->d_name), input_path_style)); + } + closedir(dp); + +#endif // WIN32 + return content; + } + + bool MkDir(const std::string& path, OsStyle input_path_style) + { + std::string native_path = ChangeSeperators(path, OsStyle::Current, input_path_style); + +#if defined(_WIN32) + int ret = _wmkdir(StrConvert::Utf8ToWide(native_path).c_str()); +#else + mode_t mode = 0755; + int ret = mkdir(native_path.c_str(), mode); +#endif + return ret == 0; + } + + bool MkPath(const std::string& path, OsStyle input_path_style) + { + std::string native_path = ChangeSeperators(CleanPath(path, input_path_style), OsStyle::Current, input_path_style); + + if (native_path.empty()) + { + return false; + } + else if (native_path.back() == NativeSeparator(OsStyle::Current)) + { + // Remove tailing separator + native_path.pop_back(); + } + + // Create last directory + if (MkDir(native_path, OsStyle::Current)) + { + return true; + } + + // If creating last directory did not work, we may need to create the parent + switch (errno) + { + case ENOENT: + // parent didn't exist, try to create it + { + size_t pos = native_path.find_last_of(NativeSeparator(OsStyle::Current)); + if (pos == std::string::npos) + return false; + if (!MkPath(native_path.substr(0, pos), OsStyle::Current)) + return false; + } + + // now, try to create again + return MkDir(native_path, OsStyle::Current); + + case EEXIST: + // done! + return IsDir(native_path, OsStyle::Current); + + default: + return false; + } + } + +#ifdef CopyFile +#define CopyFile_4c768dd038fa415ca1399214711e2b9f CopyFile +#undef CopyFile +#endif // CopyFile + bool CopyFile(const std::string& source, const std::string& destination, OsStyle input_path_style) +#ifdef CopyFile_4c768dd038fa415ca1399214711e2b9f +#define CopyFile CopyFile_4c768dd038fa415ca1399214711e2b9f +#undef CopyFile_4c768dd038fa415ca1399214711e2b9f +#endif // CopyFile_4c768dd038fa415ca1399214711e2b9f + { + std::string source_clean = ToNativeSeperators(CleanPath(source, input_path_style), input_path_style); + std::string destination_clean = ToNativeSeperators(CleanPath(destination, input_path_style), input_path_style); + +#if defined WIN32 + std::wstring w_source_clean = StrConvert::Utf8ToWide(source_clean); + std::wstring w_destination_clean = StrConvert::Utf8ToWide(destination_clean); + return (CopyFileW(w_source_clean.c_str(), w_destination_clean.c_str(), FALSE) != FALSE); +#else // WIN32 + int input_fd {-1}, output_fd {-1}; + bool copy_succeeded {false}; + if ((input_fd = open(source_clean.c_str(), O_RDONLY)) == -1) + { + return false; + } + if ((output_fd = creat(destination_clean.c_str(), 0660)) == -1) + { + close(input_fd); + return false; + } +#if defined (__APPLE__) + int result = fcopyfile(input_fd, output_fd, 0, COPYFILE_ALL); + copy_succeeded = (result == 0); +#elif defined(__FreeBSD__) + FileStatus file_status(source_clean, OsStyle::Current); + int result = sendfile(output_fd, input_fd, 0, file_status.FileSize(), nullptr, nullptr, 0); + copy_succeeded = (result != -1); +#elif defined(__linux__) + off_t bytesCopied = 0; + FileStatus file_status(source_clean, OsStyle::Current); + int result = sendfile(output_fd, input_fd, &bytesCopied, file_status.FileSize()); + copy_succeeded = (result != -1); +#else + FileStatus file_status(source_clean, OsStyle::Current); + void *mem = mmap(NULL, file_status.FileSize(), PROT_READ, MAP_SHARED, input_fd, 0); + if(mem != MAP_FAILED) + { + ssize_t written_bytes = write(output_fd, mem, file_status.FileSize()); + copy_succeeded = (written_bytes == file_status.FileSize()); + munmap(mem, file_status.FileSize()); + } + else + copy_succeeded = false; +#endif + close(input_fd); + close(output_fd); + return copy_succeeded; +#endif // WIN32 + } + + bool DeleteDir(const std::string& source, OsStyle input_path_style) + { + std::string clean_path = ToNativeSeperators(CleanPath(source, input_path_style), input_path_style); + +#if defined(WIN32) + + std::wstring w_clean_path = StrConvert::Utf8ToWide(source); + + // Abuse the internal buffer of the string to make a double-null- + // terminated-string as requested by the Win32 API. + // This is safe as C++11 demands that the data of an std string is in + // continuous memory. + w_clean_path += L'\0'; + w_clean_path += L'\0'; + + // Using the Win32 Shell API is the recommended way to delete non-empty directories on Window + SHFILEOPSTRUCTW file_op = { + NULL // hwnd + , FO_DELETE // wFunc + , w_clean_path.data() // pFrom + , L"" // pTo + , FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT // fFlags + , false // fAnyOperationsAborted + , 0 // hNameMappings + , L"" // lpszProgressTitle + }; + int error = SHFileOperationW(&file_op); + return (error == 0); + +#elif defined(__QNXNTO__) + // TODO: Find an alternative to traverse directories on QNX operating system that does not use fts + return false; +#else // WIN32 + + // This code has been taken from the open-bsd rm sourcecode: + // http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/bin/rm/rm.c?rev=1.27 + // It has then been refactored and specialized. + // + // The original code is published under the 3-Clause BSD license: + // + // Copyright (c) 1990, 1993, 1994 + // The Regents of the University of California. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions + // are met: + // 1. Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // 2. Redistributions in binary form must reproduce the above copyright + // notice, this list of conditions and the following disclaimer in the + // documentation and/or other materials provided with the distribution. + // 3. Neither the name of the University nor the names of its contributors + // may be used to endorse or promote products derived from this software + // without specific prior written permission. + + FTS *fts; + FTSENT *p; + int success = true; + + clean_path += '\0'; // Abuse internal string buffer to build a NULL-terminated C-string + char* argv[2]; + argv[0] = &clean_path[0]; + argv[1] = nullptr; + + fts = fts_open(argv, FTS_PHYSICAL, nullptr); + if (fts == nullptr) + return false; + + while ((p = fts_read(fts)) != NULL) + { + switch (p->fts_info) + { + case FTS_DNR: + success = false; + continue; + case FTS_ERR: + std::cerr << p->fts_path << ": " << strerror(p->fts_errno) << std::endl; + return false; + case FTS_NS: + /* + * FTS_NS: assume that if can't stat the file, it + * can't be unlinked. + */ + success = false; + continue; + case FTS_D: + continue; + case FTS_DP: + break; + default: + break; + } + + /* + * If we can't read or search the directory, may still be + * able to remove it. Don't print out the un{read,search}able + * message unless the remove fails. + */ + switch (p->fts_info) { + case FTS_DP: + case FTS_DNR: + if (!rmdir(p->fts_accpath)) + { + continue; + } + break; + + case FTS_F: + case FTS_NSOK: + default: + if (!unlink(p->fts_accpath)) + { + continue; + } + } + std::cerr << p->fts_path << std::endl; + success = false; + } + if (errno) + std::cerr << "fts_read" << std::endl; + fts_close(fts); + + return success; +#endif // WIN32 + } + + std::string GetAbsoluteRoot(const std::string& path, OsStyle input_path_style) + { + if ((input_path_style == OsStyle::Windows) || (input_path_style == OsStyle::Combined)) + { + std::regex win_drive_with_slash("^[a-zA-Z]\\:[/\\\\]"); // Drive with tailing separator and optional directory + std::regex win_drive_only("^[a-zA-Z]\\: *$"); // Drive only, after wich must not come anything + std::regex win_network_drive("^[/\\\\]{2}[^/\\\\]+"); // Network path starting with two slashes or backslashes + + if ((std::regex_search(path, win_drive_with_slash) + || std::regex_search(path, win_drive_only))) + { + // Windows local drive, consisting of drive-letter and colon + return path.substr(0, 2); + } + else if (std::regex_search(path, win_network_drive)) + { + // Window network drive, consisting of \\ and hostname + size_t sep_pos = path.find_first_of("/\\", 2); + return path.substr(0, sep_pos); // If no seperator was found, this will return the entire string + } + } + + if (input_path_style == OsStyle::Unix) + { + std::regex unix_slash("^/"); // Normal Unix root + if (std::regex_search(path, unix_slash)) + { + return path.substr(0, 1); + } + } + else if (input_path_style == OsStyle::Combined) + { + std::regex unix_slash("^[/\\\\]"); // Normal Unix root (with optional windows seperator indicating the root) + if (std::regex_search(path, unix_slash)) + { + return path.substr(0, 1); + } + } + + return ""; + } + + bool IsAbsolute(const std::string& path, OsStyle input_path_style) + { + return GetAbsoluteRoot(path, input_path_style) != ""; + } + + bool IsRelative(const std::string& path, OsStyle input_path_style) + { + if (path.empty()) + { + return false; + } + else + { + return !IsAbsolute(path, input_path_style); + } + } + + std::vector CleanPathComponentList(const std::string& path, OsStyle input_path_style) + { + if (path.empty()) + { + return{}; + } + + std::vector components; + + std::string unix_style_path = ToUnixSeperators(path, input_path_style); + + // Get information about the root + std::string root = GetAbsoluteRoot(path, input_path_style); + bool is_absolute = (root != ""); + + // Remove the root from the path (we will add it later) + unix_style_path = unix_style_path.substr(root.size()); + + // Split the relative path into its components + std::list splitted_path; + EcalUtils::String::Split(unix_style_path, "/", splitted_path); + + // The components-stack that will increase and shrink depending on the folders and .. elements in the splitted path + for (auto part_it = splitted_path.begin(); part_it != splitted_path.end(); part_it++) + { + if ((part_it == splitted_path.begin()) && (*part_it == ".") && !is_absolute) + { + // Preserve a leading ".", if the path is relative. It may indicate the working director, so we should not skip it. + components.push_back("."); + } + else if (part_it->empty() || *part_it == ".") + { + continue; + } + else if (*part_it == "..") + { + if (is_absolute) + { + if (!components.empty()) + { + // Move one folder up if we are not already at the root + components.pop_back(); + } + } + else + { + if (!components.empty() && (components.back() != "..")) + { + // Move one folder up by removing it. We must not remove ".." elements that we were not able to resolve previously. + components.pop_back(); + } + else + { + components.push_back(".."); + } + } + } + else + { + components.push_back(*part_it); + } + } + + return components; + } + + std::string CleanPath(const std::string& path, OsStyle input_path_style) + { + if (path.empty()) + return ""; + + // Split the path into its cleaned components + std::vector cleaned_path_components = CleanPathComponentList(path, input_path_style); + + // Check whether the path ended with a slash + bool tailing_separator = (ToUnixSeperators(path, input_path_style).back() == '/'); + + // Gather information about the root (it will not be in the components list) + std::string root = GetAbsoluteRoot(path, input_path_style); + root = ToUnixSeperators(root, input_path_style); + bool is_absolute = (root != ""); + + std::string cleaned_path; + cleaned_path.reserve(path.size() + root.size() + 2); + + if (is_absolute) + { + cleaned_path += root; + if (cleaned_path.back() != '/') + { + cleaned_path += '/'; + } + } + else + { + //cleaned_path += '.'; + //if (!cleaned_path_components.empty()) + //{ + // cleaned_path += '/'; + //} + } + + cleaned_path += EcalUtils::String::Join("/", cleaned_path_components); + + if (tailing_separator && (cleaned_path.back() != '/')) + { + cleaned_path += '/'; + } + + return cleaned_path; + } + + std::string AbsolutePath(const std::string& base_path, const std::string& relative_path, OsStyle input_path_style) + { + return CleanPath(CleanPath(base_path) + "/" + relative_path, input_path_style); + } + + std::string AbsolutePath(const std::string& relative_path, OsStyle input_path_style) + { + if (IsAbsolute(relative_path, input_path_style)) + { + return CleanPath(relative_path, input_path_style); + } + else + { + return CleanPath(CurrentWorkingDir() + "/" + relative_path); + } + } + + std::string RelativePath(const std::string& base_path, const std::string& path, OsStyle input_path_style) + { + auto base_list = CleanPathComponentList(base_path, input_path_style); + auto path_list = CleanPathComponentList(path, input_path_style); + + size_t size = (path_list.size() < base_list.size()) ? path_list.size() : base_list.size(); + unsigned int same_size(0); + for (unsigned int i = 0; i < size; ++i) + { + if (path_list[i] != base_list[i]) + { + same_size = i; + break; + } + } + + std::string relative_path = ""; + if (same_size > 0) + { + for (unsigned int i = 0; i < base_list.size() - same_size; ++i) + { + relative_path += "../"; + } + } + + for (unsigned int i = same_size; i < path_list.size(); ++i) + { + relative_path += path_list[i]; + if (i < path_list.size() - 1) + { + relative_path += "/"; + } + } + + return relative_path; + } + + std::string CurrentWorkingDir() + { +#ifdef _WIN32 + wchar_t working_dir[MAX_PATH]; + bool success (_wgetcwd(working_dir, MAX_PATH) != nullptr); + + if (success) + return StrConvert::WideToUtf8(working_dir); + else + return ""; +#else + char working_dir[PATH_MAX]; + return (getcwd(working_dir, PATH_MAX) ? working_dir : std::string("")); +#endif + } + + std::string ApplicationDir() + { +#ifdef _WIN32 + wchar_t w_app_path_buffer[MAX_PATH]; + std::wstring w_app_path { std::wstring(w_app_path_buffer, static_cast(GetModuleFileNameW(NULL, w_app_path_buffer, MAX_PATH))) }; + std::string app_path = StrConvert::WideToUtf8(w_app_path); +#else + char app_path_buffer[PATH_MAX]; + ssize_t count { readlink("/proc/self/exe", app_path_buffer, PATH_MAX) }; + if (count < 0) return {}; + auto app_path{ std::string(app_path_buffer, static_cast(count)) }; +#endif + return app_path.substr(0, app_path.find_last_of(separator)); + } + + std::string ChangeSeperators(const std::string& path, OsStyle output_path_style, OsStyle input_path_style) + { + std::string output; + output.reserve(path.size()); + + for (size_t i = 0; i < path.size(); i++) + { + if ((((input_path_style == OsStyle::Windows) || (input_path_style == OsStyle::Combined)) && (path[i] == '\\')) + || (path[i] == '/')) + { + output += NativeSeparator(output_path_style); + } + else + { + output += path[i]; + } + } + + return output; + } + + std::string ToUnixSeperators(const std::string& path, OsStyle input_path_style) + { + return ChangeSeperators(path, OsStyle::Unix, input_path_style); + } + + std::string ToNativeSeperators(const std::string& path, OsStyle input_path_style) + { + return ChangeSeperators(path, OsStyle::Current, input_path_style); + } + + bool IsEqual(const std::string& path1, const std::string& path2, OsStyle compare_for) + { + std::string clean_path1 = ToNativeSeperators(AbsolutePath(path1, compare_for), compare_for); + std::string clean_path2 = ToNativeSeperators(AbsolutePath(path2, compare_for), compare_for); + + // Compare the root + std::string path1_root = GetAbsoluteRoot(clean_path1, compare_for); + std::string path2_root = GetAbsoluteRoot(clean_path2, compare_for); + + if (compare_for == OsStyle::Windows) + { + // Windows is case-insensitive + // cause warning C4244 with VS2017, VS2019 + //std::transform(path1_root.begin(), path1_root.end(), path1_root.begin(), ::tolower); + std::transform(path1_root.begin(), path1_root.end(), path1_root.begin(), + [](char c) {return static_cast(::tolower(c)); }); + // cause warning C4244 with VS2017, VS2019 + //std::transform(path2_root.begin(), path2_root.end(), path2_root.begin(), ::tolower); + std::transform(path2_root.begin(), path2_root.end(), path2_root.begin(), + [](char c) {return static_cast(::tolower(c)); }); + } + + if (path1_root != path2_root) + { + return false; + } + + // Compare the rest + auto path1_components = CleanPathComponentList(clean_path1, compare_for); + auto path2_components = CleanPathComponentList(clean_path2, compare_for); + + if (path1_components.size() != path2_components.size()) + { + return false; + } + + auto path1_component_it = path1_components.begin(); + auto path2_component_it = path2_components.begin(); + + while (path1_component_it != path1_components.end()) + { + if (compare_for == OsStyle::Windows) + { + // Windows is case-insensitive + // cause warning C4244 with VS2017, VS2019 + //std::transform(path1_component_it->begin(), path1_component_it->end(), path1_component_it->begin(), ::tolower); + std::transform(path1_component_it->begin(), path1_component_it->end(), path1_component_it->begin(), + [](char c) {return static_cast(::tolower(c)); }); + // cause warning C4244 with VS2017, VS2019 + //std::transform(path2_component_it->begin(), path2_component_it->end(), path2_component_it->begin(), ::tolower); + std::transform(path2_component_it->begin(), path2_component_it->end(), path2_component_it->begin(), + [](char c) {return static_cast(::tolower(c)); }); + } + + if (*path1_component_it != *path2_component_it) + { + return false; + } + + ++path1_component_it; + ++path2_component_it; + } + + return true; + } + + std::string FileName(const std::string& path, OsStyle input_path_style) + { + if (path.empty()) + return ""; + + if (path.back() == '/') + return ""; + + if ((input_path_style == OsStyle::Windows) || (input_path_style == OsStyle::Combined)) + { + if (path.back() == '\\') + return ""; + } + + auto clean_path_component_list = CleanPathComponentList(path, input_path_style); + + if (clean_path_component_list.empty()) + return ""; + else + return clean_path_component_list.back(); + } + + std::string BaseName(const std::string& path, OsStyle input_path_style) + { + std::string file_name = FileName(path, input_path_style); + + if (file_name.empty()) return ""; + + size_t pos = file_name.find('.', 0); + if (pos != std::string::npos) + { + return file_name.substr(0, pos); + } + else + { + return file_name; + } + } + } +} diff --git a/src/utils/src/str_convert.cpp b/src/utils/src/str_convert.cpp new file mode 100644 index 0000000..14d8e5d --- /dev/null +++ b/src/utils/src/str_convert.cpp @@ -0,0 +1,78 @@ +/* ========================= eCAL LICENSE ================================= +* +* Copyright (C) 2021 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 + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include +#endif // WIN32 + +#include + +namespace EcalUtils +{ + namespace StrConvert + { +#ifdef WIN32 + std::string WideToAnsi(const std::wstring& wstr, unsigned int ansi_code_page) + { + int count = WideCharToMultiByte(ansi_code_page, 0, wstr.c_str(), static_cast(wstr.length()), NULL, 0, NULL, NULL); + std::string str(count, 0); + WideCharToMultiByte(ansi_code_page, 0, wstr.c_str(), -1, &str[0], count, NULL, NULL); + return str; + } + + std::wstring AnsiToWide(const std::string& str, unsigned int ansi_code_page) + { + int count = MultiByteToWideChar(ansi_code_page, 0, str.c_str(), static_cast(str.length()), NULL, 0); + std::wstring wstr(count, 0); + MultiByteToWideChar(ansi_code_page, 0, str.c_str(), static_cast(str.length()), &wstr[0], count); + return wstr; + } + + std::string WideToUtf8(const std::wstring& wstr) + { + int count = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), static_cast(wstr.length()), NULL, 0, NULL, NULL); + std::string str(count, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], count, NULL, NULL); + return str; + } + + std::wstring Utf8ToWide(const std::string& str) + { + int count = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast(str.length()), NULL, 0); + std::wstring wstr(count, 0); + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), static_cast(str.length()), &wstr[0], count); + return wstr; + } + + std::string AnsiToUtf8(const std::string& str, unsigned int ansi_code_page) + { + return WideToUtf8(AnsiToWide(str, ansi_code_page)); + } + + std::string Utf8ToAnsi(const std::string& str, unsigned int ansi_code_page) + { + return WideToAnsi(Utf8ToWide(str), ansi_code_page); + } +#endif + } +} \ No newline at end of file diff --git a/testing/ecal/clientserver_test/src/dummy.cpp b/testing/ecal/clientserver_test/src/dummy.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/testing/ecal/pubsub_inproc_test/src/pubsub_inproc_test.cpp b/testing/ecal/pubsub_inproc_test/src/pubsub_inproc_test.cpp deleted file mode 100644 index fd156d0..0000000 --- a/testing/ecal/pubsub_inproc_test/src/pubsub_inproc_test.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include -#include - -#include -#include - -#define CMN_REGISTRATION_REFRESH 1000 - -// callback function 1 -size_t sub_clock1(0); -void OnReceive1(const std::string& msg_) -{ - std::cout << "PubClock : " << msg_ << std::endl; - std::cout << "SubClock 1 : " << sub_clock1 << std::endl; - sub_clock1++; -} - -// callback function 2 -size_t sub_clock2(0); -void OnReceive2() -{ - std::cout << "SubClock 2 : " << sub_clock2 << std::endl; - sub_clock2++; -} - -// callback function 3 -size_t sub_clock3(0); -void OnReceive3() -{ - std::cout << "SubClock 3 : " << sub_clock3 << std::endl; - std::cout << std::endl; - sub_clock3++; -} - -TEST(INPROC, CLOCKS) -{ - // initialize eCAL API - EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "inproc_clock_test")); - - // publish / subscribe match in the same process - eCAL::Util::EnableLoopback(true); - - // create simple string publisher - eCAL::string::CPublisher pub("CLOCK"); - - // and force him to send on 3 layers in parallel - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - pub.SetLayerMode(eCAL::TLayer::tlayer_udp_mc, eCAL::TLayer::smode_on); - pub.SetLayerMode(eCAL::TLayer::tlayer_shm, eCAL::TLayer::smode_on); - pub.SetLayerMode(eCAL::TLayer::tlayer_inproc, eCAL::TLayer::smode_on); - - // create subscriber number 1 - eCAL::string::CSubscriber sub1("CLOCK"); - EXPECT_EQ(true, sub1.AddReceiveCallback(std::bind(OnReceive1, std::placeholders::_2))); - - // create subscriber number 2 - eCAL::string::CSubscriber sub2("CLOCK"); - EXPECT_EQ(true, sub2.AddReceiveCallback(std::bind(OnReceive2))); - - // create subscriber number 3 - eCAL::string::CSubscriber sub3("CLOCK"); - EXPECT_EQ(true, sub3.AddReceiveCallback(std::bind(OnReceive3))); - - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); - - std::cout << std::endl; - for (size_t count = 0; count <= 10; ++count) - { - // check all clocks - // they should always count with - // the publishing one - EXPECT_EQ(count, sub_clock1); - EXPECT_EQ(count, sub_clock2); - EXPECT_EQ(count, sub_clock3); - - // create a clock string - std::stringstream ss; ss << count; - // and fire on the three layers - pub.Send(ss.str()); - } - - // finalize eCAL API - EXPECT_EQ(0, eCAL::Finalize()); -} diff --git a/testing/ecal/pubsub_proto_test/src/protobuf/animal.proto b/testing/ecal/pubsub_proto_test/src/protobuf/animal.proto deleted file mode 100644 index 625a78d..0000000 --- a/testing/ecal/pubsub_proto_test/src/protobuf/animal.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Animal; - -message Dog -{ - string name = 1; - string colour = 2; -} diff --git a/testing/ecal/pubsub_proto_test/src/protobuf/house.proto b/testing/ecal/pubsub_proto_test/src/protobuf/house.proto deleted file mode 100644 index fbe8cce..0000000 --- a/testing/ecal/pubsub_proto_test/src/protobuf/house.proto +++ /dev/null @@ -1,27 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Environment; - -message House -{ - int32 rooms = 1; -} diff --git a/testing/ecal/pubsub_proto_test/src/protobuf/person.proto b/testing/ecal/pubsub_proto_test/src/protobuf/person.proto deleted file mode 100644 index 4200a43..0000000 --- a/testing/ecal/pubsub_proto_test/src/protobuf/person.proto +++ /dev/null @@ -1,42 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -import "animal.proto"; -import "house.proto"; - -package pb.People; - -message Person -{ - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - - Animal.Dog dog = 5; - Environment.House house = 6; -} diff --git a/testing/lib/ecal_protobuf/dynproto_test/CMakeLists.txt b/testing/lib/ecal_protobuf/dynproto_test/CMakeLists.txt deleted file mode 100644 index 418676f..0000000 --- a/testing/lib/ecal_protobuf/dynproto_test/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -project(test_dynproto) - -find_package(Threads REQUIRED) -find_package(GTest REQUIRED) -find_package(Protobuf REQUIRED) - -create_targets_protobuf() - -set(dynproto_test_src - src/dynproto_test.cpp -) - -set(test_dynproto_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/attitude.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) - -ecal_add_gtest(${PROJECT_NAME} ${dynproto_test_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${${PROJECT_NAME}_proto}) - -target_link_libraries(${PROJECT_NAME} - PRIVATE - Threads::Threads - eCAL::proto - protobuf::libprotobuf) - -ecal_install_gtest(${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/contrib/ecalproto) diff --git a/testing/lib/ecal_protobuf/dynproto_test/src/dynproto_test.cpp b/testing/lib/ecal_protobuf/dynproto_test/src/dynproto_test.cpp deleted file mode 100644 index 7b3e273..0000000 --- a/testing/lib/ecal_protobuf/dynproto_test/src/dynproto_test.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include - -#include - -#include "person.pb.h" - -// message processor -void ProcProtoMsg(const google::protobuf::Message& msg_, const std::string& prefix_ /* = "" */); - -// google test -TEST(IO, dynproto) -{ - // generate a class instance of Person - pb::People::Person person; - - // set person object content - person.set_id(42); - person.set_name("Max"); - person.set_stype(pb::People::Person_SType_MALE); - person.set_email("max@mail.net"); - person.set_attitude(pb::People::NICE); - person.mutable_dog()->set_name("Brandy"); - person.mutable_house()->set_rooms(4); - - /////////////////////////////////////////////// - // this is the message type string - /////////////////////////////////////////////// - std::string topic_type = person.GetTypeName(); - EXPECT_NE(0, static_cast(topic_type.size())); - - // kill namespace and proto: prefix - topic_type = topic_type.substr(topic_type.find_first_of(':')+1, topic_type.size()); - topic_type = topic_type.substr(topic_type.find_last_of('.')+1, topic_type.size()); - - //////////////////////////////////////////////////// - // this is the message description string - //////////////////////////////////////////////////// - std::string topic_desc = eCAL::protobuf::GetProtoMessageDescription(person); - EXPECT_NE(0, static_cast(topic_desc.size())); - - //////////////////////////////////////////////////// - // this is the message payload string - //////////////////////////////////////////////////// - std::string msg_s = person.SerializeAsString(); - EXPECT_NE(0, static_cast(msg_s.size())); - - // create a message decoder - eCAL::protobuf::CProtoDynDecoder decoder; - - // create google::protobuf::Message pointer - std::string error_s; - google::protobuf::FileDescriptorSet proto_desc; - proto_desc.ParseFromString(topic_desc); - std::shared_ptr msg_ptr(decoder.GetProtoMessageFromDescriptorSet(proto_desc, topic_type, error_s)); - if(msg_ptr == nullptr) - { - std::cout << error_s << std::endl; - } - EXPECT_NE(nullptr, msg_ptr); - - //std::FILE* f = std::fopen("d:\\temp.log", "w"); - //std::fwrite(topic_desc.c_str(), 1, topic_desc.size(), f); - //std::fclose(f); - - if (msg_ptr) - { - // fill message with received string - msg_ptr->ParseFromString(msg_s); - - // and dynamic decode it - ProcProtoMsg(*msg_ptr, ""); - } -} - -// message processor -void ProcValue(const std::string& group_, const std::string& name_, const double value_, size_t index_) -{ - std::string var_name; - if(!group_.empty()) var_name += group_ + "."; - var_name += name_; - if(index_ > 0) var_name += "[" + std::to_string(index_) + "]"; - std::cout << var_name << " : " << value_ << std::endl; -} - -void ProcString(const std::string& group_, const std::string& name_, const std::string& value_, size_t index_) -{ - std::string var_name; - if(!group_.empty()) var_name += group_ + "."; - var_name += name_; - if(index_ > 0) var_name += "[" + std::to_string(index_) + "]"; - std::cout << var_name << " : " << value_ << std::endl; -} - -void ProcProtoType(const std::string& group_, const std::string& name_, google::protobuf::int32 value_, size_t index_) -{ - ProcValue(group_, name_, double(value_), index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, google::protobuf::int64 value_, size_t index_) -{ - ProcValue(group_, name_, double(value_), index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, google::protobuf::uint32 value_, size_t index_) -{ - ProcValue(group_, name_, double(value_), index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, google::protobuf::uint64 value_, size_t index_) -{ - ProcValue(group_, name_, double(value_), index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, float value_, size_t index_) -{ - ProcValue(group_, name_, double(value_), index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, double value_, size_t index_) -{ - ProcValue(group_, name_, double(value_), index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, bool value_, size_t index_) -{ - ProcValue(group_, name_, double(value_), index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, const std::string& value_, size_t index_) -{ - ProcString(group_, name_, value_, index_); -} - -void ProcProtoType(const std::string& group_, const std::string& name_, const google::protobuf::EnumValueDescriptor* value_, size_t index_) -{ - ProcValue(group_, name_, double(value_->number()), index_); -} - -void ProcProtoMsg(const google::protobuf::Message& msg_, const std::string& prefix_ /* = "" */) -{ - const google::protobuf::Reflection* ref_ptr = msg_.GetReflection(); - if(ref_ptr) - { - std::vector field_v; - ref_ptr->ListFields(msg_, &field_v); - for(auto field : field_v) - { - const google::protobuf::FieldDescriptor::CppType fdt = field->cpp_type(); - switch(fdt) - { - case google::protobuf::FieldDescriptor::CPPTYPE_INT32: // TYPE_INT32, TYPE_SINT32, TYPE_SFIXED32 - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedInt32(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetInt32(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_INT64: // TYPE_INT64, TYPE_SINT64, TYPE_SFIXED64 - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedInt64(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetInt64(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: // TYPE_UINT32, TYPE_FIXED32 - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedUInt32(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetUInt32(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: // TYPE_UINT64, TYPE_FIXED64 - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedUInt64(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetUInt64(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: // TYPE_DOUBLE - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedDouble(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetDouble(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: // TYPE_FLOAT - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedFloat(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetFloat(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: // TYPE_BOOL - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedBool(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetBool(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TYPE_ENUM - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedEnum(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetEnum(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_STRING: // TYPE_STRING, TYPE_BYTES - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetRepeatedString(msg_, field, fnum), fnum); - } - } - else - { - ProcProtoType(prefix_, field->name(), ref_ptr->GetString(msg_, field), 0); - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: // TYPE_MESSAGE, TYPE_GROUP - { - if(field->is_repeated()) - { - int fsize = ref_ptr->FieldSize(msg_, field); - for(int fnum = 0; fnum < fsize; ++fnum) - { - const google::protobuf::Message& msg = ref_ptr->GetRepeatedMessage(msg_, field, fnum); - std::string prefix = field->name(); - prefix += "["; - prefix += std::to_string(fnum); - prefix += "]"; - if(!prefix_.empty()) prefix = prefix_ + "." + prefix; - ProcProtoMsg(msg, prefix); - } - } - else - { - const google::protobuf::Message& msg = ref_ptr->GetMessage(msg_, field); - std::string prefix = field->name(); - if(!prefix_.empty()) prefix = prefix_ + "." + prefix; - ProcProtoMsg(msg, prefix); - } - } - break; - default: - break; - } - } - } -} diff --git a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/animal.proto b/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/animal.proto deleted file mode 100644 index 625a78d..0000000 --- a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/animal.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Animal; - -message Dog -{ - string name = 1; - string colour = 2; -} diff --git a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/attitude.proto b/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/attitude.proto deleted file mode 100644 index a36e43e..0000000 --- a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/attitude.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.People; - -enum Attitude -{ - NICE = 0; - BAD = 1; -} diff --git a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/house.proto b/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/house.proto deleted file mode 100644 index fbe8cce..0000000 --- a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/house.proto +++ /dev/null @@ -1,27 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Environment; - -message House -{ - int32 rooms = 1; -} diff --git a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/person.proto b/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/person.proto deleted file mode 100644 index 2313e22..0000000 --- a/testing/lib/ecal_protobuf/dynproto_test/src/protobuf/person.proto +++ /dev/null @@ -1,44 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -import "animal.proto"; -import "house.proto"; -import "attitude.proto"; - -package pb.People; - -message Person -{ - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - Attitude attitude = 5; - - Animal.Dog dog = 6; - Environment.House house = 7; -} diff --git a/testing/lib/ecal_protobuf/ecal_proto_test/CMakeLists.txt b/testing/lib/ecal_protobuf/ecal_proto_test/CMakeLists.txt deleted file mode 100644 index 34adcbd..0000000 --- a/testing/lib/ecal_protobuf/ecal_proto_test/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -# ========================= eCAL LICENSE ================================= -# -# Copyright (C) 2016 - 2019 Continental Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# ========================= eCAL LICENSE ================================= - -project(test_ecal_proto) - -find_package(Threads REQUIRED) -find_package(GTest REQUIRED) -find_package(Protobuf REQUIRED) - -create_targets_protobuf() - -set(ecal_proto_test_src - src/test_filters.cpp -) - -set(${PROJECT_NAME}_proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/animal.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/house.proto - ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf/person.proto -) - -ecal_add_gtest(${PROJECT_NAME} ${ecal_proto_test_src}) -PROTOBUF_TARGET_CPP(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/protobuf ${${PROJECT_NAME}_proto}) - -target_link_libraries(${PROJECT_NAME} - PRIVATE - Threads::Threads - eCAL::proto - protobuf::libprotobuf) - -ecal_install_gtest(${PROJECT_NAME} ${PROJECT_NAME}) - -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/contrib/ecalproto) diff --git a/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/animal.proto b/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/animal.proto deleted file mode 100644 index 625a78d..0000000 --- a/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/animal.proto +++ /dev/null @@ -1,28 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Animal; - -message Dog -{ - string name = 1; - string colour = 2; -} diff --git a/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/house.proto b/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/house.proto deleted file mode 100644 index fbe8cce..0000000 --- a/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/house.proto +++ /dev/null @@ -1,27 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -package pb.Environment; - -message House -{ - int32 rooms = 1; -} diff --git a/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/person.proto b/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/person.proto deleted file mode 100644 index 4200a43..0000000 --- a/testing/lib/ecal_protobuf/ecal_proto_test/src/protobuf/person.proto +++ /dev/null @@ -1,42 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -syntax = "proto3"; - -import "animal.proto"; -import "house.proto"; - -package pb.People; - -message Person -{ - enum SType - { - MALE = 0; - FEMALE = 1; - } - - int32 id = 1; - string name = 2; - SType stype = 3; - string email = 4; - - Animal.Dog dog = 5; - Environment.House house = 6; -} diff --git a/testing/lib/ecal_protobuf/ecal_proto_test/src/test_filters.cpp b/testing/lib/ecal_protobuf/ecal_proto_test/src/test_filters.cpp deleted file mode 100644 index 61c2822..0000000 --- a/testing/lib/ecal_protobuf/ecal_proto_test/src/test_filters.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * Tests eCALHDF5 reader w/o input file (single channel) -**/ - -#include - -#include "ecal/protobuf/ecal_proto_message_filter.h" - -#include "gtest/gtest.h" - -using namespace eCAL::protobuf; - -TEST(filter, nofilter) -{ - NoFilter filter; - bool result = filter.Filter("abc"); - EXPECT_EQ(true, result); -} - -TEST(filter, include_filter_clear) -{ - SimpleIncludeFilter filter; - std::string a("a"); - filter.Insert(a); - EXPECT_EQ(true, filter.Filter(a)); - - filter.Clear(); - EXPECT_EQ(false, filter.Filter(a)); -} - -void test_regular_includes(BaseIncludeFilter& filter) -{ - std::string abc("a.b.c"); - std::string ab("a.b"); - std::string a("a"); - std::string ad("a.d"); - std::string ade("a.d.e"); - std::string adf("a.d.f"); - EXPECT_EQ(false, filter.Filter(abc)); - filter.Insert(abc); - EXPECT_EQ(true, filter.Filter(a)); - EXPECT_EQ(true, filter.Filter(ab)); - EXPECT_EQ(true, filter.Filter(abc)); - filter.Erase(abc); - EXPECT_EQ(false, filter.Filter(a)); - EXPECT_EQ(false, filter.Filter(ab)); - EXPECT_EQ(false, filter.Filter(abc)); - filter.Insert(ade); - filter.Insert(abc); - filter.Insert(adf); - filter.Erase(ade); - EXPECT_EQ(true, filter.Filter(a)); - EXPECT_EQ(true, filter.Filter(ab)); - EXPECT_EQ(true, filter.Filter(abc)); - EXPECT_EQ(true, filter.Filter(ad)); - EXPECT_EQ(false, filter.Filter(ade)); - EXPECT_EQ(true, filter.Filter(adf)); - filter.Erase(abc); - EXPECT_EQ(true, filter.Filter(a)); - EXPECT_EQ(false, filter.Filter(ab)); - EXPECT_EQ(false, filter.Filter(abc)); - EXPECT_EQ(true, filter.Filter(ad)); - EXPECT_EQ(false, filter.Filter(ade)); - EXPECT_EQ(true, filter.Filter(adf)); - filter.Erase(adf); - EXPECT_EQ(false, filter.Filter(a)); - EXPECT_EQ(false, filter.Filter(ab)); - EXPECT_EQ(false, filter.Filter(abc)); - EXPECT_EQ(false, filter.Filter(ad)); - EXPECT_EQ(false, filter.Filter(ade)); - EXPECT_EQ(false, filter.Filter(adf)); -} - -TEST(filter, include_filter) -{ - SimpleIncludeFilter simple_filter; - test_regular_includes(simple_filter); - ComplexIncludeFilter complex_filter; - test_regular_includes(complex_filter); -} - -TEST(filter, include_filter_array_simple) -{ - const std::string a("a"); - const std::string a1("a[1]"); - const std::string a2("a[2]"); - const std::string a1b("a[1].b"); - const std::string a2b("a[2].b"); - const std::string a1bc1("a[1].b.c[1]"); - - ComplexIncludeFilter filter; - filter.Insert(a1); // Add a[1] - EXPECT_EQ(true, filter.Filter(a)); // should accept a - EXPECT_EQ(true, filter.Filter(a1)); // should accept a[1] - EXPECT_EQ(false, filter.Filter(a2)); // should not accept a[2] - - // Test subsequently, all combinations to that point are included - filter.Clear(); - filter.Insert(a1bc1); - EXPECT_EQ(true, filter.Filter(a)); - EXPECT_EQ(true, filter.Filter(a1)); - EXPECT_EQ(true, filter.Filter(a1b)); - EXPECT_EQ(true, filter.Filter(a1bc1)); - - filter.Clear(); - filter.Insert(a1b); // add a[1].b - EXPECT_EQ(true, filter.Filter(a)); // should accept a - EXPECT_EQ(true, filter.Filter(a1)); // should accept a[1] - EXPECT_EQ(true, filter.Filter(a1b)); // should accept a[1].b - EXPECT_EQ(false, filter.Filter(a2)); // should not accept a[2] - EXPECT_EQ(false, filter.Filter(a2b)); // should not accept a[2].b -} - -TEST(filter, include_filter_array_complex) -{ - const std::string a ("a" ); - const std::string a1 ("a[1]" ); - const std::string a2 ("a[2]" ); - const std::string a_ ("a[*]" ); - const std::string a1b ("a[1].b" ); - const std::string a2b ("a[2].b" ); - const std::string a_b ("a[*].b" ); - const std::string a1bc_("a[1].b.c[*]"); - const std::string a1bc1("a[1].b.c[1]"); - - ComplexIncludeFilter filter; - filter.Insert(a1); // Add a[1] - EXPECT_EQ(true, filter.Filter(a)); // should accept a - EXPECT_EQ(true, filter.Filter(a1)); // should accept a[1] - EXPECT_EQ(false, filter.Filter(a2)); // should not accept a[2] - EXPECT_EQ(false, filter.Filter(a_)); // should not accept a[*] - - filter.Clear(); - filter.Insert(a_); // Add a[*] - EXPECT_EQ(true, filter.Filter(a)); // should accept a - EXPECT_EQ(true, filter.Filter(a1)); // should accept a[1] - EXPECT_EQ(true, filter.Filter(a2)); // should accept a[2] - EXPECT_EQ(true, filter.Filter(a_)); // should accept a[*] - - filter.Clear(); - filter.Insert(a_b); // Add a[*].b - EXPECT_EQ(true, filter.Filter(a1b)); // should accept a[1].b - EXPECT_EQ(true, filter.Filter(a_b)); // should accept a[*].b - - // Test subsequently, all combinations to that point are included - filter.Clear(); - filter.Insert(a1bc1); - EXPECT_EQ(true, filter.Filter(a)); - EXPECT_EQ(true, filter.Filter(a1)); - EXPECT_EQ(true, filter.Filter(a1b)); - EXPECT_EQ(true, filter.Filter(a1bc1)); - - filter.Clear(); - filter.Insert(a1bc_); - EXPECT_EQ(true, filter.Filter(a)); - EXPECT_EQ(true, filter.Filter(a1)); - EXPECT_EQ(true, filter.Filter(a1b)); - EXPECT_EQ(true, filter.Filter(a1bc1)); - - filter.Clear(); - filter.Insert(a_b); // add a[*].b - EXPECT_EQ(true, filter.Filter(a1)); // should accept a[1] - EXPECT_EQ(true, filter.Filter(a2)); // should accept a[2] - EXPECT_EQ(true, filter.Filter(a_)); // should accept a[*] - - filter.Clear(); - filter.Insert(a1b); // add a[1].b - EXPECT_EQ(true, filter.Filter(a)); // should accept a - EXPECT_EQ(true, filter.Filter(a1)); // should accept a[1] - EXPECT_EQ(true, filter.Filter(a1b)); // should accept a[1].b - EXPECT_EQ(false, filter.Filter(a2)); // should not accept a[2] - EXPECT_EQ(false, filter.Filter(a2b)); // should not accept a[2].b - EXPECT_EQ(false, filter.Filter(a_)); // should not accept a[*] - EXPECT_EQ(false, filter.Filter(a_b)); // should not accept a[*].b -} \ No newline at end of file diff --git a/testing/lib/ecal_protobuf/ecal_proto_test/src/test_maximum_array_dimensions.cpp b/testing/lib/ecal_protobuf/ecal_proto_test/src/test_maximum_array_dimensions.cpp deleted file mode 100644 index e13b6b6..0000000 --- a/testing/lib/ecal_protobuf/ecal_proto_test/src/test_maximum_array_dimensions.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * Tests eCALHDF5 reader w/o input file (single channel) -**/ - -#include -#include - -#include - -#include -#include - -#include "person.pb.h" - -using namespace eCAL::protobuf; - -TEST(MaximumArrayDimensions, Standard) -{ - pb::People::Person person; - - // set person object content - person.set_id(42); - person.set_name("Max"); - person.set_stype(pb::People::Person_SType_MALE); - person.set_email("max@mail.net"); - person.mutable_dog()->set_name("Brandy"); - person.mutable_house()->set_rooms(4); - - // We want to test, if the - CProtoDecoder decoder; - auto visitor = std::make_shared(); - decoder.SetVisitor(visitor); - - // Add a different number of favorite numbers and then Test what the maximum was. - ASSERT_EQ(0, visitor->MaxSize("favorite_numbers")); - - person.mutable_favorite_numbers()->Resize(1, 0); - decoder.ProcProtoMsg(person); - ASSERT_EQ(1, visitor->MaxSize("favorite_numbers")); - - person.mutable_favorite_numbers()->Resize(7, 0); - decoder.ProcProtoMsg(person); - ASSERT_EQ(7, visitor->MaxSize("favorite_numbers")); - - person.mutable_favorite_numbers()->Resize(5, 0); - decoder.ProcProtoMsg(person); - ASSERT_EQ(7, visitor->MaxSize("favorite_numbers")); -} - diff --git a/testing/lib/ecal_protobuf/ecal_proto_test/src/test_proto_dyn_decoder.cpp b/testing/lib/ecal_protobuf/ecal_proto_test/src/test_proto_dyn_decoder.cpp deleted file mode 100644 index b4c11ab..0000000 --- a/testing/lib/ecal_protobuf/ecal_proto_test/src/test_proto_dyn_decoder.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include -#include -#include - -#include - -#include - -#include "person.pb.h" - -using namespace eCAL::protobuf; - -std::vector elems; -std::vector values; - -void clear_output() -{ - elems.clear(); - values.clear(); -} - -class TestVisitor : public eCAL::protobuf::MessageVisitorDoubleIntegral -{ -public: - TestVisitor() :filter(std::make_shared()) {}; - std::shared_ptr filter; - -protected: - using eCAL::protobuf::MessageVisitorDoubleIntegral::ScalarValueIntegral; - virtual void ScalarValueIntegral(const std::string& field_, const std::string& /*group_*/, double /*value_*/) override - { - elems.push_back(field_); - }; - virtual void ScalarValueEnum(const std::string& field_, const std::string& group_, int value_) override - { - ScalarValueIntegral(field_, group_, static_cast(value_)); - }; - virtual void ArrayValueIntegral(size_t index_, double /*value_*/) override - { - values.push_back(index_); - }; - - virtual bool AcceptMessage(const std::string& message) override - { - return filter->Filter(message); - }; -}; - -void add_favorite_numbers(pb::People::Person& person) -{ - person.mutable_favorite_numbers()->Resize(5, 0); - for (int num = 0; num < 5; num++) - { - person.mutable_favorite_numbers()->Set(num, num); - } -} - - -TEST(protodecoder, no_filter) -{ - // generate a class instance of Person - pb::People::Person person; - - // set person object content - person.set_id(42); - person.set_name("Max"); - person.set_stype(pb::People::Person_SType_MALE); - person.set_email("max@mail.net"); - person.mutable_dog()->set_name("Brandy"); - person.mutable_house()->set_rooms(4); - - CProtoDecoder decoder; - auto visitor = std::make_shared(); - decoder.SetVisitor(visitor); - - clear_output(); - decoder.ProcProtoMsg(person); - std::vector expected_all{ "id", "stype", "rooms" }; - EXPECT_EQ(expected_all, elems); - - clear_output(); - auto filter = std::make_shared(); - filter->Insert("id"); - visitor->filter= filter; - - decoder.ProcProtoMsg(person); - std::vector expected_id{ "id" }; - EXPECT_EQ(expected_id, elems); - - clear_output(); - filter->Insert("house.rooms"); - decoder.ProcProtoMsg(person); - std::vector expected_id_rooms{ "id", "rooms" }; - EXPECT_EQ(expected_id_rooms, elems); - - // Now lets add some array members, and see how it performs with the filter. - add_favorite_numbers(person); - - // Test case, assert the array callback was called only once - clear_output(); - filter->Clear(); - filter->Insert("favorite_numbers[1]"); - decoder.ProcProtoMsg(person); - std::vector expected_values_numbers{ 1 }; - EXPECT_EQ(expected_values_numbers, values); - - // Test case, assert the array callback was called for each element - clear_output(); - filter->Clear(); - filter->Insert("favorite_numbers[*]"); - decoder.ProcProtoMsg(person); - std::vector expected_values_all_numbers{ 0, 1, 2, 3, 4 }; - EXPECT_EQ(expected_values_all_numbers, values); - -} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..db81156 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,45 @@ +# ========================= 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.13) + +add_subdirectory(expmap_test) +add_subdirectory(serialization_test) +add_subdirectory(topic2mcast_test) +add_subdirectory(util_test) + +if(ECAL_CORE_REGISTRATION_SHM OR ECAL_CORE_TRANSPORT_SHM) + add_subdirectory(io_memfile_test) +endif() + +if(ECAL_CORE_PUBLISHER AND ECAL_CORE_SUBSCRIBER) + add_subdirectory(core_test) + if(ECAL_CORE_TRANSPORT_SHM OR ECAL_CORE_TRANSPORT_UDP) # this test is running for shm and udp layer only, needs to be fixed for tcp + add_subdirectory(pubsub_test) + endif() + if(ECAL_CORE_BUILD_TESTS_PROTOBUF) + add_subdirectory(pubsub_proto_test) + endif() +endif() + +if(ECAL_CORE_SERVICE) + add_subdirectory(clientserver_test) + if(ECAL_CORE_BUILD_TESTS_PROTOBUF) + add_subdirectory(clientserver_proto_test) + endif() +endif() diff --git a/testing/ecal/clientserver_test/CMakeLists.txt b/tests/clientserver_proto_test/CMakeLists.txt similarity index 90% rename from testing/ecal/clientserver_test/CMakeLists.txt rename to tests/clientserver_proto_test/CMakeLists.txt index 2607bd7..8b9ff3e 100644 --- a/testing/ecal/clientserver_test/CMakeLists.txt +++ b/tests/clientserver_proto_test/CMakeLists.txt @@ -16,7 +16,7 @@ # # ========================= eCAL LICENSE ================================= -project(test_clientserver) +project(test_clientserver_proto) find_package(Threads REQUIRED) find_package(GTest REQUIRED) @@ -25,7 +25,7 @@ find_package(Protobuf REQUIRED) create_targets_protobuf() set(${PROJECT_NAME}_src - src/clientserver_test.cpp + src/clientserver_test_proto.cpp ) set(clientserver_test_proto @@ -46,4 +46,8 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_gtest(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal/service) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${${PROJECT_NAME}_src} +) \ No newline at end of file diff --git a/tests/clientserver_proto_test/src/clientserver_test_proto.cpp b/tests/clientserver_proto_test/src/clientserver_test_proto.cpp new file mode 100644 index 0000000..4ccf4e6 --- /dev/null +++ b/tests/clientserver_proto_test/src/clientserver_test_proto.cpp @@ -0,0 +1,206 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include +#include + +#include +#include + +#include + +#include "math.pb.h" +#include "ping.pb.h" + +#define ClientServerProtoCallbackTest 1 +#define ClientServerProtoBlockingTest 1 + +#if ClientServerProtoCallbackTest + +/////////////////////////////////////////////// +// Math Service +/////////////////////////////////////////////// +class MathServiceImpl : public MathService +{ +public: + virtual void Add(::google::protobuf::RpcController* /* controller_ */, const ::SFloatTuple* request_, ::SFloat* response_, ::google::protobuf::Closure* /* done_ */) + { + // print request and + std::cout << "Received request MathService / Add : " << request_->inp1() << " and " << request_->inp2() << std::endl << std::endl; + // create response + response_->set_out(request_->inp1() + request_->inp2()); + } + + virtual void Multiply(::google::protobuf::RpcController* /* controller_ */, const ::SFloatTuple* request_, ::SFloat* response_, ::google::protobuf::Closure* /* done_ */) + { + // print request and + std::cout << "Received request MathService / Multiply : " << request_->inp1() << " and " << request_->inp2() << std::endl << std::endl; + // create response + response_->set_out(request_->inp1() * request_->inp2()); + } + + virtual void Divide(::google::protobuf::RpcController* /* controller_ */, const ::SFloatTuple* request_, ::SFloat* response_, ::google::protobuf::Closure* /* done_ */) + { + // print request and + std::cout << "Received request MathService / Divide : " << request_->inp1() << " and " << request_->inp2() << std::endl << std::endl; + // create response + if (std::fabs(request_->inp2()) > DBL_EPSILON) response_->set_out(request_->inp1() / request_->inp2()); + else response_->set_out(0.0); + } +}; + +TEST(ClientServerProto, ClientServerProtoCallback) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "clientserver proto callback test"); + + // create MathService server + std::shared_ptr math_service_impl = std::make_shared(); + eCAL::protobuf::CServiceServer math_server(math_service_impl); + + // create MathService client + eCAL::protobuf::CServiceClient math_client; + + // response callback function + double math_response(0.0); + auto response_callback = [&](const struct eCAL::SServiceResponse& service_response_) + { + math_response = 0.0; + switch (service_response_.call_state) + { + // service successful executed + case call_state_executed: + { + SFloat response; + response.ParseFromString(service_response_.response); + std::cout << "Received response MathService / " << service_response_.method_name << " : " << response.out() << " from host " << service_response_.host_name << std::endl; + math_response = response.out(); + } + break; + // service execution failed + case call_state_failed: + std::cout << "Received error MathService / " << service_response_.method_name << " : " << service_response_.error_msg << " from host " << service_response_.host_name << std::endl; + break; + default: + break; + } + }; + + // add callback for server response + math_client.AddResponseCallback(response_callback); + + // let's match them -> wait REGISTRATION_REFRESH_CYCLE (ecal_def.h) + eCAL::Process::SleepMS(2000); + + // test math service + SFloatTuple math_request; + double inp1 = 20.0; + double inp2 = 22.0; + math_request.set_inp1(inp1); + math_request.set_inp2(inp2); + std::string math_request_s = math_request.SerializeAsString(); + + math_response = 0.0; + math_client.Call("Add", math_request_s); + std::cout << std::endl << "Add method called with : " << math_request.inp1() << " and " << math_request.inp1() << std::endl; + EXPECT_EQ(inp1 + inp2, static_cast(math_response)); + + math_response = 0.0; + math_client.Call("Multiply", math_request_s); + std::cout << std::endl << "Multiply method called with : " << math_request.inp1() << " and " << math_request.inp1() << std::endl; + EXPECT_EQ(inp1 * inp2, static_cast(math_response)); + + math_response = 0.0; + math_client.Call("Divide", math_request_s); + std::cout << std::endl << "Divide method called with : " << math_request.inp1() << " and " << math_request.inp1() << std::endl; + EXPECT_EQ(inp1 / inp2, static_cast(math_response)); + + // finalize eCAL API + eCAL::Finalize(); +} + +#endif /* ClientServerProtoCallbackTest */ + +#if ClientServerProtoBlockingTest + +/////////////////////////////////////////////// +// Ping Service +/////////////////////////////////////////////// +class PingServiceImpl : public PingService +{ +public: + void Ping(::google::protobuf::RpcController* /* controller_ */, const ::PingRequest* request_, ::PingResponse* response_, ::google::protobuf::Closure* /* done_ */) + { + // print request and + std::cout << "Received request PingService / Ping : " << request_->message() << std::endl << std::endl; + // create response + response_->set_answer("PONG"); + } +}; + +TEST(ClientServerProto, ClientServerProtoBlocking) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "clientserver proto blocking test"); + + // create PingService server + std::shared_ptr ping_service_impl = std::make_shared(); + eCAL::protobuf::CServiceServer ping_server(ping_service_impl); + + // create PingService client + eCAL::protobuf::CServiceClient ping_client; + + // let's match them -> wait REGISTRATION_REFRESH_CYCLE (ecal_def.h) + eCAL::Process::SleepMS(2000); + + // test ping service + eCAL::ServiceResponseVecT service_response_vec; + PingRequest ping_request; + ping_request.set_message("PING"); + ping_client.Call("Ping", ping_request, -1, &service_response_vec); + std::cout << std::endl << "Ping method called with message : " << ping_request.message() << std::endl; + for (auto service_response : service_response_vec) + { + EXPECT_EQ(call_state_executed, service_response.call_state); + switch (service_response.call_state) + { + // service successful executed + case call_state_executed: + { + PingResponse response; + response.ParseFromString(service_response.response); + std::cout << "Received response PingService / Ping : " << response.answer() << " from host " << service_response.host_name << std::endl; + EXPECT_STREQ(response.answer().c_str(), "PONG"); + } + break; + // service execution failed + case call_state_failed: + std::cout << "Received error PingService / Ping : " << service_response.error_msg << " from host " << service_response.host_name << std::endl; + break; + default: + break; + } + } + + // finalize eCAL API + eCAL::Finalize(); +} + +#endif /* ClientServerProtoBlockingTest */ diff --git a/testing/ecal/clientserver_test/src/protobuf/math.proto b/tests/clientserver_proto_test/src/protobuf/math.proto similarity index 100% rename from testing/ecal/clientserver_test/src/protobuf/math.proto rename to tests/clientserver_proto_test/src/protobuf/math.proto diff --git a/testing/ecal/clientserver_test/src/protobuf/ping.proto b/tests/clientserver_proto_test/src/protobuf/ping.proto similarity index 100% rename from testing/ecal/clientserver_test/src/protobuf/ping.proto rename to tests/clientserver_proto_test/src/protobuf/ping.proto diff --git a/testing/ecal/event_test/CMakeLists.txt b/tests/clientserver_test/CMakeLists.txt similarity index 79% rename from testing/ecal/event_test/CMakeLists.txt rename to tests/clientserver_test/CMakeLists.txt index 101b9c3..401872a 100644 --- a/testing/ecal/event_test/CMakeLists.txt +++ b/tests/clientserver_test/CMakeLists.txt @@ -16,16 +16,19 @@ # # ========================= eCAL LICENSE ================================= -project(test_event) +project(test_clientserver) find_package(Threads REQUIRED) find_package(GTest REQUIRED) -set(event_test_src - src/event_test.cpp +create_targets_protobuf() + +set(${PROJECT_NAME}_src + src/atomic_signalable.h + src/clientserver_test.cpp ) -ecal_add_gtest(${PROJECT_NAME} ${event_test_src}) +ecal_add_gtest(${PROJECT_NAME} ${${PROJECT_NAME}_src}) target_link_libraries(${PROJECT_NAME} PRIVATE @@ -36,4 +39,8 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_gtest(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal/io) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${${PROJECT_NAME}_src} +) \ No newline at end of file diff --git a/tests/clientserver_test/src/atomic_signalable.h b/tests/clientserver_test/src/atomic_signalable.h new file mode 100644 index 0000000..6c0d72f --- /dev/null +++ b/tests/clientserver_test/src/atomic_signalable.h @@ -0,0 +1,202 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2023 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 + +template +class atomic_signalable +{ +public: + atomic_signalable(T initial_value) : value(initial_value) {} + + atomic_signalable& operator=(const T new_value) + { + std::lock_guard lock(mutex); + value = new_value; + cv.notify_all(); + return *this; + } + + T operator++() + { + std::lock_guard lock(mutex); + T newValue = ++value; + cv.notify_all(); + return newValue; + } + + T operator++(T) + { + std::lock_guard lock(mutex); + T oldValue = value++; + cv.notify_all(); + return oldValue; + } + + T operator--() + { + std::lock_guard lock(mutex); + T newValue = --value; + cv.notify_all(); + return newValue; + } + + T operator--(T) + { + std::lock_guard lock(mutex); + T oldValue = value--; + cv.notify_all(); + return oldValue; + } + + T operator+=(const T& other) + { + std::lock_guard lock(mutex); + value += other; + cv.notify_all(); + return value; + } + + T operator-=(const T& other) + { + std::lock_guard lock(mutex); + value -= other; + cv.notify_all(); + return value; + } + + T operator*=(const T& other) + { + std::lock_guard lock(mutex); + value *= other; + cv.notify_all(); + return value; + } + + T operator/=(const T& other) + { + std::lock_guard lock(mutex); + value /= other; + cv.notify_all(); + return value; + } + + T operator%=(const T& other) + { + std::lock_guard lock(mutex); + value %= other; + cv.notify_all(); + return value; + } + + template + bool wait_for(Predicate predicate, std::chrono::milliseconds timeout) + { + std::unique_lock lock(mutex); + return cv.wait_for(lock, timeout, [&]() { return predicate(value); }); + } + + bool operator==(T other) const + { + std::lock_guard lock(mutex); + return value == other; + } + + bool operator==(const atomic_signalable& other) const + { + std::lock_guard lock_this(mutex); + std::lock_guard lock_other(other.mutex); + return value == other.value; + } + + bool operator!=(T other) const + { + std::lock_guard lock(mutex); + return value != other; + } + + bool operator<(T other) const + { + std::lock_guard lock(mutex); + return value < other; + } + + bool operator<=(T other) const + { + std::lock_guard lock(mutex); + return value <= other; + } + + bool operator>(T other) const + { + std::lock_guard lock(mutex); + return value > other; + } + + bool operator>=(T other) const + { + std::lock_guard lock(mutex); + return value >= other; + } + +private: + T value; + std::condition_variable cv; + mutable std::mutex mutex; +}; + + +template +bool operator==(const T& other, const atomic_signalable& atomic) +{ + return atomic == other; +} + +template +bool operator!=(const T& other, const atomic_signalable& atomic) +{ + return atomic != other; +} + +template +bool operator<(const T& other, const atomic_signalable& atomic) +{ + return atomic > other; +} + +template +bool operator<=(const T& other, const atomic_signalable& atomic) +{ + return atomic >= other; +} + +template +bool operator>(const T& other, const atomic_signalable& atomic) +{ + return atomic < other; +} + +template +bool operator>=(const T& other, const atomic_signalable& atomic) +{ + return atomic <= other; +} diff --git a/testing/ecal/clientserver_test/src/clientserver_test.cpp b/tests/clientserver_test/src/clientserver_test.cpp similarity index 64% rename from testing/ecal/clientserver_test/src/clientserver_test.cpp rename to tests/clientserver_test/src/clientserver_test.cpp index 1f650fc..7a3566c 100644 --- a/testing/ecal/clientserver_test/src/clientserver_test.cpp +++ b/tests/clientserver_test/src/clientserver_test.cpp @@ -18,16 +18,13 @@ */ #include -#include -#include #include #include #include -#include "math.pb.h" -#include "ping.pb.h" +#include "atomic_signalable.h" #define ClientConnectEventTest 1 #define ServerConnectEventTest 1 @@ -36,13 +33,10 @@ #define ClientServerBaseCallbackTimeoutTest 1 #define ClientServerBaseAsyncCallbackTest 1 -#define ClientServerBaseAsyncCallbackTimeoutTest 1 +#define ClientServerBaseAsyncTest 1 #define ClientServerBaseBlockingTest 1 -#define ClientServerProtoCallbackTest 1 -#define ClientServerProtoBlockingTest 1 - #define NestedRPCCallTest 1 namespace @@ -85,12 +79,12 @@ namespace std::cout << "Execution error msg : " << service_response_.error_msg << std::endl; std::cout << "Response : " << service_response_.response << std::endl << std::endl; std::cout << std::endl; - }; + } } #if ClientConnectEventTest -TEST(IO, ClientConnectEvent) +TEST(ClientServer, ClientConnectEvent) { // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base connect event callback"); @@ -99,24 +93,25 @@ TEST(IO, ClientConnectEvent) eCAL::CServiceClient client("service"); // add client event callback for connect event - int event_connected_fired(0); - int event_disconnected_fired(0); + atomic_signalable event_connected_fired (0); + atomic_signalable event_disconnected_fired(0); + auto event_callback = [&](const struct eCAL::SClientEventCallbackData* data_) -> void - { - switch (data_->type) - { - case client_event_connected: - std::cout << "event connected fired" << std::endl; - event_connected_fired++; - break; - case client_event_disconnected: - std::cout << "event disconnected fired" << std::endl; - event_disconnected_fired++; - break; - default: - break; - } - }; + { + switch (data_->type) + { + case client_event_connected: + std::cout << "event connected fired" << std::endl; + event_connected_fired++; + break; + case client_event_disconnected: + std::cout << "event disconnected fired" << std::endl; + event_disconnected_fired++; + break; + default: + break; + } + }; // attach event client.AddEventCallback(client_event_connected, std::bind(event_callback, std::placeholders::_2)); client.AddEventCallback(client_event_disconnected, std::bind(event_callback, std::placeholders::_2)); @@ -130,23 +125,19 @@ TEST(IO, ClientConnectEvent) { eCAL::CServiceServer server1("service"); - eCAL::Process::SleepMS(2000); + event_connected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::seconds(5)); EXPECT_EQ(1, event_connected_fired); EXPECT_EQ(0, event_disconnected_fired); eCAL::CServiceServer server2("service"); - eCAL::Process::SleepMS(2000); + event_connected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::seconds(5)); EXPECT_EQ(2, event_connected_fired); EXPECT_EQ(0, event_disconnected_fired); } - // do a dummy call - // after that disconnect events should fire for now - // this needs to be improved, clients needs to be - // informed about disconnection without calling a service method - client.Call("foo", ""); - eCAL::Process::SleepMS(1000); + event_disconnected_fired.wait_for([](int v) { return v >= 2; }, std::chrono::seconds(5)); + EXPECT_EQ(2, event_connected_fired); EXPECT_EQ(2, event_disconnected_fired); // finalize eCAL API @@ -157,7 +148,7 @@ TEST(IO, ClientConnectEvent) #if ServerConnectEventTest -TEST(IO, ServerConnectEvent) +TEST(ClientServer, ServerConnectEvent) { // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base connect event callback"); @@ -166,8 +157,8 @@ TEST(IO, ServerConnectEvent) eCAL::CServiceServer server("service"); // add server event callback for connect event - int event_connected_fired(0); - int event_disconnected_fired(0); + atomic_signalable event_connected_fired (0); + atomic_signalable event_disconnected_fired(0); auto event_callback = [&](const struct eCAL::SServerEventCallbackData* data_) -> void { switch (data_->type) @@ -193,11 +184,11 @@ TEST(IO, ServerConnectEvent) EXPECT_EQ(0, event_connected_fired); EXPECT_EQ(0, event_disconnected_fired); - // create server + // create clients { eCAL::CServiceClient client1("service"); - eCAL::Process::SleepMS(2000); + event_connected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::seconds(5)); EXPECT_EQ(1, event_connected_fired); EXPECT_EQ(0, event_disconnected_fired); @@ -207,7 +198,7 @@ TEST(IO, ServerConnectEvent) EXPECT_EQ(1, event_connected_fired); EXPECT_EQ(0, event_disconnected_fired); } - eCAL::Process::SleepMS(2000); + event_disconnected_fired.wait_for([](int v) { return v >= 1; }, std::chrono::seconds(5)); EXPECT_EQ(1, event_connected_fired); EXPECT_EQ(1, event_disconnected_fired); @@ -219,7 +210,7 @@ TEST(IO, ServerConnectEvent) #if ClientServerBaseCallbackTest -TEST(IO, ClientServerBaseCallback) +TEST(ClientServer, ClientServerBaseCallback) { const int num_services(2); const int num_clients(3); @@ -237,8 +228,8 @@ TEST(IO, ClientServerBaseCallback) } // method callback function - int methods_executed(0); - int method_process_time(0); + std::atomic methods_executed(0); + std::atomic method_process_time(0); auto method_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int { eCAL::Process::SleepMS(method_process_time); @@ -263,7 +254,7 @@ TEST(IO, ClientServerBaseCallback) } // response callback function - int responses_executed(0); + std::atomic responses_executed(0); auto response_callback = [&](const struct eCAL::SServiceResponse& service_response_) { PrintResponse(service_response_); @@ -280,7 +271,7 @@ TEST(IO, ClientServerBaseCallback) eCAL::Process::SleepMS(2000); // call service - int methods_called(0); + std::atomic methods_called(0); bool success(true); // some calls with no sleep in the method callback @@ -301,7 +292,7 @@ TEST(IO, ClientServerBaseCallback) } } - // some calls with method_process_time sleep in the method callback + // some calls with service_callback_time_ms sleep in the method callback method_process_time = 100; for (auto i = 0; i < calls; ++i) { @@ -333,7 +324,7 @@ TEST(IO, ClientServerBaseCallback) #if ClientServerBaseCallbackTimeoutTest -TEST(IO, ClientServerBaseCallbackTimeout) +TEST(ClientServer, ClientServerBaseCallbackTimeout) { const int num_services(2); const int num_clients(3); @@ -351,16 +342,16 @@ TEST(IO, ClientServerBaseCallbackTimeout) } // method callback function - int methods_executed(0); - int method_process_time(0); + std::atomic methods_executed(0); + std::atomic method_process_time(0); auto method_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int - { - eCAL::Process::SleepMS(method_process_time); - PrintRequest(method_, req_type_, resp_type_, request_); - response_ = "I answer on " + request_; - methods_executed++; - return 42; - }; + { + eCAL::Process::SleepMS(method_process_time); + PrintRequest(method_, req_type_, resp_type_, request_); + response_ = "I answer on " + request_; + methods_executed++; + return 42; + }; // add method callbacks for (auto service : service_vec) @@ -377,12 +368,12 @@ TEST(IO, ClientServerBaseCallbackTimeout) } // response callback function - int responses_executed(0); + std::atomic responses_executed(0); auto response_callback = [&](const struct eCAL::SServiceResponse& service_response_) - { - PrintResponse(service_response_); - responses_executed++; - }; + { + PrintResponse(service_response_); + responses_executed++; + }; // add callback for server response for (auto client : client_vec) @@ -391,11 +382,11 @@ TEST(IO, ClientServerBaseCallbackTimeout) } // add event callback for timeout event - int timeout_fired = 0; + std::atomic timeout_fired(0); auto event_callback = [&](const struct eCAL::SClientEventCallbackData* /*data_*/) -> void - { - timeout_fired++; - }; + { + timeout_fired++; + }; for (auto client : client_vec) { // catch events @@ -406,10 +397,10 @@ TEST(IO, ClientServerBaseCallbackTimeout) eCAL::Process::SleepMS(2000); // call service - int methods_called(0); + std::atomic methods_called(0); bool success(true); - // some calls with method_process_time sleep in the method callback + // some calls with service_callback_time_ms sleep in the method callback method_process_time = 100; for (auto i = 0; i < calls; ++i) { @@ -440,20 +431,20 @@ TEST(IO, ClientServerBaseCallbackTimeout) responses_executed = 0; timeout_fired = 0; - // some calls with method_process_time sleep in the method callback and a proper timeout parameter - method_process_time = 100; + // some calls with service_callback_time_ms sleep in the method callback and a proper timeout parameter + method_process_time = 50; for (auto i = 0; i < calls; ++i) { // call methods for (auto client : client_vec) { // call method 1 - success &= client->Call("foo::method1", "my request for method 1", method_process_time * 2); + success &= client->Call("foo::method1", "my request for method 1", method_process_time * 4); eCAL::Process::SleepMS(sleep); methods_called++; // call method 2 - success &= client->Call("foo::method2", "my request for method 2", method_process_time * 2); + success &= client->Call("foo::method2", "my request for method 2", method_process_time * 4); eCAL::Process::SleepMS(sleep); methods_called++; } @@ -471,8 +462,8 @@ TEST(IO, ClientServerBaseCallbackTimeout) responses_executed = 0; timeout_fired = 0; - // some calls with method_process_time sleep in the method callback and to small timeout parameter - method_process_time = 100; + // some calls with service_callback_time_ms sleep in the method callback and to small timeout parameter + method_process_time = 50; for (auto i = 0; i < calls; ++i) { // call methods @@ -480,12 +471,12 @@ TEST(IO, ClientServerBaseCallbackTimeout) { // call method 1 success &= client->Call("foo::method1", "my request for method 1", method_process_time / 10); - eCAL::Process::SleepMS(method_process_time * 2); + eCAL::Process::SleepMS(method_process_time * 4); methods_called++; // call method 2 success &= client->Call("foo::method2", "my request for method 2", method_process_time / 10); - eCAL::Process::SleepMS(method_process_time * 2); + eCAL::Process::SleepMS(method_process_time * 4); methods_called++; } } @@ -503,7 +494,7 @@ TEST(IO, ClientServerBaseCallbackTimeout) #if ClientServerBaseAsyncCallbackTest -TEST(IO, ClientServerBaseAsyncCallback) +TEST(ClientServer, ClientServerBaseAsyncCallback) { const int calls(1); const int sleep(100); @@ -515,7 +506,7 @@ TEST(IO, ClientServerBaseAsyncCallback) eCAL::CServiceServer server("service"); // method callback function - int methods_executed(0); + std::atomic methods_executed(0); auto method_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int { PrintRequest(method_, req_type_, resp_type_, request_); @@ -532,7 +523,7 @@ TEST(IO, ClientServerBaseAsyncCallback) eCAL::CServiceClient client("service"); // response callback function - int responses_executed(0); + std::atomic responses_executed(0); auto response_callback = [&](const struct eCAL::SServiceResponse& service_response_) { PrintResponse(service_response_); @@ -546,7 +537,7 @@ TEST(IO, ClientServerBaseAsyncCallback) eCAL::Process::SleepMS(2000); // call service - int methods_called(0); + std::atomic methods_called(0); std::string m1("foo::method1"); std::string m2("foo::method2"); std::string r1("my request for method 1"); @@ -575,11 +566,11 @@ TEST(IO, ClientServerBaseAsyncCallback) #endif /* ClientServerBaseAsyncCallbackTest */ -#if ClientServerBaseAsyncCallbackTimeoutTest +#if ClientServerBaseAsyncTest -TEST(IO, ClientServerBaseAsyncCallbackTimeout) +TEST(ClientServer, ClientServerBaseAsync) { - const int calls(1); + const int calls(5); // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base async callback test with timeout"); @@ -588,103 +579,103 @@ TEST(IO, ClientServerBaseAsyncCallbackTimeout) eCAL::CServiceServer server("service"); // method callback function - int methods_executed(0); - int method_process_time(0); - auto method_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int - { - eCAL::Process::SleepMS(method_process_time); - PrintRequest(method_, req_type_, resp_type_, request_); - response_ = "I answered on " + request_; - methods_executed++; - return 42; - }; + atomic_signalable num_service_callbacks_finished(0); + int service_callback_time_ms(0); + + auto service_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int + { + eCAL::Process::SleepMS(service_callback_time_ms); + PrintRequest(method_, req_type_, resp_type_, request_); + response_ = "I answered on " + request_; + num_service_callbacks_finished++; + return 42; + }; // add callback for client request - server.AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", method_callback); - server.AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", method_callback); + server.AddMethodCallback("foo::method1", "foo::req_type1", "foo::resp_type1", service_callback); + server.AddMethodCallback("foo::method2", "foo::req_type2", "foo::resp_type2", service_callback); // create service client eCAL::CServiceClient client("service"); // response callback function - int responses_executed(0); - auto response_callback = [&](const struct eCAL::SServiceResponse& service_response_) - { - PrintResponse(service_response_); - responses_executed++; - }; + atomic_signalable num_client_response_callbacks_finished(0); + auto client_response_callback = [&](const struct eCAL::SServiceResponse& service_response_) + { + PrintResponse(service_response_); + num_client_response_callbacks_finished++; + }; // add callback for server response - client.AddResponseCallback(response_callback); + client.AddResponseCallback(client_response_callback); // let's match them -> wait REGISTRATION_REFRESH_CYCLE (ecal_def.h) eCAL::Process::SleepMS(2000); // call service - int methods_called(0); + int num_service_calls(0); std::string m1("foo::method1"); std::string m2("foo::method2"); std::string r1("my request for method 1"); std::string r2("my request for method 2"); - // some calls with method_process_time sleep in the method callback - method_process_time = 100; + // some calls with service_callback_time_ms sleep in the method callback + service_callback_time_ms = 100; for (auto i = 0; i < calls; ++i) { // call method 1 client.CallAsync(m1, r1); - eCAL::Process::SleepMS(method_process_time * 2); - methods_called++; + eCAL::Process::SleepMS(service_callback_time_ms * 2); + num_service_calls++; // call method 2 client.CallAsync(m2, r2); - eCAL::Process::SleepMS(method_process_time * 2); - methods_called++; + eCAL::Process::SleepMS(service_callback_time_ms * 2); + num_service_calls++; } - eCAL::Process::SleepMS(1000); + num_client_response_callbacks_finished.wait_for([num_service_calls](int v) { return num_service_calls == v; }, std::chrono::seconds(1)); - EXPECT_EQ(methods_called, methods_executed); - EXPECT_EQ(methods_called, responses_executed); + EXPECT_EQ(num_service_calls, num_service_callbacks_finished); + EXPECT_EQ(num_service_calls, num_client_response_callbacks_finished); - // call the same method with method_process_time sleep in the method callback - // and less than method_process_time sleep time between - // the calls -> second call should be blocked and fail - methods_called = 0; - methods_executed = 0; - responses_executed = 0; + // Call the methods directly one after another and then wait for the responses. + // As the service callback needs some time to finish and we call it from only + // 1 Client, they will be effectively be serialized. + num_service_calls = 0; + num_service_callbacks_finished = 0; + num_client_response_callbacks_finished = 0; - method_process_time = 100; + service_callback_time_ms = 100; + + auto start = std::chrono::steady_clock::now(); for (auto i = 0; i < calls; ++i) { - // call method 1 - client.CallAsync(m1, r1); - eCAL::Process::SleepMS(method_process_time / 2); - methods_called++; - - // call method 1 - // this one should fail that means: - // server method is not called - // response is called with error message client.CallAsync(m1, r1); - eCAL::Process::SleepMS(method_process_time / 2); - methods_called++; + num_service_calls++; } + auto async_call_end = std::chrono::steady_clock::now(); - eCAL::Process::SleepMS(1000); + EXPECT_LT(async_call_end - start, std::chrono::milliseconds(100)); // The call should return immediately, as they are async. + + num_client_response_callbacks_finished.wait_for([num_service_calls](int v) { return num_service_calls == v; }, std::chrono::seconds(10)); - EXPECT_EQ(methods_called/2, methods_executed); - EXPECT_EQ(methods_called, responses_executed); + auto async_response_end = std::chrono::steady_clock::now(); + + EXPECT_EQ(num_service_calls, num_service_callbacks_finished); + EXPECT_EQ(num_service_calls, num_client_response_callbacks_finished); + EXPECT_GT(async_response_end - start, std::chrono::milliseconds(service_callback_time_ms) * num_service_calls); // The response should take some time, as the service callback needs some time to finish. + // finalize eCAL API eCAL::Finalize(); } -#endif /* ClientServerBaseAsyncCallbackTimeoutTest */ +#endif /* ClientServerBaseAsyncTest */ #if ClientServerBaseBlockingTest -TEST(IO, ClientServerBaseBlocking) +TEST(ClientServer, ClientServerBaseBlocking) { const int num_services(2); const int num_clients(3); @@ -702,7 +693,7 @@ TEST(IO, ClientServerBaseBlocking) } // method callback function - int methods_executed(0); + std::atomic methods_executed(0); auto method_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int { PrintRequest(method_, req_type_, resp_type_, request_); @@ -729,8 +720,8 @@ TEST(IO, ClientServerBaseBlocking) eCAL::Process::SleepMS(2000); // call service - int methods_called(0); - int responses_executed(0); + std::atomic methods_called(0); + std::atomic responses_executed(0); eCAL::ServiceResponseVecT service_response_vec; for (auto i = 0; i < calls; ++i) { @@ -785,182 +776,9 @@ TEST(IO, ClientServerBaseBlocking) #endif /* ClientServerBaseBlockingTest */ -#if ClientServerProtoCallbackTest - -/////////////////////////////////////////////// -// Math Service -/////////////////////////////////////////////// -class MathServiceImpl : public MathService -{ -public: - virtual void Add(::google::protobuf::RpcController* /* controller_ */, const ::SFloatTuple* request_, ::SFloat* response_, ::google::protobuf::Closure* /* done_ */) - { - // print request and - std::cout << "Received request MathService / Add : " << request_->inp1() << " and " << request_->inp2() << std::endl << std::endl; - // create response - response_->set_out(request_->inp1() + request_->inp2()); - } - - virtual void Multiply(::google::protobuf::RpcController* /* controller_ */, const ::SFloatTuple* request_, ::SFloat* response_, ::google::protobuf::Closure* /* done_ */) - { - // print request and - std::cout << "Received request MathService / Multiply : " << request_->inp1() << " and " << request_->inp2() << std::endl << std::endl; - // create response - response_->set_out(request_->inp1() * request_->inp2()); - } - - virtual void Divide(::google::protobuf::RpcController* /* controller_ */, const ::SFloatTuple* request_, ::SFloat* response_, ::google::protobuf::Closure* /* done_ */) - { - // print request and - std::cout << "Received request MathService / Divide : " << request_->inp1() << " and " << request_->inp2() << std::endl << std::endl; - // create response - if (std::fabs(request_->inp2()) > DBL_EPSILON) response_->set_out(request_->inp1() / request_->inp2()); - else response_->set_out(0.0); - } -}; - -TEST(IO, ClientServerProtoCallback) -{ - // initialize eCAL API - eCAL::Initialize(0, nullptr, "clientserver proto callback test"); - - // create MathService server - std::shared_ptr math_service_impl = std::make_shared(); - eCAL::protobuf::CServiceServer math_server(math_service_impl); - - // create MathService client - eCAL::protobuf::CServiceClient math_client; - - // response callback function - double math_response(0.0); - auto response_callback = [&](const struct eCAL::SServiceResponse& service_response_) - { - math_response = 0.0; - switch (service_response_.call_state) - { - // service successful executed - case call_state_executed: - { - SFloat response; - response.ParseFromString(service_response_.response); - std::cout << "Received response MathService / " << service_response_.method_name << " : " << response.out() << " from host " << service_response_.host_name << std::endl; - math_response = response.out(); - } - break; - // service execution failed - case call_state_failed: - std::cout << "Received error MathService / " << service_response_.method_name << " : " << service_response_.error_msg << " from host " << service_response_.host_name << std::endl; - break; - default: - break; - } - }; - - // add callback for server response - math_client.AddResponseCallback(response_callback); - - // let's match them -> wait REGISTRATION_REFRESH_CYCLE (ecal_def.h) - eCAL::Process::SleepMS(2000); - - // test math service - SFloatTuple math_request; - double inp1 = 20.0; - double inp2 = 22.0; - math_request.set_inp1(inp1); - math_request.set_inp2(inp2); - std::string math_request_s = math_request.SerializeAsString(); - - math_response = 0.0; - math_client.Call("Add", math_request_s); - std::cout << std::endl << "Add method called with : " << math_request.inp1() << " and " << math_request.inp1() << std::endl; - EXPECT_EQ(inp1 + inp2, static_cast(math_response)); - - math_response = 0.0; - math_client.Call("Multiply", math_request_s); - std::cout << std::endl << "Multiply method called with : " << math_request.inp1() << " and " << math_request.inp1() << std::endl; - EXPECT_EQ(inp1 * inp2, static_cast(math_response)); - - math_response = 0.0; - math_client.Call("Divide", math_request_s); - std::cout << std::endl << "Divide method called with : " << math_request.inp1() << " and " << math_request.inp1() << std::endl; - EXPECT_EQ(inp1 / inp2, static_cast(math_response)); - - // finalize eCAL API - eCAL::Finalize(); -} - -#endif /* ClientServerProtoCallbackTest */ - -#if ClientServerProtoBlockingTest - -/////////////////////////////////////////////// -// Ping Service -/////////////////////////////////////////////// -class PingServiceImpl : public PingService -{ -public: - void Ping(::google::protobuf::RpcController* /* controller_ */, const ::PingRequest* request_, ::PingResponse* response_, ::google::protobuf::Closure* /* done_ */) - { - // print request and - std::cout << "Received request PingService / Ping : " << request_->message() << std::endl << std::endl; - // create response - response_->set_answer("PONG"); - } -}; - -TEST(IO, ClientServerProtoBlocking) -{ - // initialize eCAL API - eCAL::Initialize(0, nullptr, "clientserver proto blocking test"); - - // create PingService server - std::shared_ptr ping_service_impl = std::make_shared(); - eCAL::protobuf::CServiceServer ping_server(ping_service_impl); - - // create PingService client - eCAL::protobuf::CServiceClient ping_client; - - // let's match them -> wait REGISTRATION_REFRESH_CYCLE (ecal_def.h) - eCAL::Process::SleepMS(2000); - - // test ping service - eCAL::ServiceResponseVecT service_response_vec; - PingRequest ping_request; - ping_request.set_message("PING"); - ping_client.Call("Ping", ping_request, -1, &service_response_vec); - std::cout << std::endl << "Ping method called with message : " << ping_request.message() << std::endl; - for (auto service_response : service_response_vec) - { - EXPECT_EQ(call_state_executed, service_response.call_state); - switch (service_response.call_state) - { - // service successful executed - case call_state_executed: - { - PingResponse response; - response.ParseFromString(service_response.response); - std::cout << "Received response PingService / Ping : " << response.answer() << " from host " << service_response.host_name << std::endl; - EXPECT_STREQ(response.answer().c_str(), "PONG"); - } - break; - // service execution failed - case call_state_failed: - std::cout << "Received error PingService / Ping : " << service_response.error_msg << " from host " << service_response.host_name << std::endl; - break; - default: - break; - } - } - - // finalize eCAL API - eCAL::Finalize(); -} - -#endif /* ClientServerProtoBlockingTest */ - #if NestedRPCCallTest -TEST(IO, NestedRPCCall) +TEST(ClientServer, NestedRPCCall) { const int calls(1); const int sleep(0); @@ -972,7 +790,7 @@ TEST(IO, NestedRPCCall) eCAL::CServiceServer server("service"); // request callback function - int methods_executed(0); + std::atomic methods_executed(0); auto method_callback = [&](const std::string& method_, const std::string& req_type_, const std::string& resp_type_, const std::string& request_, std::string& response_) -> int { PrintRequest(method_, req_type_, resp_type_, request_); @@ -990,8 +808,8 @@ TEST(IO, NestedRPCCall) eCAL::CServiceClient client2("service"); // response callback function - int methods_called(0); - int responses_executed(0); + std::atomic methods_called(0); + std::atomic responses_executed(0); bool success(true); auto response_callback1 = [&](const struct eCAL::SServiceResponse& service_response_) { diff --git a/testing/ecal/core_test/CMakeLists.txt b/tests/core_test/CMakeLists.txt similarity index 99% rename from testing/ecal/core_test/CMakeLists.txt rename to tests/core_test/CMakeLists.txt index 96eb4ac..139c596 100644 --- a/testing/ecal/core_test/CMakeLists.txt +++ b/tests/core_test/CMakeLists.txt @@ -27,8 +27,10 @@ set(core_test_src ecal_add_gtest(${PROJECT_NAME} ${core_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 testing/ecal/core) \ No newline at end of file +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) diff --git a/testing/ecal/core_test/src/core_test.cpp b/tests/core_test/src/core_test.cpp similarity index 77% rename from testing/ecal/core_test/src/core_test.cpp rename to tests/core_test/src/core_test.cpp index 32f22eb..47fcc63 100644 --- a/testing/ecal/core_test/src/core_test.cpp +++ b/tests/core_test/src/core_test.cpp @@ -111,60 +111,61 @@ TEST(Core, LeakedPubSub) TEST(Core, CallbackDestruction) { - // initialize eCAL API - EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "callback destruction")); + for (int i = 0; i < 10; ++i) + { + // initialize eCAL API + EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "callback destruction")); - // enable loop back communication in the same thread - eCAL::Util::EnableLoopback(true); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); - // create subscriber and register a callback - std::shared_ptr< eCAL::string::CSubscriber> sub; + // create subscriber and register a callback + std::shared_ptr> sub; - // create publisher - eCAL::string::CPublisher pub("foo"); + // create publisher + eCAL::string::CPublisher pub("foo"); - // start publishing thread - std::atomic pub_stop(false); - std::thread pub_t([&]() { - while (!pub_stop) - { - pub.Send("Hello World"); + // start publishing thread + std::atomic pub_stop(false); + std::thread pub_t([&]() { + while (!pub_stop) { + pub.Send("Hello World"); #if 0 - // some kind of busy waiting.... - int y = 0; - for (int i = 0; i < 100000; i++) - { - y += i; - } + // some kind of busy waiting.... + int y = 0; + for (int i = 0; i < 100000; i++) + { + y += i; + } #else - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); #endif - } - }); + } + }); + + std::atomic sub_stop(false); + std::thread sub_t([&]() { + while (!sub_stop) { + sub = std::make_shared>("foo"); + sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_4)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); - std::atomic sub_stop(false); - std::thread sub_t([&]() { - while (!sub_stop) - { - sub = std::make_shared>("foo"); - sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_4)); - std::this_thread::sleep_for(std::chrono::seconds(2)); - } - }); + // let them work together + std::this_thread::sleep_for(std::chrono::seconds(10)); - // let them work together - std::this_thread::sleep_for(std::chrono::seconds(10)); + // stop publishing thread + pub_stop = true; + pub_t.join(); - // stop publishing thread - pub_stop = true; - pub_t.join(); - - sub_stop = true; - sub_t.join(); + sub_stop = true; + sub_t.join(); - // finalize eCAL API - // without destroying any pub / sub - EXPECT_EQ(0, eCAL::Finalize()); + // finalize eCAL API + // without destroying any pub / sub + EXPECT_EQ(0, eCAL::Finalize()); + } } /* excluded for now, system timer jitter too high */ diff --git a/testing/ecal/expmap_test/CMakeLists.txt b/tests/expmap_test/CMakeLists.txt similarity index 99% rename from testing/ecal/expmap_test/CMakeLists.txt rename to tests/expmap_test/CMakeLists.txt index b1f0494..04229c6 100644 --- a/testing/ecal/expmap_test/CMakeLists.txt +++ b/tests/expmap_test/CMakeLists.txt @@ -39,4 +39,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_gtest(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal/core) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) diff --git a/testing/ecal/expmap_test/src/expmap_test.cpp b/tests/expmap_test/src/expmap_test.cpp similarity index 71% rename from testing/ecal/expmap_test/src/expmap_test.cpp rename to tests/expmap_test/src/expmap_test.cpp index 1d04515..b54f937 100644 --- a/testing/ecal/expmap_test/src/expmap_test.cpp +++ b/tests/expmap_test/src/expmap_test.cpp @@ -5,9 +5,9 @@ * 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. @@ -18,18 +18,19 @@ */ #include -#include "ecal_expmap.h" +#include "util/ecal_expmap.h" #include #include #include +#include #include #include TEST(ExpMap, ExpMapSetGet) -{ +{ // create the map with 2500 ms expiration eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); @@ -125,6 +126,28 @@ TEST(ExpMap, ExpMapFind) EXPECT_EQ(0, expmap.size()); } +// This test assures that find can be called on a const CExpMap and returns an CExpMap::const_iterator +TEST(ExpMap, ExpMapFindConst) +{ + eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + + auto it = expmap.find("A"); + EXPECT_EQ(expmap.end(), it); + + expmap["A"] = 1; + + const auto& const_ref_exmap = expmap; + auto const_it = const_ref_exmap.find("A"); + // assert that we are actually getting a const_iterator here! + static_assert(std::is_same::const_iterator>::value, "We're not being returned a const_iterator from find."); + int i = (*const_it).second; + EXPECT_EQ(i, 1); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + expmap.remove_deprecated(); + EXPECT_EQ(0, expmap.size()); +} + TEST(ExpMap, ExpMapIterate) { // create the map with 2500 ms expiration @@ -136,7 +159,22 @@ TEST(ExpMap, ExpMapIterate) for (auto&& entry : expmap) { - key = entry.first; + key = entry.first; + value = entry.second; + } + + EXPECT_EQ(std::string("A"), key); + EXPECT_EQ(1, value); +} + +void ConstRefIterate(const eCAL::Util::CExpMap& map) +{ + std::string key; + int value; + + for (auto&& entry : map) + { + key = entry.first; value = entry.second; } @@ -144,6 +182,15 @@ TEST(ExpMap, ExpMapIterate) EXPECT_EQ(1, value); } +TEST(ExpMap, ConstExpMapIterate) +{ + // create the map with 2500 ms expiration + eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + expmap["A"] = 1; + + ConstRefIterate(expmap); +} + TEST(ExpMap, ExpMapEmpty) { eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); @@ -158,4 +205,14 @@ TEST(ExpMap, ExpMapSize) EXPECT_EQ(0, expmap.size()); expmap["A"] = 1; EXPECT_EQ(1, expmap.size()); +} + +TEST(ExpMap, ExpMapRemove) +{ + eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + expmap["A"] = 1; + EXPECT_EQ(1, expmap.size()); + EXPECT_TRUE(expmap.erase("A")); + EXPECT_EQ(0, expmap.size()); + EXPECT_FALSE(expmap.erase("B")); } \ No newline at end of file diff --git a/testing/ecal/io_memfile_test/CMakeLists.txt b/tests/io_memfile_test/CMakeLists.txt similarity index 72% rename from testing/ecal/io_memfile_test/CMakeLists.txt rename to tests/io_memfile_test/CMakeLists.txt index 2e23ba6..881a182 100644 --- a/testing/ecal/io_memfile_test/CMakeLists.txt +++ b/tests/io_memfile_test/CMakeLists.txt @@ -23,22 +23,24 @@ find_package(GTest REQUIRED) set(memfile_test_src src/memfile_test.cpp - ../../../ecal/core/src/io/ecal_memfile.cpp - ../../../ecal/core/src/io/ecal_memfile_db.cpp - ../../../ecal/core/src/io/ecal_named_mutex.cpp + src/memfile_naming_test.cpp + ../../src/core/src/io/mtx/ecal_named_mutex.cpp + ../../src/core/src/io/shm/ecal_memfile.cpp + ../../src/core/src/io/shm/ecal_memfile_db.cpp + ../../src/core/src/io/shm/ecal_memfile_naming.cpp ) if(UNIX) set(memfile_test_os_src - ../../../ecal/core/src/io/linux/ecal_memfile_os.cpp - ../../../ecal/core/src/io/linux/ecal_named_mutex_impl.cpp + ../../src/core/src/io/mtx/linux/ecal_named_mutex_impl.cpp + ../../src/core/src/io/shm/linux/ecal_memfile_os.cpp ) endif() if(WIN32) set(memfile_test_os_src - ../../../ecal/core/src/io/win32/ecal_memfile_os.cpp - ../../../ecal/core/src/io/win32/ecal_named_mutex_impl.cpp + ../../src/core/src/io/mtx/win32/ecal_named_mutex_impl.cpp + ../../src/core/src/io/shm/win32/ecal_memfile_os.cpp ) endif() @@ -54,7 +56,8 @@ target_link_libraries(${PROJECT_NAME} ) 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 testing/ecal/io) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) diff --git a/testing/ecal/event_test/src/event_test.cpp b/tests/io_memfile_test/src/memfile_naming_test.cpp similarity index 63% rename from testing/ecal/event_test/src/event_test.cpp rename to tests/io_memfile_test/src/memfile_naming_test.cpp index 6e02acc..929d701 100644 --- a/testing/ecal/event_test/src/event_test.cpp +++ b/tests/io_memfile_test/src/memfile_naming_test.cpp @@ -5,9 +5,9 @@ * 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. @@ -17,25 +17,24 @@ * ========================= eCAL LICENSE ================================= */ -#include +#include "io/shm/ecal_memfile_naming.h" -#include +#include +#include +#include +#include +#include -TEST(Event, EventSetGet) -{ - // global parameter - const std::string event_name = "my_event"; +#include - // create named event - eCAL::EventHandleT event_handle; - EXPECT_EQ(true, eCAL::gOpenEvent(&event_handle, event_name)); - // get none set event - EXPECT_EQ(false, gWaitForEvent(event_handle, 10)); +TEST(MemFile, MemfileNaming) +{ + std::chrono::steady_clock::time_point timepoint{}; - // set event - EXPECT_EQ(true, gSetEvent(event_handle)); + std::string memfile_name_1{ eCAL::memfile::BuildRandomMemFileName("test_")}; + std::string memfile_name_2{ eCAL::memfile::BuildRandomMemFileName("test_")}; - // get set event - EXPECT_EQ(true, gWaitForEvent(event_handle, 100)); + EXPECT_LE(memfile_name_1.size(), 13); + EXPECT_NE(memfile_name_1, memfile_name_2); } diff --git a/testing/ecal/io_memfile_test/src/memfile_test.cpp b/tests/io_memfile_test/src/memfile_test.cpp similarity index 90% rename from testing/ecal/io_memfile_test/src/memfile_test.cpp rename to tests/io_memfile_test/src/memfile_test.cpp index 81996fe..bf8e6fc 100644 --- a/testing/ecal/io_memfile_test/src/memfile_test.cpp +++ b/tests/io_memfile_test/src/memfile_test.cpp @@ -18,8 +18,8 @@ */ #include -#include "io/ecal_memfile.h" -#include "io/ecal_memfile_db.h" +#include "io/shm/ecal_memfile.h" +#include "io/shm/ecal_memfile_db.h" #include #include @@ -38,7 +38,7 @@ namespace eCAL } } -TEST(IO, MemfileReadWrite) +TEST(MemFile, MemfileReadWrite) { eCAL::CMemoryFile mem_file; @@ -68,7 +68,7 @@ TEST(IO, MemfileReadWrite) EXPECT_EQ(false, mem_file.IsOpened()); // write content to memory file before open - EXPECT_EQ(0, mem_file.Write((void*)send_s.c_str(), slen, 0)); + EXPECT_EQ(0, mem_file.WriteBuffer((void*)send_s.c_str(), slen, 0)); // open memory file with write access (timeout 100 ms) EXPECT_EQ(true, mem_file.GetWriteAccess(100)); @@ -83,19 +83,19 @@ TEST(IO, MemfileReadWrite) EXPECT_EQ(false, mem_file.HasReadAccess()); // write half content to memory file - EXPECT_EQ(slen/2, mem_file.Write((void*)send_s.c_str(), slen/2, 0)); + EXPECT_EQ(slen/2, mem_file.WriteBuffer((void*)send_s.c_str(), slen/2, 0)); // write full content to memory file - EXPECT_EQ(slen, mem_file.Write((void*)send_s.c_str(), slen, 0)); + EXPECT_EQ(slen, mem_file.WriteBuffer((void*)send_s.c_str(), slen, 0)); // write double sized content to memory file std::string double_send_s = send_s + send_s; - EXPECT_EQ(0, mem_file.Write((void*)double_send_s.c_str(), double_send_s.size(), 0)); + EXPECT_EQ(0, mem_file.WriteBuffer((void*)double_send_s.c_str(), double_send_s.size(), 0)); // finally write half content to memory file again for later reader test slen /= 2; send_s.resize(slen); - EXPECT_EQ(slen, mem_file.Write((void*)send_s.c_str(), slen, 0)); + EXPECT_EQ(slen, mem_file.WriteBuffer((void*)send_s.c_str(), slen, 0)); // close memory file EXPECT_EQ(true, mem_file.ReleaseWriteAccess()); @@ -110,7 +110,7 @@ TEST(IO, MemfileReadWrite) EXPECT_EQ(false, mem_file.HasReadAccess()); // write content to closed memory file - EXPECT_EQ(0, mem_file.Write((void*)send_s.c_str(), slen, 0)); + EXPECT_EQ(0, mem_file.WriteBuffer((void*)send_s.c_str(), slen, 0)); // open memory file with timeout 100 ms EXPECT_EQ(true, mem_file.GetReadAccess(100)); @@ -140,7 +140,7 @@ TEST(IO, MemfileReadWrite) EXPECT_EQ(true, mem_file.Destroy(true)); } -TEST(IO, MemfilePerf) +TEST(MemFile, MemfilePerf) { eCAL::CMemoryFile mem_file; @@ -171,7 +171,7 @@ TEST(IO, MemfilePerf) EXPECT_EQ(true, mem_file.GetWriteAccess(10)); // write content to memory file - EXPECT_EQ(slen, mem_file.Write((void*)send_s.c_str(), slen, 0)); + EXPECT_EQ(slen, mem_file.WriteBuffer((void*)send_s.c_str(), slen, 0)); // release write access EXPECT_EQ(true, mem_file.ReleaseWriteAccess()); @@ -200,7 +200,7 @@ TEST(IO, MemfilePerf) EXPECT_EQ(true, mem_file.Destroy(true)); } -TEST(IO, MemfileConcurrency) +TEST(MemFile, MemfileConcurrency) { eCAL::CMemoryFile mem_file; @@ -227,7 +227,7 @@ TEST(IO, MemfileConcurrency) if (mem_file.HasWriteAccess()) { write_buf[0] = num_writes; - auto written = mem_file.Write((void*)write_buf.data(), write_buf.size(), 0); + auto written = mem_file.WriteBuffer((void*)write_buf.data(), write_buf.size(), 0); EXPECT_EQ(buflen, written); std::cout << std::endl; std::cout << "producer write access : " << num_writes << std::endl; @@ -293,4 +293,7 @@ TEST(IO, MemfileConcurrency) EXPECT_EQ(num_writes, num_reads1); EXPECT_EQ(num_writes, num_reads2); + + // destroy memory file + EXPECT_EQ(true, mem_file.Destroy(true)); } diff --git a/testing/ecal/pubsub_proto_test/CMakeLists.txt b/tests/pubsub_proto_test/CMakeLists.txt similarity index 97% rename from testing/ecal/pubsub_proto_test/CMakeLists.txt rename to tests/pubsub_proto_test/CMakeLists.txt index f2bedfa..ac7c77f 100644 --- a/testing/ecal/pubsub_proto_test/CMakeLists.txt +++ b/tests/pubsub_proto_test/CMakeLists.txt @@ -25,6 +25,7 @@ find_package(Protobuf REQUIRED) create_targets_protobuf() set(${PROJECT_NAME}_src + src/proto_dyn_subscriber_test.cpp src/proto_publisher_test.cpp src/proto_subscriber_test.cpp ) @@ -48,4 +49,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_gtest(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal/pubsub) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) diff --git a/tests/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp b/tests/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp new file mode 100644 index 0000000..a79630c --- /dev/null +++ b/tests/pubsub_proto_test/src/proto_dyn_subscriber_test.cpp @@ -0,0 +1,133 @@ +/* ========================= 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 ================================= +*/ + +// std headers +#include +#include +#include +// used libraries +#include +// own project +#include +#include +#include + +#include + +// subscriber callback function + +#define REGISTRATION_REFRESH_CYCLE 1000 + +class ProtoDynSubscriberTest : public ::testing::Test { +public: + ProtoDynSubscriberTest() + : received_callbacks(0) + { + // Initialize eCAL + eCAL::Initialize(); + // publish / subscribe match in the same process + eCAL::Util::EnableLoopback(true); + } + + virtual ~ProtoDynSubscriberTest() { + // Finalize eCAL + eCAL::Finalize(); + } + + void SendPerson(eCAL::protobuf::CPublisher& pub) + { + pb::People::Person p; + p.set_id(1); + p.set_name("Max"); + pub.Send(p); + } + + void OnPerson(const char*, const google::protobuf::Message&, long long) + { + received_callbacks++; + } + + std::atomic received_callbacks; +}; + +int extract_id(const google::protobuf::Message& msg_) +{ + int count = msg_.GetDescriptor()->field_count(); + const google::protobuf::Reflection* ref_ptr = msg_.GetReflection(); + + if (ref_ptr) + { + for (int i = 0; i < count; ++i) + { + auto field = msg_.GetDescriptor()->field(i); + + const google::protobuf::FieldDescriptor::CppType fdt = field->cpp_type(); + if (fdt == google::protobuf::FieldDescriptor::CPPTYPE_INT32 && !field->is_repeated()) + { + if (field->name() == "id") + { + return ref_ptr->GetInt32(msg_, field); + } + } + } + } + return 0; +} + + +TEST_F(ProtoDynSubscriberTest, SendReceiveCB) +{ + // Assert that the Subscriber can be move constructed. + eCAL::protobuf::CDynamicSubscriber person_dyn_rec("ProtoSubscriberTest"); + auto person_callback = std::bind(&ProtoDynSubscriberTest::OnPerson, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + person_dyn_rec.AddReceiveCallback(person_callback); + ASSERT_TRUE(person_dyn_rec.IsCreated()); + + eCAL::protobuf::CPublisher person_pub("ProtoSubscriberTest"); + + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + + SendPerson(person_pub); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + // assert that the OnPerson callback has been called once. + ASSERT_EQ(1, received_callbacks); +} + +TEST_F(ProtoDynSubscriberTest, SendReceive) +{ + // Assert that the Subscriber can be move constructed. + eCAL::protobuf::CDynamicSubscriber person_dyn_rec("ProtoSubscriberTest"); + ASSERT_TRUE(person_dyn_rec.IsCreated()); + + eCAL::protobuf::CPublisher person_pub("ProtoSubscriberTest"); + + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + + SendPerson(person_pub); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + google::protobuf::Message* message = person_dyn_rec.getMessagePointer(); + ASSERT_NE(message, nullptr) << "pointer returned by dynamic subscriber may not be null"; + + bool received = person_dyn_rec.Receive(*message, nullptr, 500); + // assert that the OnPerson callback has been called once. + ASSERT_TRUE(received) << "we should have received data that was sent"; + auto id = extract_id(*message); + ASSERT_EQ(id, 1); +} diff --git a/testing/ecal/pubsub_proto_test/src/proto_publisher_test.cpp b/tests/pubsub_proto_test/src/proto_publisher_test.cpp similarity index 97% rename from testing/ecal/pubsub_proto_test/src/proto_publisher_test.cpp rename to tests/pubsub_proto_test/src/proto_publisher_test.cpp index 0b312b2..a0f10c5 100644 --- a/testing/ecal/pubsub_proto_test/src/proto_publisher_test.cpp +++ b/tests/pubsub_proto_test/src/proto_publisher_test.cpp @@ -19,7 +19,6 @@ // std headers #include -#include // used libraries #include // own project @@ -28,10 +27,6 @@ #include - -// subscriber callback function - - class ProtoPublisherTest : public ::testing::Test { public: diff --git a/testing/ecal/pubsub_proto_test/src/proto_subscriber_test.cpp b/tests/pubsub_proto_test/src/proto_subscriber_test.cpp similarity index 98% rename from testing/ecal/pubsub_proto_test/src/proto_subscriber_test.cpp rename to tests/pubsub_proto_test/src/proto_subscriber_test.cpp index 20732f5..001b8ff 100644 --- a/testing/ecal/pubsub_proto_test/src/proto_subscriber_test.cpp +++ b/tests/pubsub_proto_test/src/proto_subscriber_test.cpp @@ -30,10 +30,6 @@ #include -// subscriber callback function - -#define REGISTRATION_REFRESH_CYCLE 1000 - class ProtoSubscriberTest : public ::testing::Test { public: ProtoSubscriberTest() @@ -85,7 +81,6 @@ TEST_F(ProtoSubscriberTest, SendReceive) } - TEST_F(ProtoSubscriberTest, MoveAssignment) { eCAL::protobuf::CSubscriber person_rec("ProtoSubscriberTest"); @@ -141,5 +136,4 @@ TEST_F(ProtoSubscriberTest, MoveConstruction) std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // assert that the OnPerson callback has been called once. ASSERT_EQ(1, received_callbacks); - } diff --git a/samples/cpp/person/person_snd/src/protobuf/animal.proto b/tests/pubsub_proto_test/src/protobuf/animal.proto similarity index 100% rename from samples/cpp/person/person_snd/src/protobuf/animal.proto rename to tests/pubsub_proto_test/src/protobuf/animal.proto diff --git a/samples/cpp/person/person_snd/src/protobuf/house.proto b/tests/pubsub_proto_test/src/protobuf/house.proto similarity index 100% rename from samples/cpp/person/person_snd/src/protobuf/house.proto rename to tests/pubsub_proto_test/src/protobuf/house.proto diff --git a/samples/cpp/person/person_snd/src/protobuf/person.proto b/tests/pubsub_proto_test/src/protobuf/person.proto similarity index 100% rename from samples/cpp/person/person_snd/src/protobuf/person.proto rename to tests/pubsub_proto_test/src/protobuf/person.proto diff --git a/testing/ecal/pubsub_test/CMakeLists.txt b/tests/pubsub_test/CMakeLists.txt similarity index 99% rename from testing/ecal/pubsub_test/CMakeLists.txt rename to tests/pubsub_test/CMakeLists.txt index dc25cd2..b9c50d1 100644 --- a/testing/ecal/pubsub_test/CMakeLists.txt +++ b/tests/pubsub_test/CMakeLists.txt @@ -33,4 +33,4 @@ target_link_libraries(${PROJECT_NAME} Threads::Threads) target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_gtest(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal/pubsub) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) diff --git a/testing/ecal/pubsub_test/src/pubsub_receive_test.cpp b/tests/pubsub_test/src/pubsub_receive_test.cpp similarity index 98% rename from testing/ecal/pubsub_test/src/pubsub_receive_test.cpp rename to tests/pubsub_test/src/pubsub_receive_test.cpp index dfba50e..1c58d65 100644 --- a/testing/ecal/pubsub_test/src/pubsub_receive_test.cpp +++ b/tests/pubsub_test/src/pubsub_receive_test.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -64,8 +63,7 @@ void measure_execution_within_range(const std::string& description, std::functio EXPECT_TRUE(within_epsilon) << "Execution of " << description << " took " << duration.count() << " ns but expected was " << expected_runtime.count()*1000000 << "ns"; } - -TEST(SUBSCRIBER, TimingSubscriberReceive) +TEST(PubSub, TimingSubscriberReceive) { // initialize eCAL API EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "subscriber_receive_timing")); @@ -161,10 +159,8 @@ TEST(SUBSCRIBER, TimingSubscriberReceive) EXPECT_EQ(0, eCAL::Finalize()); } - - // This tests test for sporadically received empty messages which were a problem. -TEST(SUBSCRIBER, SporadicEmptyReceives) +TEST(PubSub, SporadicEmptyReceives) { // initialize eCAL API EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "sporadic_empty_receives")); @@ -215,7 +211,6 @@ TEST(SUBSCRIBER, SporadicEmptyReceives) sub_stop = true; sub_t.join(); - // finalize eCAL API EXPECT_EQ(0, eCAL::Finalize()); } diff --git a/testing/ecal/pubsub_test/src/pubsub_test.cpp b/tests/pubsub_test/src/pubsub_test.cpp similarity index 53% rename from testing/ecal/pubsub_test/src/pubsub_test.cpp rename to tests/pubsub_test/src/pubsub_test.cpp index 572fc53..1639080 100644 --- a/testing/ecal/pubsub_test/src/pubsub_test.cpp +++ b/tests/pubsub_test/src/pubsub_test.cpp @@ -18,8 +18,11 @@ */ #include +#include +#include #include +#include #include @@ -47,8 +50,8 @@ static std::string CreatePayLoad(size_t payload_size_) return(s); } -TEST(IO, InitializeFinalize) -{ +TEST(PubSub, InitializeFinalize) +{ // Is eCAL API initialized ? EXPECT_EQ(0, eCAL::IsInitialized()); @@ -61,185 +64,181 @@ TEST(IO, InitializeFinalize) // initialize eCAL API again we expect return value 1 for yet initialized EXPECT_EQ(1, eCAL::Initialize(0, nullptr, "initialize_test")); - // post initialize eCAL API monitoring API we expect return value 0 for success - EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "initialize_test", eCAL::Init::Monitoring)); - - // post initialize eCAL API monitoring API again we expect return value 1 for yet initialized - EXPECT_EQ(1, eCAL::Initialize(0, nullptr, "initialize_test", eCAL::Init::Monitoring)); - - // finalize eCAL API 2 times for the two monitoring post calls, so we decrease the reference - // counter and expect 0 for success - EXPECT_EQ(0, eCAL::Finalize()); + // finalize eCAL API we expect return value 0 even it will not be really finalized because it's 2 times initialzed and 1 time finalized EXPECT_EQ(0, eCAL::Finalize()); - // Is eCAL API initialized ? yes .. + // Is eCAL API initialized ? yes it' still initialized EXPECT_EQ(1, eCAL::IsInitialized()); - // finalize first time eCAL API we still expect 0 because - // reference counter still greater 0 + // finalize eCAL API we expect return value 0 because now it will be finalized EXPECT_EQ(0, eCAL::Finalize()); - // finalize second time eCAL API we still expect 0 - // but now reference counter is 0 and destruction should be succeeded - EXPECT_EQ(0, eCAL::Finalize()); + // Is eCAL API initialized ? no + EXPECT_EQ(0, eCAL::IsInitialized()); - // finalize eCAL API again we expect 1 because yet finalized + // finalize eCAL API we expect return value 1 because it was finalized before EXPECT_EQ(1, eCAL::Finalize()); } -TEST(IO, CreateDestroy) -{ - // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); - - // create publisher for topic "A" - eCAL::CPublisher pub; - - // check state - EXPECT_EQ(false, pub.IsCreated()); - - // create - EXPECT_EQ(true, pub.Create("A")); - - // check state - EXPECT_EQ(true, pub.IsCreated()); - - // create subscriber for topic "A" - eCAL::CSubscriber sub; - - // check state - EXPECT_EQ(false, sub.IsCreated()); - - // create - EXPECT_EQ(true, sub.Create("A")); - - // check state - EXPECT_EQ(true, sub.IsCreated()); - - // destroy publisher - EXPECT_EQ(true, pub.Destroy()); - - // destroy subscriber - EXPECT_EQ(true, sub.Destroy()); +TEST(PubSub, MultipleInitializeFinalize) +{ + // try to initialize / finalize multiple times + for (auto i = 0; i < 4; ++i) + { + // initialize eCAL API + EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "multiple initialize/finalize")); - // finalize eCAL API - eCAL::Finalize(); + // finalize eCAL API + EXPECT_EQ(0, eCAL::Finalize()); + } } -TEST(IO, TypeDescriptionStatic) +TEST(PubSub, LeakedPubSub) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); + EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "leaked pub/sub")); - // create publisher without type and description - eCAL::CPublisher pub("A"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); - // check type name - std::string ttype = pub.GetTypeName(); - EXPECT_EQ("", ttype); + // create subscriber and register a callback + eCAL::CSubscriber sub("foo"); + sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2)); - // check description - std::string tdesc = pub.GetDescription(); - EXPECT_EQ("", tdesc); + // create publisher + eCAL::CPublisher pub("foo"); + + // let's match them + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); - // check type name - eCAL::Util::GetTopicTypeName("A", ttype); - EXPECT_EQ("", ttype); + // start publishing thread + std::atomic pub_stop(false); + std::thread pub_t([&]() { + while (!pub_stop) + { + pub.Send("Hello World"); +#if 0 + // some kind of busy waiting.... + int y = 0; + for (int i = 0; i < 100000; i++) + { + y += i; + } +#else + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +#endif + } + }); - // check description - eCAL::Util::GetTopicDescription("A", tdesc); - EXPECT_EQ("", tdesc); + // let them work together + std::this_thread::sleep_for(std::chrono::seconds(2)); // finalize eCAL API - eCAL::Finalize(); + // without destroying any pub / sub + EXPECT_EQ(0, eCAL::Finalize()); + + // stop publishing thread + pub_stop = true; pub_t.join(); } -TEST(IO, TypeDescriptionDynamic) +TEST(PubSub, CallbackDestruction) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); + EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "callback destruction")); - { - std::string ttype("type_A"); - std::string tdesc("desc_A"); - - // create publisher without type and description - eCAL::CPublisher pub("A", ttype, tdesc); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); - // check type name - std::string desc_type = pub.GetTypeName(); - EXPECT_EQ(ttype, desc_type); + // create subscriber and register a callback + std::shared_ptr sub; - // check description - std::string desc_a = pub.GetDescription(); - EXPECT_EQ(tdesc, desc_a); + // create publisher + eCAL::CPublisher pub("foo"); - // check type name - eCAL::Util::GetTopicTypeName("A", desc_type); - EXPECT_EQ(ttype, desc_type); + // start publishing thread + std::atomic pub_stop(false); + std::thread pub_t([&]() { + while (!pub_stop) + { + pub.Send("Hello World"); +#if 0 + // some kind of busy waiting.... + int y = 0; + for (int i = 0; i < 100000; i++) + { + y += i; + } +#else + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +#endif + } + }); - // check description - eCAL::Util::GetTopicDescription("A", desc_a); - EXPECT_EQ(tdesc, desc_a); + std::atomic sub_stop(false); + std::thread sub_t([&]() { + while (!sub_stop) + { + sub = std::make_shared>("foo"); + sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2)); + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + }); - // set topic description - std::string ttdesc_new("desc_A_new"); - pub.SetDescription(ttdesc_new); // Already set descriptions are not overwritten in the database + // let them work together + std::this_thread::sleep_for(std::chrono::seconds(10)); - // check type name (should not be influenced by SetDescription) - EXPECT_EQ(eCAL::Util::GetTopicTypeName("A"), ttype); + // stop publishing thread + pub_stop = true; + pub_t.join(); - // check description of publiher - EXPECT_EQ(pub.GetDescription(), ttdesc_new); + sub_stop = true; + sub_t.join(); - // check description of general database - EXPECT_EQ(eCAL::Util::GetTopicDescription("A"), tdesc); - } + // finalize eCAL API + // without destroying any pub / sub + EXPECT_EQ(0, eCAL::Finalize()); +} - // Test replace empty description - { - auto pub2 = eCAL::CPublisher("B", "my_type", ""); +TEST(PubSub, CreateDestroy) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "pubsub_test"); - EXPECT_EQ(eCAL::Util::GetTopicTypeName("B"), "my_type"); - EXPECT_EQ(eCAL::Util::GetTopicDescription("B"), ""); + // create publisher for topic "foo" + eCAL::CPublisher pub; - pub2.SetDescription("my_description"); + // check state + EXPECT_EQ(false, pub.IsCreated()); - EXPECT_EQ(eCAL::Util::GetTopicTypeName("B"), "my_type"); - EXPECT_EQ(eCAL::Util::GetTopicDescription("B"), "my_description"); + // create + EXPECT_EQ(true, pub.Create("foo")); - // A new publisher cannot change the description any more - auto pub3 = eCAL::CPublisher("B", "my_type", "my_description_2"); + // check state + EXPECT_EQ(true, pub.IsCreated()); - EXPECT_EQ(eCAL::Util::GetTopicTypeName("B"), "my_type"); - EXPECT_EQ(eCAL::Util::GetTopicDescription("B"), "my_description"); - } + // create subscriber for topic "foo" + eCAL::CSubscriber sub; - // Test Publisher replaces subscriber's description - { - auto sub = eCAL::CSubscriber("C", "type", "desc"); + // check state + EXPECT_EQ(false, sub.IsCreated()); - EXPECT_EQ(eCAL::Util::GetTopicTypeName("C"), "type"); - EXPECT_EQ(eCAL::Util::GetTopicDescription("C"), "desc"); + // create + EXPECT_EQ(true, sub.Create("foo")); - // A publisher without a description will NOT replace the subscribers data - auto pub1 = eCAL::CPublisher("C", "type2", ""); - - EXPECT_EQ(eCAL::Util::GetTopicTypeName("C"), "type"); - EXPECT_EQ(eCAL::Util::GetTopicDescription("C"), "desc"); + // check state + EXPECT_EQ(true, sub.IsCreated()); - // A publisher with a description will replace the subscribers data - auto pub2 = eCAL::CPublisher("C", "type3", "desc3"); + // destroy publisher + EXPECT_EQ(true, pub.Destroy()); - EXPECT_EQ(eCAL::Util::GetTopicTypeName("C"), "type3"); - EXPECT_EQ(eCAL::Util::GetTopicDescription("C"), "desc3"); - } + // destroy subscriber + EXPECT_EQ(true, sub.Destroy()); // finalize eCAL API eCAL::Finalize(); } -TEST(IO, SimpleMessage1) +TEST(PubSub, SimpleMessage1) { // default send / receive strings std::string send_s = CreatePayLoad(PAYLOAD_SIZE); @@ -251,11 +250,11 @@ TEST(IO, SimpleMessage1) // publish / subscribe match in the same process eCAL::Util::EnableLoopback(true); - // create publisher for topic "A" - eCAL::CPublisher pub("A"); + // create publisher for topic "foo" + eCAL::CPublisher pub("foo"); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); + // create subscriber for topic "foo" + eCAL::CSubscriber sub("foo"); // let's match them eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); @@ -284,7 +283,7 @@ TEST(IO, SimpleMessage1) eCAL::Finalize(); } -TEST(IO, SimpleMessage2) +TEST(PubSub, SimpleMessage2) { // default send / receive strings std::string send_s = CreatePayLoad(PAYLOAD_SIZE); @@ -296,11 +295,11 @@ TEST(IO, SimpleMessage2) // publish / subscribe match in the same process eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); + // create subscriber for topic "foo" + eCAL::CSubscriber sub("foo"); - // create publisher for topic "A" - eCAL::CPublisher pub("A"); + // create publisher for topic "foo" + eCAL::CPublisher pub("foo"); // let's match them eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); @@ -323,7 +322,7 @@ TEST(IO, SimpleMessage2) eCAL::Finalize(); } -TEST(IO, SimpleMessageCB) +TEST(PubSub, SimpleMessageCB) { // default send string std::string send_s = CreatePayLoad(PAYLOAD_SIZE); @@ -334,11 +333,11 @@ TEST(IO, SimpleMessageCB) // publish / subscribe match in the same process eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); + // create subscriber for topic "foo" + eCAL::CSubscriber sub("foo"); - // create publisher for topic "A" - eCAL::CPublisher pub("A"); + // create publisher for topic "foo" + eCAL::CPublisher pub("foo"); // add callback EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); @@ -402,7 +401,7 @@ TEST(IO, SimpleMessageCB) eCAL::Finalize(); } -TEST(IO, DynamicSizeCB) +TEST(PubSub, DynamicSizeCB) { // default send string std::string send_s = CreatePayLoad(PAYLOAD_SIZE); @@ -413,11 +412,11 @@ TEST(IO, DynamicSizeCB) // publish / subscribe match in the same process eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); + // create subscriber for topic "foo" + eCAL::CSubscriber sub("foo"); - // create publisher for topic "A" - eCAL::CPublisher pub("A"); + // create publisher for topic "foo" + eCAL::CPublisher pub("foo"); // add callback EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); @@ -458,7 +457,7 @@ TEST(IO, DynamicSizeCB) eCAL::Finalize(); } -TEST(IO, DynamicCreate) +TEST(PubSub, DynamicCreate) { // default send string std::string send_s = CreatePayLoad(PAYLOAD_SIZE); @@ -469,13 +468,13 @@ TEST(IO, DynamicCreate) // publish / subscribe match in the same process eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" + // create subscriber for topic "foo" eCAL::CSubscriber* sub; - sub = new eCAL::CSubscriber("A"); + sub = new eCAL::CSubscriber("foo"); - // create publisher for topic "A" + // create publisher for topic "foo" eCAL::CPublisher* pub; - pub = new eCAL::CPublisher("A"); + pub = new eCAL::CPublisher("foo"); // add callback EXPECT_EQ(true, sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); @@ -497,8 +496,8 @@ TEST(IO, DynamicCreate) delete sub; sub = nullptr; - // create subscriber for topic "A" - sub = new eCAL::CSubscriber("A"); + // create subscriber for topic "foo" + sub = new eCAL::CSubscriber("foo"); // add callback EXPECT_EQ(true, sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); @@ -519,8 +518,8 @@ TEST(IO, DynamicCreate) // destroy subscriber sub->Destroy(); - // create subscriber for topic "A" - sub->Create("A"); + // create subscriber for topic "foo" + sub->Create("foo"); // add callback EXPECT_EQ(true, sub->AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); @@ -550,7 +549,7 @@ TEST(IO, DynamicCreate) eCAL::Finalize(); } -TEST(IO, ZeroPayloadMessageInProc) +TEST(PubSub, ZeroPayloadMessageUDP) { // default send string std::string send_s; @@ -561,13 +560,11 @@ TEST(IO, ZeroPayloadMessageInProc) // publish / subscribe match in the same process eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); + // create subscriber for topic "foo" + eCAL::CSubscriber sub("foo"); - // create publisher for topic "A" - eCAL::CPublisher pub("A"); - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - pub.SetLayerMode(eCAL::TLayer::tlayer_inproc, eCAL::TLayer::smode_on); + // create publisher for topic "foo" + eCAL::CPublisher pub("foo"); // add callback EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); @@ -598,10 +595,12 @@ TEST(IO, ZeroPayloadMessageInProc) eCAL::Finalize(); } -TEST(IO, ZeroPayloadMessageSHM) +TEST(PubSub, MultipleSendsUDP) { // default send string - std::string send_s; + std::vector send_vector{ "this", "is", "a", "", "testtest" }; + std::string last_received_msg; + long long last_received_timestamp; // initialize eCAL API eCAL::Initialize(0, nullptr, "pubsub_test"); @@ -609,32 +608,31 @@ TEST(IO, ZeroPayloadMessageSHM) // publish / subscribe match in the same process eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); + // create subscriber for topic "foo" + eCAL::string::CSubscriber sub("foo"); - // create publisher for topic "A" - eCAL::CPublisher pub("A"); - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - pub.SetLayerMode(eCAL::TLayer::tlayer_shm, eCAL::TLayer::smode_on); + // create publisher for topic "foo" + eCAL::string::CPublisher pub("foo"); // add callback - EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); + auto save_data = [&last_received_msg, &last_received_timestamp](const char* /*topic_name_*/, const std::string& msg_, long long time_, long long /*clock_*/, long long /*id_*/) + { + last_received_msg = msg_; + last_received_timestamp = time_; + }; + EXPECT_TRUE(sub.AddReceiveCallback(save_data)); // let's match them eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); - - g_callback_received_bytes = 0; - g_callback_received_count = 0; - - EXPECT_EQ(send_s.size(), pub.Send(send_s)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); - - EXPECT_EQ(send_s.size(), pub.Send(nullptr, 0)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); - - // check callback receive - EXPECT_EQ(send_s.size(), g_callback_received_bytes); - EXPECT_EQ(2, g_callback_received_count); + long long timestamp = 1; + for (const auto& elem : send_vector) + { + pub.Send(elem, timestamp); + eCAL::Process::SleepMS(DATA_FLOW_TIME); + EXPECT_EQ(last_received_msg, elem); + EXPECT_EQ(last_received_timestamp, timestamp); + ++timestamp; + } // destroy subscriber sub.Destroy(); @@ -646,100 +644,138 @@ TEST(IO, ZeroPayloadMessageSHM) eCAL::Finalize(); } -TEST(IO, ZeroPayloadMessageUDP) +TEST(PubSub, DestroyInCallback) { - // default send string - std::string send_s; + /* Test setup : + * 2 pair of pub_sub connections ("foo" and "destroy") + * "foo" publisher sends message every 100ms + * "destroy" publisher sends destroy message after 2 seconds, which will destroy foo subscriber in its callback + * Test ensures that this does not create a deadlock or other unintended situation. + */ // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); + eCAL::Initialize(0, nullptr, "New Publisher in Callback"); - // publish / subscribe match in the same process + // enable loop back communication in the same thread eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); - - // create publisher for topic "A" - eCAL::CPublisher pub("A"); - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - pub.SetLayerMode(eCAL::TLayer::tlayer_udp_mc, eCAL::TLayer::smode_on); - - // add callback - EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); - - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); - - g_callback_received_bytes = 0; - g_callback_received_count = 0; - - EXPECT_EQ(send_s.size(), pub.Send(send_s)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); - - EXPECT_EQ(send_s.size(), pub.Send(nullptr, 0)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); - - // check callback receive - EXPECT_EQ(send_s.size(), g_callback_received_bytes); - EXPECT_EQ(2, g_callback_received_count); - - // destroy subscriber - sub.Destroy(); - - // destroy publisher - pub.Destroy(); + // start publishing thread + eCAL::string::CPublisher pub_foo("foo"); + eCAL::string::CSubscriber sub_foo("foo"); + + eCAL::string::CPublisher pub_destroy("destroy"); + eCAL::string::CSubscriber sub_destroy("destroy"); + std::atomic destroyed(false); + + auto destroy_lambda = [&sub_foo, &destroyed](const char* /*topic_*/, const std::string& /*msg*/, long long /*time_*/, long long /*clock_*/, long long /*id_*/) { + std::cout << "Receive destroy command" << std::endl; + sub_foo.Destroy(); + destroyed = true; + std::cout << "Finnished destroying" << std::endl; + }; + sub_destroy.AddReceiveCallback(destroy_lambda); + + auto receive_lambda = [](const char* /*topic_*/, const std::string& /*msg*/, long long /*time_*/, long long /*clock_*/, long long /*id_*/) { + std::cout << "Hello" << std::endl; + }; + sub_foo.AddReceiveCallback(receive_lambda); + + // sleep for 2 seconds, registration should be good! + std::this_thread::sleep_for(std::chrono::seconds(2)); + + std::thread pub_foo_t([&pub_foo, &destroyed]() { + while (!destroyed) + { + pub_foo.Send("Hello World"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + std::cout << "Stopped sending foo" << std::endl; + }); + + std::thread pub_destroy_t([&pub_destroy]() { + // sleep for two second, then send the destroy command + std::this_thread::sleep_for(std::chrono::seconds(2)); + std::cout << "Sending destroy message" << std::endl; + pub_destroy.Send("Destroy"); + std::cout << "Done sending destroy message" << std::endl; + }); + + + pub_foo_t.join(); + pub_destroy_t.join(); // finalize eCAL API + // without destroying any pub / sub eCAL::Finalize(); } -#if 0 -TEST(IO, ZeroPayloadMessageTCP) +TEST(PubSub, SubscriberReconnection) { - // default send string - std::string send_s; + /* Test setup : + * publisher runs permanently in a thread + * subscriber start reading + * subscriber gets out of scope (destruction) + * subscriber starts again in a new scope + * Test ensures that subscriber is reconnecting and all sync mechanism are working properly again. + */ // initialize eCAL API - eCAL::Initialize(0, nullptr, "pubsub_test"); + eCAL::Initialize(0, nullptr, "SubscriberReconnection"); - // publish / subscribe match in the same process + // enable loop back communication in the same thread eCAL::Util::EnableLoopback(true); - // create subscriber for topic "A" - eCAL::CSubscriber sub("A"); - - // create publisher for topic "A" - eCAL::CPublisher pub("A"); - pub.SetLayerMode(eCAL::TLayer::tlayer_all, eCAL::TLayer::smode_off); - pub.SetLayerMode(eCAL::TLayer::tlayer_tcp, eCAL::TLayer::smode_on); + // start publishing thread + std::atomic stop_publishing(false); + eCAL::string::CPublisher pub_foo("foo"); + std::thread pub_foo_t([&pub_foo, &stop_publishing]() { + while (!stop_publishing) + { + pub_foo.Send("Hello World"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + std::cout << "Stopped publishing" << std::endl; + }); + + // scope 1 + { + size_t callback_received_count(0); - // add callback - EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); + eCAL::string::CSubscriber sub_foo("foo"); + auto receive_lambda = [&sub_foo, &callback_received_count](const char* /*topic_*/, const std::string& /*msg*/, long long /*time_*/, long long /*clock_*/, long long /*id_*/) { + std::cout << "Receiving in scope 1" << std::endl; + callback_received_count++; + }; + sub_foo.AddReceiveCallback(receive_lambda); - // let's match them - eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH); + // sleep for 2 seconds, we should receive something + std::this_thread::sleep_for(std::chrono::seconds(2)); - g_callback_received_bytes = 0; - g_callback_received_count = 0; + EXPECT_TRUE(callback_received_count > 0); + } - EXPECT_EQ(send_s.size(), pub.Send(send_s)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); + // scope 2 + { + size_t callback_received_count(0); - EXPECT_EQ(send_s.size(), pub.Send(nullptr, 0)); - eCAL::Process::SleepMS(DATA_FLOW_TIME); + eCAL::string::CSubscriber sub_foo("foo"); + auto receive_lambda = [&sub_foo, &callback_received_count](const char* /*topic_*/, const std::string& /*msg*/, long long /*time_*/, long long /*clock_*/, long long /*id_*/) { + std::cout << "Receiving in scope 2" << std::endl; + callback_received_count++; + }; + sub_foo.AddReceiveCallback(receive_lambda); - // check callback receive - EXPECT_EQ(send_s.size(), g_callback_received_bytes); - EXPECT_EQ(2, g_callback_received_count); + // sleep for 2 seconds, we should receive something + std::this_thread::sleep_for(std::chrono::seconds(2)); - // destroy subscriber - sub.Destroy(); + EXPECT_TRUE(callback_received_count > 0); + } - // destroy publisher - pub.Destroy(); + // stop publishing and join thread + stop_publishing = true; + pub_foo_t.join(); // finalize eCAL API + // without destroying any pub / sub eCAL::Finalize(); } -#endif diff --git a/tests/serialization_test/CMakeLists.txt b/tests/serialization_test/CMakeLists.txt new file mode 100644 index 0000000..a9ebc23 --- /dev/null +++ b/tests/serialization_test/CMakeLists.txt @@ -0,0 +1,113 @@ +# ========================= 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 ================================= + +project(test_serialization) + +find_package(Threads REQUIRED) +find_package(GTest REQUIRED) + +set(nanopb_lib_src + ../../src/core/src/serialization/nanopb/nanopb/pb.h + ../../src/core/src/serialization/nanopb/nanopb/pb_common.c + ../../src/core/src/serialization/nanopb/nanopb/pb_common.h + ../../src/core/src/serialization/nanopb/nanopb/pb_decode.c + ../../src/core/src/serialization/nanopb/nanopb/pb_decode.h + ../../src/core/src/serialization/nanopb/nanopb/pb_encode.c + ../../src/core/src/serialization/nanopb/nanopb/pb_encode.h +) + +set(nanopb_generated_src + ../../src/core/src/serialization/nanopb/ecal.pb.c + ../../src/core/src/serialization/nanopb/ecal.pb.h + ../../src/core/src/serialization/nanopb/host.pb.c + ../../src/core/src/serialization/nanopb/host.pb.h + ../../src/core/src/serialization/nanopb/layer.pb.c + ../../src/core/src/serialization/nanopb/layer.pb.h + ../../src/core/src/serialization/nanopb/logging.pb.c + ../../src/core/src/serialization/nanopb/logging.pb.h + ../../src/core/src/serialization/nanopb/monitoring.pb.c + ../../src/core/src/serialization/nanopb/monitoring.pb.h + ../../src/core/src/serialization/nanopb/process.pb.c + ../../src/core/src/serialization/nanopb/process.pb.h + ../../src/core/src/serialization/nanopb/service.pb.c + ../../src/core/src/serialization/nanopb/service.pb.h + ../../src/core/src/serialization/nanopb/topic.pb.c + ../../src/core/src/serialization/nanopb/topic.pb.h +) + +set(ecal_serialize_src + ../../src/core/src/serialization/ecal_serialize_common.cpp + ../../src/core/src/serialization/ecal_serialize_common.h + ../../src/core/src/serialization/ecal_serialize_logging.cpp + ../../src/core/src/serialization/ecal_serialize_logging.h + ../../src/core/src/serialization/ecal_serialize_monitoring.cpp + ../../src/core/src/serialization/ecal_serialize_monitoring.h + ../../src/core/src/serialization/ecal_serialize_sample_payload.cpp + ../../src/core/src/serialization/ecal_serialize_sample_payload.h + ../../src/core/src/serialization/ecal_serialize_sample_registration.cpp + ../../src/core/src/serialization/ecal_serialize_sample_registration.h + ../../src/core/src/serialization/ecal_serialize_service.cpp + ../../src/core/src/serialization/ecal_serialize_service.h + ../../src/core/src/serialization/ecal_struct_logging.h + ../../src/core/src/serialization/ecal_struct_sample_common.h + ../../src/core/src/serialization/ecal_struct_sample_payload.h + ../../src/core/src/serialization/ecal_struct_sample_registration.h + ../../src/core/src/serialization/ecal_struct_service.h +) + +set(unit_test_src + src/common_generate.cpp + src/logging_compare.cpp + src/logging_generate.cpp + src/logging_serialization_test.cpp + src/monitoring_compare.cpp + src/monitoring_generate.cpp + src/monitoring_serialization_test.cpp + src/payload_compare.cpp + src/payload_generate.cpp + src/payload_serialization_test.cpp + src/registration_compare.cpp + src/registration_generate.cpp + src/registration_serialization_test.cpp + src/service_compare.cpp + src/service_generate.cpp + src/service_serialization_test.cpp +) + +source_group("nanopb/lib" FILES ${nanopb_lib_src}) +source_group("nanopb/generated" FILES ${nanopb_generated_src}) +source_group("ecal_serialize" FILES ${ecal_serialize_src}) + +ecal_add_gtest(${PROJECT_NAME} ${nanopb_lib_src} ${nanopb_generated_src} ${ecal_serialize_src} ${unit_test_src}) + +target_include_directories(${PROJECT_NAME} + PRIVATE + $ + $ + $ +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + Threads::Threads) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_gtest(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) diff --git a/tests/serialization_test/src/common_generate.cpp b/tests/serialization_test/src/common_generate.cpp new file mode 100644 index 0000000..ab7eab7 --- /dev/null +++ b/tests/serialization_test/src/common_generate.cpp @@ -0,0 +1,39 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include + +namespace eCAL +{ + // Generate a random string of given length (may contain null character) + std::string GenerateString(size_t length) + { + const std::string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + std::string result; + result.reserve(length); + + for (size_t i = 0; i < length; ++i) { + // use the full charset, including null character + result.push_back(charset[rand() % (charset.length() + 1)]); + } + + return result; + } +} diff --git a/ecal/core/src/io/udp_init.h b/tests/serialization_test/src/logging_compare.cpp similarity index 58% rename from ecal/core/src/io/udp_init.h rename to tests/serialization_test/src/logging_compare.cpp index a967838..3b10bd0 100644 --- a/ecal/core/src/io/udp_init.h +++ b/tests/serialization_test/src/logging_compare.cpp @@ -17,17 +17,22 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @brief UDP initialization -**/ - -#pragma once +#include "../../serialization/ecal_struct_logging.h" namespace eCAL { - namespace Net + namespace Logging { - int Initialize(); - int Finalize(); + // compare two LogMessages for equality + bool CompareLogMessages(const LogMessage& message1, const LogMessage& message2) + { + return (message1.time == message2.time && + message1.hname == message2.hname && + message1.pid == message2.pid && + message1.pname == message2.pname && + message1.uname == message2.uname && + message1.level == message2.level && + message1.content == message2.content); + } } } diff --git a/tests/serialization_test/src/logging_generate.cpp b/tests/serialization_test/src/logging_generate.cpp new file mode 100644 index 0000000..91eeddf --- /dev/null +++ b/tests/serialization_test/src/logging_generate.cpp @@ -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 ================================= +*/ + +#include "../../serialization/ecal_struct_logging.h" + +#include + +namespace eCAL +{ + std::string GenerateString(size_t length); + + namespace Logging + { + LogMessage GenerateLogMessage() + { + LogMessage logMessage; + logMessage.time = rand() % 1000; + logMessage.hname = GenerateString(10); + logMessage.pid = rand() % 1000; + logMessage.pname = GenerateString(8); + logMessage.uname = GenerateString(6); + logMessage.level = static_cast(rand() % 2); + logMessage.content = GenerateString(50); + + return logMessage; + } + } +} diff --git a/tests/serialization_test/src/logging_serialization_test.cpp b/tests/serialization_test/src/logging_serialization_test.cpp new file mode 100644 index 0000000..41741d8 --- /dev/null +++ b/tests/serialization_test/src/logging_serialization_test.cpp @@ -0,0 +1,92 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_serialize_logging.h" + +#include + +namespace eCAL +{ + namespace Logging + { + LogMessage GenerateLogMessage(); + bool CompareLogMessages(const LogMessage& message1, const LogMessage& message2); + + TEST(Serialization, LogMessage2String) + { + LogMessage message_in = GenerateLogMessage(); + + std::string message_buffer; + ASSERT_TRUE(SerializeToBuffer(message_in, message_buffer)); + + LogMessage message_out; + ASSERT_TRUE(DeserializeFromBuffer(message_buffer.data(), message_buffer.size(), message_out)); + + ASSERT_TRUE(CompareLogMessages(message_in, message_out)); + } + + TEST(Serialization, LogMessage2Vector) + { + LogMessage message_in = GenerateLogMessage(); + + std::vector message_buffer; + ASSERT_TRUE(SerializeToBuffer(message_in, message_buffer)); + + LogMessage message_out; + ASSERT_TRUE(DeserializeFromBuffer(message_buffer.data(), message_buffer.size(), message_out)); + + ASSERT_TRUE(CompareLogMessages(message_in, message_out)); + } + + + TEST(Serialization, LogMessageList2String) + { + LogMessageList message_list_in; + message_list_in.log_messages.push_back(GenerateLogMessage()); + message_list_in.log_messages.push_back(GenerateLogMessage()); + message_list_in.log_messages.push_back(GenerateLogMessage()); + + std::string sample_buffer; + ASSERT_TRUE(SerializeToBuffer(message_list_in, sample_buffer)); + + LogMessageList message_list_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), message_list_out)); + + ASSERT_TRUE(message_list_in.log_messages.size() == message_list_out.log_messages.size()); + ASSERT_TRUE(std::equal(message_list_in.log_messages.begin(), message_list_in.log_messages.end(), message_list_out.log_messages.begin(), CompareLogMessages)); + } + + TEST(Serialization, LogMessageList2Vector) + { + LogMessageList message_list_in; + message_list_in.log_messages.push_back(GenerateLogMessage()); + message_list_in.log_messages.push_back(GenerateLogMessage()); + message_list_in.log_messages.push_back(GenerateLogMessage()); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(message_list_in, sample_buffer)); + + LogMessageList message_list_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), message_list_out)); + + ASSERT_TRUE(message_list_in.log_messages.size() == message_list_out.log_messages.size()); + ASSERT_TRUE(std::equal(message_list_in.log_messages.begin(), message_list_in.log_messages.end(), message_list_out.log_messages.begin(), CompareLogMessages)); + } + } +} diff --git a/tests/serialization_test/src/monitoring_compare.cpp b/tests/serialization_test/src/monitoring_compare.cpp new file mode 100644 index 0000000..905c404 --- /dev/null +++ b/tests/serialization_test/src/monitoring_compare.cpp @@ -0,0 +1,188 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +namespace eCAL +{ + namespace Monitoring + { + // compare two monitoring structs + bool CompareMonitorings(const SMonitoring& monitoring1, const SMonitoring& monitoring2) + { + // compare process info + if (monitoring1.processes.size() != monitoring2.processes.size()) + { + return false; + } + + for (size_t i = 0; i < monitoring1.processes.size(); ++i) + { + if (monitoring1.processes[i].rclock != monitoring2.processes[i].rclock || + monitoring1.processes[i].hname != monitoring2.processes[i].hname || + monitoring1.processes[i].hgname != monitoring2.processes[i].hgname || + monitoring1.processes[i].pid != monitoring2.processes[i].pid || + monitoring1.processes[i].pname != monitoring2.processes[i].pname || + monitoring1.processes[i].uname != monitoring2.processes[i].uname || + monitoring1.processes[i].pparam != monitoring2.processes[i].pparam || + monitoring1.processes[i].datawrite != monitoring2.processes[i].datawrite || + monitoring1.processes[i].dataread != monitoring2.processes[i].dataread || + monitoring1.processes[i].state_severity != monitoring2.processes[i].state_severity || + monitoring1.processes[i].state_severity_level != monitoring2.processes[i].state_severity_level || + monitoring1.processes[i].state_info != monitoring2.processes[i].state_info || + monitoring1.processes[i].tsync_state != monitoring2.processes[i].tsync_state || + monitoring1.processes[i].tsync_mod_name != monitoring2.processes[i].tsync_mod_name || + monitoring1.processes[i].component_init_state != monitoring2.processes[i].component_init_state || + monitoring1.processes[i].component_init_info != monitoring2.processes[i].component_init_info || + monitoring1.processes[i].ecal_runtime_version != monitoring2.processes[i].ecal_runtime_version) + { + return false; + } + } + + // compare publisher info + if (monitoring1.publisher.size() != monitoring2.publisher.size()) + { + return false; + } + + for (size_t i = 0; i < monitoring1.publisher.size(); ++i) + { + if (monitoring1.publisher[i].rclock != monitoring2.publisher[i].rclock || + monitoring1.publisher[i].hname != monitoring2.publisher[i].hname || + monitoring1.publisher[i].hgname != monitoring2.publisher[i].hgname || + monitoring1.publisher[i].pid != monitoring2.publisher[i].pid || + monitoring1.publisher[i].pname != monitoring2.publisher[i].pname || + monitoring1.publisher[i].uname != monitoring2.publisher[i].uname || + monitoring1.publisher[i].tid != monitoring2.publisher[i].tid || + monitoring1.publisher[i].tname != monitoring2.publisher[i].tname || + monitoring1.publisher[i].direction != monitoring2.publisher[i].direction || + monitoring1.publisher[i].tdatatype.name != monitoring2.publisher[i].tdatatype.name || + monitoring1.publisher[i].tdatatype.encoding != monitoring2.publisher[i].tdatatype.encoding || + monitoring1.publisher[i].tdatatype.descriptor != monitoring2.publisher[i].tdatatype.descriptor || + monitoring1.publisher[i].tlayer.size() != monitoring2.publisher[i].tlayer.size() || + monitoring1.publisher[i].tsize != monitoring2.publisher[i].tsize || + monitoring1.publisher[i].connections_loc != monitoring2.publisher[i].connections_loc || + monitoring1.publisher[i].connections_ext != monitoring2.publisher[i].connections_ext || + monitoring1.publisher[i].message_drops != monitoring2.publisher[i].message_drops || + monitoring1.publisher[i].did != monitoring2.publisher[i].did || + monitoring1.publisher[i].dclock != monitoring2.publisher[i].dclock || + monitoring1.publisher[i].dfreq != monitoring2.publisher[i].dfreq || + monitoring1.publisher[i].attr != monitoring2.publisher[i].attr) + { + return false; + } + } + + // compare subscriber info + if (monitoring1.subscriber.size() != monitoring2.subscriber.size()) + { + return false; + } + + for (size_t i = 0; i < monitoring1.subscriber.size(); ++i) + { + if (monitoring1.subscriber[i].rclock != monitoring2.subscriber[i].rclock || + monitoring1.subscriber[i].hname != monitoring2.subscriber[i].hname || + monitoring1.subscriber[i].hgname != monitoring2.subscriber[i].hgname || + monitoring1.subscriber[i].pid != monitoring2.subscriber[i].pid || + monitoring1.subscriber[i].pname != monitoring2.subscriber[i].pname || + monitoring1.subscriber[i].uname != monitoring2.subscriber[i].uname || + monitoring1.subscriber[i].tid != monitoring2.subscriber[i].tid || + monitoring1.subscriber[i].tname != monitoring2.subscriber[i].tname || + monitoring1.subscriber[i].direction != monitoring2.subscriber[i].direction || + monitoring1.subscriber[i].tdatatype.name != monitoring2.subscriber[i].tdatatype.name || + monitoring1.subscriber[i].tdatatype.encoding != monitoring2.subscriber[i].tdatatype.encoding || + monitoring1.subscriber[i].tdatatype.descriptor != monitoring2.subscriber[i].tdatatype.descriptor || + monitoring1.subscriber[i].tlayer.size() != monitoring2.subscriber[i].tlayer.size() || + monitoring1.subscriber[i].tsize != monitoring2.subscriber[i].tsize || + monitoring1.subscriber[i].connections_loc != monitoring2.subscriber[i].connections_loc || + monitoring1.subscriber[i].connections_ext != monitoring2.subscriber[i].connections_ext || + monitoring1.subscriber[i].message_drops != monitoring2.subscriber[i].message_drops || + monitoring1.subscriber[i].did != monitoring2.subscriber[i].did || + monitoring1.subscriber[i].dclock != monitoring2.subscriber[i].dclock || + monitoring1.subscriber[i].dfreq != monitoring2.subscriber[i].dfreq || + monitoring1.subscriber[i].attr != monitoring2.subscriber[i].attr) + { + return false; + } + } + + // compare server info + if (monitoring1.server.size() != monitoring2.server.size()) + { + return false; + } + + for (size_t i = 0; i < monitoring1.server.size(); ++i) + { + if (monitoring1.server[i].rclock != monitoring2.server[i].rclock || + monitoring1.server[i].hname != monitoring2.server[i].hname || + monitoring1.server[i].pname != monitoring2.server[i].pname || + monitoring1.server[i].uname != monitoring2.server[i].uname || + monitoring1.server[i].pid != monitoring2.server[i].pid || + monitoring1.server[i].sname != monitoring2.server[i].sname || + monitoring1.server[i].sid != monitoring2.server[i].sid || + monitoring1.server[i].version != monitoring2.server[i].version || + monitoring1.server[i].tcp_port_v0 != monitoring2.server[i].tcp_port_v0 || + monitoring1.server[i].tcp_port_v1 != monitoring2.server[i].tcp_port_v1 || + monitoring1.server[i].methods.size() != monitoring2.server[i].methods.size()) + { + return false; + } + + for (size_t j = 0; j < monitoring1.server[i].methods.size(); ++j) + { + if (monitoring1.server[i].methods[j].mname != monitoring2.server[i].methods[j].mname || + monitoring1.server[i].methods[j].req_type != monitoring2.server[i].methods[j].req_type || + monitoring1.server[i].methods[j].req_desc != monitoring2.server[i].methods[j].req_desc || + monitoring1.server[i].methods[j].resp_type != monitoring2.server[i].methods[j].resp_type || + monitoring1.server[i].methods[j].resp_desc != monitoring2.server[i].methods[j].resp_desc || + monitoring1.server[i].methods[j].call_count != monitoring2.server[i].methods[j].call_count) + { + return false; + } + } + } + + // compare client info + if (monitoring1.clients.size() != monitoring2.clients.size()) + { + return false; + } + + for (size_t i = 0; i < monitoring1.clients.size(); ++i) + { + if (monitoring1.clients[i].rclock != monitoring2.clients[i].rclock || + monitoring1.clients[i].hname != monitoring2.clients[i].hname || + monitoring1.clients[i].pname != monitoring2.clients[i].pname || + monitoring1.clients[i].uname != monitoring2.clients[i].uname || + monitoring1.clients[i].pid != monitoring2.clients[i].pid || + monitoring1.clients[i].sname != monitoring2.clients[i].sname || + monitoring1.clients[i].sid != monitoring2.clients[i].sid || + monitoring1.clients[i].version != monitoring2.clients[i].version) + { + return false; + } + } + + return true; + } + } +} diff --git a/tests/serialization_test/src/monitoring_generate.cpp b/tests/serialization_test/src/monitoring_generate.cpp new file mode 100644 index 0000000..f2dbcbd --- /dev/null +++ b/tests/serialization_test/src/monitoring_generate.cpp @@ -0,0 +1,161 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include + +namespace eCAL +{ + std::string GenerateString(size_t length); + + namespace Monitoring + { + // generate process + SProcessMon GenerateProcess() + { + SProcessMon process; + process.rclock = rand() % 1000; + process.hname = GenerateString(10); + process.hgname = GenerateString(8); + process.pid = rand() % 1000; + process.pname = GenerateString(8); + process.uname = GenerateString(6); + process.pparam = GenerateString(20); + process.datawrite = rand() % 10000; + process.dataread = rand() % 10000; + process.state_severity = rand() % 5; + process.state_severity_level = rand() % 6; + process.state_info = GenerateString(15); + process.tsync_state = rand() % 3; + process.tsync_mod_name = GenerateString(12); + process.component_init_state = rand() % 10; + process.component_init_info = GenerateString(20); + process.ecal_runtime_version = GenerateString(8); + return process; + } + + // generate topic + STopicMon GenerateTopic(const std::string& direction) + { + STopicMon publisher; + publisher.rclock = rand() % 1000; + publisher.hname = GenerateString(10); + publisher.hgname = GenerateString(8); + publisher.pid = rand() % 1000; + publisher.pname = GenerateString(8); + publisher.uname = GenerateString(6); + publisher.tid = GenerateString(8); + publisher.tname = GenerateString(10); + publisher.direction = direction; + publisher.tdatatype.name = GenerateString(8); + publisher.tdatatype.encoding = GenerateString(8); + publisher.tdatatype.descriptor = GenerateString(10); + publisher.tlayer.push_back({ tl_ecal_shm, 1, true }); + publisher.tsize = rand() % 5000; + publisher.connections_loc = rand() % 10; + publisher.connections_ext = rand() % 10; + publisher.message_drops = rand() % 100; + publisher.did = rand() % 10000; + publisher.dclock = rand() % 10000; + publisher.dfreq = rand() % 100; + return publisher; + } + + // generate servicemethod + SMethodMon GenerateServiceMethod() + { + SMethodMon method; + method.mname = GenerateString(8); + method.req_type = GenerateString(8); + method.req_desc = GenerateString(10); + method.resp_type = GenerateString(8); + method.resp_desc = GenerateString(10); + method.call_count = rand() % 10000; + return method; + } + + // generate service + SServerMon GenerateService() + { + SServerMon server; + server.rclock = rand() % 1000; + server.hname = GenerateString(10); + server.pname = GenerateString(8); + server.uname = GenerateString(6); + server.pid = rand() % 1000; + server.sname = GenerateString(10); + server.sid = GenerateString(8); + server.version = rand() % 100; + server.tcp_port_v0 = rand() % 65536; + server.tcp_port_v1 = rand() % 65536; + + server.methods.push_back(GenerateServiceMethod()); + server.methods.push_back(GenerateServiceMethod()); + server.methods.push_back(GenerateServiceMethod()); + return server; + } + + // generate client + SClientMon GenerateClient() + { + SClientMon client; + client.rclock = rand() % 1000; + client.hname = GenerateString(10); + client.pname = GenerateString(8); + client.uname = GenerateString(6); + client.pid = rand() % 1000; + client.sname = GenerateString(10); + client.sid = GenerateString(8); + client.version = rand() % 100; + return client; + } + + // generate monitoring + SMonitoring GenerateMonitoring() + { + SMonitoring monitoring; + + // generate process + monitoring.processes.push_back(GenerateProcess()); + monitoring.processes.push_back(GenerateProcess()); + monitoring.processes.push_back(GenerateProcess()); + + // generate topics + monitoring.publisher.push_back(GenerateTopic("publisher")); + monitoring.publisher.push_back(GenerateTopic("publisher")); + monitoring.publisher.push_back(GenerateTopic("publisher")); + monitoring.subscriber.push_back(GenerateTopic("subscriber")); + monitoring.subscriber.push_back(GenerateTopic("subscriber")); + monitoring.subscriber.push_back(GenerateTopic("subscriber")); + + // generate services + monitoring.server.push_back(GenerateService()); + monitoring.server.push_back(GenerateService()); + monitoring.server.push_back(GenerateService()); + + // generate clients + monitoring.clients.push_back(GenerateClient()); + monitoring.clients.push_back(GenerateClient()); + monitoring.clients.push_back(GenerateClient()); + + return monitoring; + } + } +} diff --git a/tests/serialization_test/src/monitoring_serialization_test.cpp b/tests/serialization_test/src/monitoring_serialization_test.cpp new file mode 100644 index 0000000..3a0cfbd --- /dev/null +++ b/tests/serialization_test/src/monitoring_serialization_test.cpp @@ -0,0 +1,57 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_serialize_monitoring.h" + +#include + +namespace eCAL +{ + namespace Monitoring + { + SMonitoring GenerateMonitoring(); + bool CompareMonitorings(const SMonitoring& monitoring1, const SMonitoring& monitoring2); + + TEST(Serialization, Monitoring2String) + { + SMonitoring message_in = GenerateMonitoring(); + + std::string message_buffer; + ASSERT_TRUE(SerializeToBuffer(message_in, message_buffer)); + + SMonitoring message_out; + ASSERT_TRUE(DeserializeFromBuffer(message_buffer.data(), message_buffer.size(), message_out)); + + ASSERT_TRUE(CompareMonitorings(message_in, message_out)); + } + + TEST(Serialization, Monitoring2Vector) + { + SMonitoring message_in = GenerateMonitoring(); + + std::vector message_buffer; + ASSERT_TRUE(SerializeToBuffer(message_in, message_buffer)); + + SMonitoring message_out; + ASSERT_TRUE(DeserializeFromBuffer(message_buffer.data(), message_buffer.size(), message_out)); + + ASSERT_TRUE(CompareMonitorings(message_in, message_out)); + } + } +} diff --git a/tests/serialization_test/src/payload_compare.cpp b/tests/serialization_test/src/payload_compare.cpp new file mode 100644 index 0000000..9be97ab --- /dev/null +++ b/tests/serialization_test/src/payload_compare.cpp @@ -0,0 +1,82 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_struct_sample_payload.h" + +namespace eCAL +{ + namespace Payload + { + // compare two samples for equality + bool ComparePayloadSamples(const Sample& sample1, const Sample& sample2) + { + // compare cmd_type + if (sample1.cmd_type != sample2.cmd_type) { + return false; + } + + // compare topic + if (sample1.topic.hname != sample2.topic.hname || + sample1.topic.tid != sample2.topic.tid || + sample1.topic.tname != sample2.topic.tname) { + return false; + } + + // compare content + if (sample1.content.id != sample2.content.id || + sample1.content.clock != sample2.content.clock || + sample1.content.time != sample2.content.time || + sample1.content.hash != sample2.content.hash || + sample1.content.size != sample2.content.size) { + return false; + } + + // deserialized payloads (sample2) must always use payload type 'pl_vec' + if (sample2.content.payload.type != pl_vec) { + return false; + } + + if ((sample1.content.payload.type != pl_none) && + (sample2.content.payload.type != pl_none)) + { + // extract payload1 + std::vector payload1_vec; + switch (sample1.content.payload.type) + { + case pl_none: + break; + case pl_raw: + payload1_vec = std::vector(sample1.content.payload.raw_addr, sample1.content.payload.raw_addr + sample1.content.payload.raw_size); + break; + case pl_vec: + payload1_vec = sample1.content.payload.vec; + break; + } + + // compare payloads + if (payload1_vec != sample2.content.payload.vec) { + return false; + } + } + + // all comparisons passed, samples are equal + return true; + } + } +} diff --git a/tests/serialization_test/src/payload_generate.cpp b/tests/serialization_test/src/payload_generate.cpp new file mode 100644 index 0000000..9ebf01f --- /dev/null +++ b/tests/serialization_test/src/payload_generate.cpp @@ -0,0 +1,112 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_struct_sample_payload.h" + +#include + +namespace eCAL +{ + std::string GenerateString(size_t length); + + namespace Payload + { + // generate Topic + Topic GenerateTopic() + { + Topic topic; + topic.hname = GenerateString(8); + topic.tid = GenerateString(5); + topic.tname = GenerateString(10); + + return topic; + } + + // generate Payload + Payload GeneratePayload(const char* payload_addr, size_t payload_size) + { + Payload payload; + payload.type = pl_raw; + payload.raw_addr = payload_addr; + payload.raw_size = payload_size; + + return payload; + } + + // generate Payload + Payload GeneratePayload(const std::vector& payload_vec) + { + Payload payload; + payload.type = pl_vec; + payload.vec = payload_vec; + + return payload; + } + + // generate Content + Content GenerateContent(const char* payload_addr, size_t payload_size) + { + Content content; + content.id = rand() % 100; + content.clock = rand() % 1000; + content.time = rand() % 10000; + content.hash = rand() % 100000; + content.size = rand() % 50; + content.payload = GeneratePayload(payload_addr, payload_size); + + return content; + } + + // generate Content + Content GenerateContent(const std::vector& payload_vec) + { + Content content; + content.id = rand() % 100; + content.clock = rand() % 1000; + content.time = rand() % 10000; + content.hash = rand() % 100000; + content.size = rand() % 50; + content.payload = GeneratePayload(payload_vec); + + return content; + } + + // generate Payload Sample (payload raw pointer + size) + 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); + + return sample; + } + + // generate Payload Sample (payload vector) + 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); + + return sample; + } + } +} diff --git a/tests/serialization_test/src/payload_serialization_test.cpp b/tests/serialization_test/src/payload_serialization_test.cpp new file mode 100644 index 0000000..0a17953 --- /dev/null +++ b/tests/serialization_test/src/payload_serialization_test.cpp @@ -0,0 +1,144 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_serialize_sample_payload.h" + +#include +#include + +namespace +{ + void InitializeVec(std::vector& vec, size_t size) + { + // initialize random number generator + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 255); // Range for char values + + // fill the vector with random values + vec.resize(size); + for (char& element : vec) + { + element = static_cast(dis(gen)); + } + } +} + +namespace eCAL +{ + namespace Payload + { + Sample GeneratePayloadSample(const char* payload_addr, size_t payload_size); + Sample GeneratePayloadSample(const std::vector& payload_vec); + + bool ComparePayloadSamples(const Sample& sample1, const Sample& sample2); + + TEST(Serialization, RawPayload2String) + { + std::vector payload; + InitializeVec(payload, 1024); + + Sample sample_in = GeneratePayloadSample(payload.data(), payload.size()); + + std::string sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(ComparePayloadSamples(sample_in, sample_out)); + } + + TEST(Serialization, RawPayload2Vector) + { + std::vector payload; + InitializeVec(payload, 1024); + + Sample sample_in = GeneratePayloadSample(payload.data(), payload.size()); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(ComparePayloadSamples(sample_in, sample_out)); + } + + TEST(Serialization, VecPayload2String) + { + std::vector payload; + InitializeVec(payload, 1024); + + Sample sample_in = GeneratePayloadSample(payload); + + std::string sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(ComparePayloadSamples(sample_in, sample_out)); + } + + TEST(Serialization, VecPayload2Vector) + { + std::vector payload; + InitializeVec(payload, 1024); + + Sample sample_in = GeneratePayloadSample(payload); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(ComparePayloadSamples(sample_in, sample_out)); + } + + TEST(Serialization, VecPayloadEmpty) + { + std::vector payload; + + Sample sample_in = GeneratePayloadSample(payload); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(ComparePayloadSamples(sample_in, sample_out)); + } + + TEST(Serialization, RawPayloadEmpty) + { + Sample sample_in = GeneratePayloadSample(nullptr, 0); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(ComparePayloadSamples(sample_in, sample_out)); + } + } +} diff --git a/tests/serialization_test/src/registration_compare.cpp b/tests/serialization_test/src/registration_compare.cpp new file mode 100644 index 0000000..d721ce5 --- /dev/null +++ b/tests/serialization_test/src/registration_compare.cpp @@ -0,0 +1,194 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_struct_sample_registration.h" + +#include + +namespace eCAL +{ + namespace Registration + { + // compare two ProcessState objects + bool CompareProcessState(const ProcessState& state1, const ProcessState& state2) + { + return (state1.severity == state2.severity) && + (state1.severity_level == state2.severity_level) && + (state1.info == state2.info); + } + + // compare two Process objects + bool CompareProcess(const Process& process1, const Process& process2) + { + return (process1.rclock == process2.rclock) && + (process1.hname == process2.hname) && + (process1.hgname == process2.hgname) && + (process1.pid == process2.pid) && + (process1.pname == process2.pname) && + (process1.uname == process2.uname) && + (process1.pparam == process2.pparam) && + (process1.datawrite == process2.datawrite) && + (process1.dataread == process2.dataread) && + CompareProcessState(process1.state, process2.state) && + (process1.tsync_state == process2.tsync_state) && + (process1.tsync_mod_name == process2.tsync_mod_name) && + (process1.component_init_state == process2.component_init_state) && + (process1.component_init_info == process2.component_init_info) && + (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) + { + return (service1.rclock == service2.rclock) && + (service1.hname == service2.hname) && + (service1.pname == service2.pname) && + (service1.uname == service2.uname) && + (service1.pid == service2.pid) && + (service1.sname == service2.sname) && + (service1.sid == service2.sid) && + CompareMethods(service1.methods, service2.methods) && + (service1.version == service2.version) && + (service1.tcp_port_v0 == service2.tcp_port_v0) && + (service1.tcp_port_v1 == service2.tcp_port_v1); + } + + // compare two Client objects + bool CompareClient(const Service::Client& client1, const Service::Client& client2) + { + return (client1.rclock == client2.rclock) && + (client1.hname == client2.hname) && + (client1.pname == client2.pname) && + (client1.uname == client2.uname) && + (client1.pid == client2.pid) && + (client1.sname == client2.sname) && + (client1.sid == client2.sid) && + (client1.version == client2.version); + } + + // compare two DataTypeInformation objects + bool CompareDataTypeInformation(const DataTypeInformation& dt1, const DataTypeInformation& dt2) + { + return (dt1.name == dt2.name) && (dt1.encoding == dt2.encoding) && (dt1.desc == dt2.desc); + } + + // compare two LayerParUdpMC objects + bool CompareLayerParUdpMC(const LayerParUdpMC& /*par1*/, const LayerParUdpMC& /*par2*/) + { + return true; + } + + // compare two LayerParTcp objects + bool CompareLayerParTcp(const LayerParTcp& par1, const LayerParTcp& par2) + { + return par1.port == par2.port; + } + + // compare two LayerParShm objects + bool CompareLayerParShm(const LayerParShm& par1, const LayerParShm& par2) + { + return par1.memory_file_list == par2.memory_file_list; + } + + // compare two ConnectionPar objects + bool CompareConnectionPar(const ConnectionPar& par1, const ConnectionPar& par2) + { + return CompareLayerParUdpMC(par1.layer_par_udpmc, par2.layer_par_udpmc) && + CompareLayerParTcp(par1.layer_par_tcp, par2.layer_par_tcp) && + CompareLayerParShm(par1.layer_par_shm, par2.layer_par_shm); + } + + // compare two TLayer vector objects + bool CompareTLayer(const std::vector& layer1_vec, const std::vector& layer2_vec) + { + // ensure that both vectors have the same size + if (layer1_vec.size() != layer2_vec.size()) { + return false; + } + + // compare vectors element-wise + return std::equal(layer1_vec.begin(), layer1_vec.end(), layer2_vec.begin(), + [](const TLayer& layer1, const TLayer& layer2) { + // compare TLayer objects for equality + return (layer1.type == layer2.type) && + (layer1.version == layer2.version) && + (layer1.confirmed == layer2.confirmed) && + CompareConnectionPar(layer1.par_layer, layer2.par_layer); + }); + } + + // compare two Topic objects + bool CompareTopic(const Topic& topic1, const Topic& topic2) + { + return (topic1.rclock == topic2.rclock) && + (topic1.hname == topic2.hname) && + (topic1.hgname == topic2.hgname) && + (topic1.pid == topic2.pid) && + (topic1.pname == topic2.pname) && + (topic1.uname == topic2.uname) && + (topic1.tid == topic2.tid) && + (topic1.tname == topic2.tname) && + (topic1.direction == topic2.direction) && + (topic1.ttype == topic2.ttype) && + (topic1.tdesc == topic2.tdesc) && + CompareDataTypeInformation(topic1.tdatatype, topic2.tdatatype) && + CompareTLayer(topic1.tlayer, topic2.tlayer) && + (topic1.tsize == topic2.tsize) && + (topic1.connections_loc == topic2.connections_loc) && + (topic1.connections_ext == topic2.connections_ext) && + (topic1.message_drops == topic2.message_drops) && + (topic1.did == topic2.did) && + (topic1.dclock == topic2.dclock) && + (topic1.dfreq == topic2.dfreq) && + (topic1.attr == topic2.attr); + } + + // compare two Registration Sample objects + bool CompareRegistrationSamples(const Sample& sample1, const Sample& sample2) + { + return (sample1.cmd_type == sample2.cmd_type) && + (sample1.host.hname == sample2.host.hname) && + CompareProcess(sample1.process, sample2.process) && + CompareService(sample1.service, sample2.service) && + CompareClient(sample1.client, sample2.client) && + CompareTopic(sample1.topic, sample2.topic); + } + } +} diff --git a/tests/serialization_test/src/registration_generate.cpp b/tests/serialization_test/src/registration_generate.cpp new file mode 100644 index 0000000..051bffb --- /dev/null +++ b/tests/serialization_test/src/registration_generate.cpp @@ -0,0 +1,155 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_struct_sample_registration.h" + +#include + +namespace eCAL +{ + std::string GenerateString(size_t length); + + namespace Registration + { + // generate Method + Service::Method GenerateMethod() + { + Service::Method method; + method.mname = GenerateString(6); + method.req_type = GenerateString(8); + method.req_desc = GenerateString(10); + method.resp_type = GenerateString(8); + method.resp_desc = GenerateString(10); + method.call_count = rand() % 100; + + return method; + } + + // generate Service + Service::Service GenerateService() + { + Service::Service service; + service.rclock = rand() % 1000; + service.hname = GenerateString(8); + service.pname = GenerateString(10); + service.uname = GenerateString(5); + service.pid = rand() % 100; + service.sname = GenerateString(8); + service.sid = GenerateString(7); + service.methods.push_back(GenerateMethod()); + service.methods.push_back(GenerateMethod()); + service.version = rand() % 10; + service.tcp_port_v0 = rand() % 1000; + service.tcp_port_v1 = rand() % 1000; + + return service; + } + + // generate Client + Service::Client GenerateClient() + { + Service::Client client; + client.rclock = rand() % 1000; + client.hname = GenerateString(8); + client.pname = GenerateString(10); + client.uname = GenerateString(5); + client.pid = rand() % 100; + client.sname = GenerateString(8); + client.sid = GenerateString(7); + client.version = rand() % 10; + + return client; + } + + // generate DataTypeInformation + DataTypeInformation GenerateDataTypeInformation() + { + DataTypeInformation dt; + dt.name = GenerateString(8); + dt.encoding = GenerateString(6); + dt.desc = GenerateString(10); + return dt; + } + + // generate TLayer + TLayer GenerateTLayer() + { + TLayer layer; + layer.type = static_cast(rand() % (tl_all + 1)); + layer.version = rand() % 100; + layer.confirmed = rand() % 2 == 1; + return layer; + } + + // generate Topic + Topic GenerateTopic() + { + Topic topic; + topic.rclock = rand() % 1000; + topic.hname = GenerateString(8); + topic.hgname = GenerateString(6); + topic.pid = rand() % 100; + topic.pname = GenerateString(10); + topic.uname = GenerateString(5); + topic.tid = GenerateString(7); + topic.tname = GenerateString(8); + topic.direction = GenerateString(5); + topic.ttype = GenerateString(10); + topic.tdesc = GenerateString(12); + topic.tdatatype = GenerateDataTypeInformation(); + topic.tlayer.push_back(GenerateTLayer()); + topic.tlayer.push_back(GenerateTLayer()); + topic.tsize = rand() % 1000; + topic.connections_loc = rand() % 50; + topic.connections_ext = rand() % 50; + topic.message_drops = rand() % 10; + topic.did = rand(); + topic.dclock = rand(); + topic.dfreq = rand() % 100; + return topic; + } + + // generate Registration Sample + Sample GenerateRegistrationSample() + { + Sample sample; + sample.cmd_type = static_cast(rand() % (bct_unreg_client + 1)); + sample.host.hname = GenerateString(8); + sample.process.rclock = rand() % 1000; + sample.process.hname = GenerateString(8); + sample.process.hgname = GenerateString(6); + sample.process.pid = rand() % 100; + sample.process.pname = GenerateString(10); + sample.process.uname = GenerateString(5); + sample.process.pparam = GenerateString(12); + sample.process.datawrite = rand(); + sample.process.dataread = rand(); + sample.process.tsync_state = static_cast(rand() % (tsync_replay + 1)); + sample.process.tsync_mod_name = GenerateString(6); + sample.process.component_init_state = rand() % 5; + sample.process.component_init_info = GenerateString(8); + sample.process.ecal_runtime_version = GenerateString(5); + sample.service = GenerateService(); + sample.client = GenerateClient(); + sample.topic = GenerateTopic(); + + return sample; + } + } +} diff --git a/tests/serialization_test/src/registration_serialization_test.cpp b/tests/serialization_test/src/registration_serialization_test.cpp new file mode 100644 index 0000000..49f9fae --- /dev/null +++ b/tests/serialization_test/src/registration_serialization_test.cpp @@ -0,0 +1,91 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_serialize_sample_registration.h" + +#include + +namespace eCAL +{ + namespace Registration + { + Sample GenerateRegistrationSample(); + bool CompareRegistrationSamples(const Sample& sample1, const Sample& sample2); + + TEST(Serialization, Registration2String) + { + Sample sample_in = GenerateRegistrationSample(); + + std::string sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(CompareRegistrationSamples(sample_in, sample_out)); + } + + TEST(Serialization, Registration2Vector) + { + Sample sample_in = GenerateRegistrationSample(); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Sample sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(CompareRegistrationSamples(sample_in, sample_out)); + } + + TEST(Serialization, RegistrationList2String) + { + SampleList sample_list_in; + sample_list_in.samples.push_back(GenerateRegistrationSample()); + sample_list_in.samples.push_back(GenerateRegistrationSample()); + sample_list_in.samples.push_back(GenerateRegistrationSample()); + + std::string sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_list_in, sample_buffer)); + + SampleList sample_list_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); + + ASSERT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); + ASSERT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); + } + + TEST(Serialization, RegistrationList2Vector) + { + SampleList sample_list_in; + sample_list_in.samples.push_back(GenerateRegistrationSample()); + sample_list_in.samples.push_back(GenerateRegistrationSample()); + sample_list_in.samples.push_back(GenerateRegistrationSample()); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_list_in, sample_buffer)); + + SampleList sample_list_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); + + ASSERT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); + ASSERT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); + } + } +} diff --git a/tests/serialization_test/src/service_compare.cpp b/tests/serialization_test/src/service_compare.cpp new file mode 100644 index 0000000..981a1ab --- /dev/null +++ b/tests/serialization_test/src/service_compare.cpp @@ -0,0 +1,53 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_struct_service.h" + +namespace eCAL +{ + namespace Service + { + // compare two ServiceHeaders for equality + bool CompareServiceHeaders(const ServiceHeader& header1, const ServiceHeader& header2) + { + return (header1.hname == header2.hname && + header1.sname == header2.sname && + header1.sid == header2.sid && + header1.mname == header2.mname && + header1.error == header2.error && + header1.id == header2.id && + header1.state == header2.state); + } + + // compare two Requests for equality + bool CompareRequests(const Request& request1, const Request& request2) + { + return (CompareServiceHeaders(request1.header, request2.header) && + request1.request == request2.request); + } + + // compare two Responses for equality + bool CompareResponses(const Response& response1, const Response& response2) + { + return (CompareServiceHeaders(response1.header, response2.header) && + response1.response == response2.response && + response1.ret_state == response2.ret_state); + } + } +} diff --git a/tests/serialization_test/src/service_generate.cpp b/tests/serialization_test/src/service_generate.cpp new file mode 100644 index 0000000..70dc557 --- /dev/null +++ b/tests/serialization_test/src/service_generate.cpp @@ -0,0 +1,66 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_struct_service.h" + +#include + +namespace eCAL +{ + std::string GenerateString(size_t length); + + namespace Service + { + // generate ServiceHeader + ServiceHeader GenerateServiceHeader() + { + ServiceHeader header; + header.hname = GenerateString(8); + header.sname = GenerateString(8); + header.sid = GenerateString(5); + header.mname = GenerateString(8); + header.error = GenerateString(15); + header.id = rand() % 100; + header.state = static_cast(rand() % 3); + + return header; + } + + // generate Request + Request GenerateRequest() + { + Request request; + request.header = GenerateServiceHeader(); + request.request = GenerateString(20); + + return request; + } + + // generate Response + Response GenerateResponse() + { + Response response; + response.header = GenerateServiceHeader(); + response.response = GenerateString(20); + response.ret_state = rand() % 100; + + return response; + } + } +} diff --git a/tests/serialization_test/src/service_serialization_test.cpp b/tests/serialization_test/src/service_serialization_test.cpp new file mode 100644 index 0000000..cdaa070 --- /dev/null +++ b/tests/serialization_test/src/service_serialization_test.cpp @@ -0,0 +1,86 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "../../serialization/ecal_serialize_service.h" + +#include + +namespace eCAL +{ + namespace Service + { + Request GenerateRequest(); + bool CompareRequests(const Request& request1, const Request& request2); + + Response GenerateResponse(); + bool CompareResponses(const Response& response1, const Response& response2); + + TEST(Serialization, Request2String) + { + Request sample_in = GenerateRequest(); + + std::string sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Request sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(CompareRequests(sample_in, sample_out)); + } + + TEST(Serialization, Request2Vector) + { + Request sample_in = GenerateRequest(); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Request sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(CompareRequests(sample_in, sample_out)); + } + + TEST(Serialization, Response2String) + { + Response sample_in = GenerateResponse(); + + std::string sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Response sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(CompareResponses(sample_in, sample_out)); + } + + TEST(Serialization, Response2Vector) + { + Response sample_in = GenerateResponse(); + + std::vector sample_buffer; + ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + + Response sample_out; + ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + ASSERT_TRUE(CompareResponses(sample_in, sample_out)); + } + } +} diff --git a/testing/ecal/topic2mcast_test/CMakeLists.txt b/tests/topic2mcast_test/CMakeLists.txt similarity index 99% rename from testing/ecal/topic2mcast_test/CMakeLists.txt rename to tests/topic2mcast_test/CMakeLists.txt index 7bb3a40..9aa45aa 100644 --- a/testing/ecal/topic2mcast_test/CMakeLists.txt +++ b/tests/topic2mcast_test/CMakeLists.txt @@ -39,4 +39,4 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) ecal_install_gtest(${PROJECT_NAME}) -set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal/core) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) diff --git a/testing/ecal/topic2mcast_test/src/topic2mcast_test.cpp b/tests/topic2mcast_test/src/topic2mcast_test.cpp similarity index 98% rename from testing/ecal/topic2mcast_test/src/topic2mcast_test.cpp rename to tests/topic2mcast_test/src/topic2mcast_test.cpp index 425e2b4..55862d7 100644 --- a/testing/ecal/topic2mcast_test/src/topic2mcast_test.cpp +++ b/tests/topic2mcast_test/src/topic2mcast_test.cpp @@ -18,18 +18,13 @@ */ #include -#include "topic2mcast.h" +#include "io/udp/ecal_udp_topic2mcast.h" #include #include -#include -#include #include -#include - - struct IP { uint32_t int_ip; diff --git a/testing/ecal/pubsub_inproc_test/CMakeLists.txt b/tests/util_test/CMakeLists.txt similarity index 80% rename from testing/ecal/pubsub_inproc_test/CMakeLists.txt rename to tests/util_test/CMakeLists.txt index f44c9da..3ef8203 100644 --- a/testing/ecal/pubsub_inproc_test/CMakeLists.txt +++ b/tests/util_test/CMakeLists.txt @@ -16,20 +16,19 @@ # # ========================= eCAL LICENSE ================================= -project(test_pubsub_inproc) +project(test_util) find_package(Threads REQUIRED) find_package(GTest REQUIRED) -set(pubsub_inproc_test_src - src/pubsub_inproc_test.cpp +set(util_test_src + src/util_test.cpp ) -ecal_add_gtest(${PROJECT_NAME} ${pubsub_inproc_test_src}) +ecal_add_gtest(${PROJECT_NAME} ${util_test_src}) +target_include_directories(${PROJECT_NAME} PRIVATE $) target_link_libraries(${PROJECT_NAME} - PRIVATE - eCAL::core - Threads::Threads) + 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 testing/ecal/pubsub) +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER testing/ecal) \ No newline at end of file diff --git a/tests/util_test/src/util_test.cpp b/tests/util_test/src/util_test.cpp new file mode 100644 index 0000000..cf60821 --- /dev/null +++ b/tests/util_test/src/util_test.cpp @@ -0,0 +1,54 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2019 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include +#include + + +namespace { + void TestCombinedTopicEncodingAndType(const std::string& encoding, const std::string& type, const std::string& expected_result) + { + auto combined = eCAL::Util::CombinedTopicEncodingAndType(encoding, type); + EXPECT_EQ(combined, expected_result); + } + + void TestSplitCombinedTopicType(const std::string& combined_topic_type, const std::string& expected_encoding, const std::string& expected_type) + { + auto split = eCAL::Util::SplitCombinedTopicType(combined_topic_type); + EXPECT_EQ(split.first, expected_encoding); + EXPECT_EQ(split.second, expected_type); + } +} + +TEST(Util, CombineTopicEncodingAndType) +{ + TestCombinedTopicEncodingAndType("", "", ""); + TestCombinedTopicEncodingAndType("proto", "pb.Person.People", "proto:pb.Person.People"); + TestCombinedTopicEncodingAndType("base", "", "base:"); + TestCombinedTopicEncodingAndType("", "MyType", "MyType"); +} + +TEST(Util, SplitCombinedTopicType) +{ + TestSplitCombinedTopicType("", "", ""); + TestSplitCombinedTopicType("proto:pb.Person.People", "proto", "pb.Person.People"); + TestSplitCombinedTopicType("base:", "base", ""); + TestSplitCombinedTopicType("base:std::string", "base", "std::string"); + TestSplitCombinedTopicType("MyType", "", "MyType"); +} diff --git a/thirdparty/asio b/thirdparty/asio deleted file mode 160000 index 6c054e9..0000000 --- a/thirdparty/asio +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6c054e98f3f53352d12b6cd46d63b6d404cc044b diff --git a/thirdparty/asio/Modules/Findasio.cmake b/thirdparty/asio/Modules/Findasio.cmake new file mode 100644 index 0000000..cbc155d --- /dev/null +++ b/thirdparty/asio/Modules/Findasio.cmake @@ -0,0 +1 @@ +set(asio_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/asio/build-asio.cmake b/thirdparty/asio/build-asio.cmake new file mode 100644 index 0000000..713ba1c --- /dev/null +++ b/thirdparty/asio/build-asio.cmake @@ -0,0 +1,7 @@ +add_library(asio INTERFACE) +target_include_directories(asio INTERFACE ${CMAKE_CURRENT_LIST_DIR}/asio/asio/include) +target_compile_definitions(asio INTERFACE ASIO_STANDALONE) + +add_library(asio::asio ALIAS asio) + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file diff --git a/thirdparty/build-asio.cmake b/thirdparty/build-asio.cmake deleted file mode 100644 index e69de29..0000000 diff --git a/thirdparty/build-cmakefunctions.cmake b/thirdparty/build-cmakefunctions.cmake deleted file mode 100644 index ba641fe..0000000 --- a/thirdparty/build-cmakefunctions.cmake +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(thirdparty/cmake_functions) \ No newline at end of file diff --git a/thirdparty/build-recycle.cmake b/thirdparty/build-recycle.cmake deleted file mode 100644 index 035baf5..0000000 --- a/thirdparty/build-recycle.cmake +++ /dev/null @@ -1,2 +0,0 @@ -add_subdirectory(thirdparty/recycle EXCLUDE_FROM_ALL) -add_library(steinwurf::recycle ALIAS recycle) diff --git a/thirdparty/build-simpleini.cmake b/thirdparty/build-simpleini.cmake deleted file mode 100644 index e69de29..0000000 diff --git a/thirdparty/build-tclap.cmake b/thirdparty/build-tclap.cmake deleted file mode 100644 index e69de29..0000000 diff --git a/thirdparty/build-tcp_pubsub.cmake b/thirdparty/build-tcp_pubsub.cmake deleted file mode 100644 index 2b44161..0000000 --- a/thirdparty/build-tcp_pubsub.cmake +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(thirdparty/tcp_pubsub/tcp_pubsub EXCLUDE_FROM_ALL) -set_property(TARGET tcp_pubsub PROPERTY FOLDER lib/tcp_pubsub) -add_library(tcp_pubsub::tcp_pubsub ALIAS tcp_pubsub) \ No newline at end of file diff --git a/thirdparty/cmakefunctions/Modules/FindCMakeFunctions.cmake b/thirdparty/cmakefunctions/Modules/FindCMakeFunctions.cmake new file mode 100644 index 0000000..59d818a --- /dev/null +++ b/thirdparty/cmakefunctions/Modules/FindCMakeFunctions.cmake @@ -0,0 +1 @@ +set(CMakeFunctions_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/cmakefunctions/build-cmakefunctions.cmake b/thirdparty/cmakefunctions/build-cmakefunctions.cmake new file mode 100644 index 0000000..beb732a --- /dev/null +++ b/thirdparty/cmakefunctions/build-cmakefunctions.cmake @@ -0,0 +1,2 @@ +add_subdirectory(thirdparty/cmakefunctions/cmake_functions) +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file diff --git a/thirdparty/cmake_functions/.gitignore b/thirdparty/cmakefunctions/cmake_functions/.gitignore similarity index 100% rename from thirdparty/cmake_functions/.gitignore rename to thirdparty/cmakefunctions/cmake_functions/.gitignore diff --git a/thirdparty/cmake_functions/CMakeLists.txt b/thirdparty/cmakefunctions/cmake_functions/CMakeLists.txt similarity index 97% rename from thirdparty/cmake_functions/CMakeLists.txt rename to thirdparty/cmakefunctions/cmake_functions/CMakeLists.txt index b3e9326..219a5c6 100644 --- a/thirdparty/cmake_functions/CMakeLists.txt +++ b/thirdparty/cmakefunctions/cmake_functions/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5.1) include(cmake_functions.cmake) diff --git a/thirdparty/cmake_functions/cmake/CMakeFunctionsConfig.cmake.in b/thirdparty/cmakefunctions/cmake_functions/cmake/CMakeFunctionsConfig.cmake.in similarity index 100% rename from thirdparty/cmake_functions/cmake/CMakeFunctionsConfig.cmake.in rename to thirdparty/cmakefunctions/cmake_functions/cmake/CMakeFunctionsConfig.cmake.in diff --git a/thirdparty/cmake_functions/cmake/cpack_variables.cmake b/thirdparty/cmakefunctions/cmake_functions/cmake/cpack_variables.cmake similarity index 100% rename from thirdparty/cmake_functions/cmake/cpack_variables.cmake rename to thirdparty/cmakefunctions/cmake_functions/cmake/cpack_variables.cmake diff --git a/thirdparty/cmake_functions/cmake_functions.cmake b/thirdparty/cmakefunctions/cmake_functions/cmake_functions.cmake similarity index 100% rename from thirdparty/cmake_functions/cmake_functions.cmake rename to thirdparty/cmakefunctions/cmake_functions/cmake_functions.cmake diff --git a/thirdparty/cmake_functions/git/git_revision_information.cmake b/thirdparty/cmakefunctions/cmake_functions/git/git_revision_information.cmake similarity index 100% rename from thirdparty/cmake_functions/git/git_revision_information.cmake rename to thirdparty/cmakefunctions/cmake_functions/git/git_revision_information.cmake diff --git a/thirdparty/cmake_functions/msvc_helper/msvc_macros.cmake b/thirdparty/cmakefunctions/cmake_functions/msvc_helper/msvc_macros.cmake similarity index 100% rename from thirdparty/cmake_functions/msvc_helper/msvc_macros.cmake rename to thirdparty/cmakefunctions/cmake_functions/msvc_helper/msvc_macros.cmake diff --git a/thirdparty/cmake_functions/protoc_functions/protoc_generate_cpp.cmake b/thirdparty/cmakefunctions/cmake_functions/protoc_functions/protoc_generate_cpp.cmake similarity index 100% rename from thirdparty/cmake_functions/protoc_functions/protoc_generate_cpp.cmake rename to thirdparty/cmakefunctions/cmake_functions/protoc_functions/protoc_generate_cpp.cmake diff --git a/thirdparty/cmake_functions/protoc_functions/protoc_generate_files.cmake b/thirdparty/cmakefunctions/cmake_functions/protoc_functions/protoc_generate_files.cmake similarity index 100% rename from thirdparty/cmake_functions/protoc_functions/protoc_generate_files.cmake rename to thirdparty/cmakefunctions/cmake_functions/protoc_functions/protoc_generate_files.cmake diff --git a/thirdparty/cmake_functions/protoc_functions/protoc_generate_python.cmake b/thirdparty/cmakefunctions/cmake_functions/protoc_functions/protoc_generate_python.cmake similarity index 100% rename from thirdparty/cmake_functions/protoc_functions/protoc_generate_python.cmake rename to thirdparty/cmakefunctions/cmake_functions/protoc_functions/protoc_generate_python.cmake diff --git a/thirdparty/cmake_functions/target_definitions/targets_protobuf.cmake b/thirdparty/cmakefunctions/cmake_functions/target_definitions/targets_protobuf.cmake similarity index 100% rename from thirdparty/cmake_functions/target_definitions/targets_protobuf.cmake rename to thirdparty/cmakefunctions/cmake_functions/target_definitions/targets_protobuf.cmake diff --git a/thirdparty/ecal-utils b/thirdparty/ecal-utils deleted file mode 160000 index 152c8ff..0000000 --- a/thirdparty/ecal-utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 152c8ff267fd3d95ee285f63af72f4d6d0972193 diff --git a/thirdparty/googletest b/thirdparty/googletest deleted file mode 160000 index e2239ee..0000000 --- a/thirdparty/googletest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 diff --git a/thirdparty/gtest/Modules/FindGTest.cmake b/thirdparty/gtest/Modules/FindGTest.cmake new file mode 100644 index 0000000..4cfd2bf --- /dev/null +++ b/thirdparty/gtest/Modules/FindGTest.cmake @@ -0,0 +1 @@ +set(GTest_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/build-gtest.cmake b/thirdparty/gtest/build-gtest.cmake similarity index 75% rename from thirdparty/build-gtest.cmake rename to thirdparty/gtest/build-gtest.cmake index c3421a4..5faa260 100644 --- a/thirdparty/build-gtest.cmake +++ b/thirdparty/gtest/build-gtest.cmake @@ -4,10 +4,12 @@ if(MSVC) set(BUILD_GMOCK OFF CACHE BOOL "My option" FORCE) set(INSTALL_GTEST OFF CACHE BOOL "My option" FORCE) endif() -add_subdirectory(thirdparty/googletest EXCLUDE_FROM_ALL) +add_subdirectory(thirdparty/gtest/googletest EXCLUDE_FROM_ALL) if(NOT TARGET GTest::gtest) add_library(GTest::gtest ALIAS gtest) endif() if(NOT TARGET GTest::gtest_main) add_library(GTest::gtest_main ALIAS gtest_main) -endif() \ No newline at end of file +endif() + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file diff --git a/thirdparty/protobuf b/thirdparty/protobuf deleted file mode 160000 index d0bfd52..0000000 --- a/thirdparty/protobuf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0bfd5221182da1a7cc280f3337b5e41a89539cf diff --git a/thirdparty/protobuf/Modules/FindProtobuf.cmake b/thirdparty/protobuf/Modules/FindProtobuf.cmake new file mode 100644 index 0000000..fc518cf --- /dev/null +++ b/thirdparty/protobuf/Modules/FindProtobuf.cmake @@ -0,0 +1 @@ +set(Protobuf_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/build-protobuf.cmake b/thirdparty/protobuf/build-protobuf.cmake similarity index 91% rename from thirdparty/build-protobuf.cmake rename to thirdparty/protobuf/build-protobuf.cmake index fc41247..96cbbbb 100644 --- a/thirdparty/build-protobuf.cmake +++ b/thirdparty/protobuf/build-protobuf.cmake @@ -3,7 +3,7 @@ set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "My option" FORCE) if(UNIX) set(protobuf_BUILD_SHARED_LIBS ON CACHE BOOL "My option" FORCE) endif() -add_subdirectory(thirdparty/protobuf/cmake) +add_subdirectory(thirdparty/protobuf/protobuf/cmake) if (NOT TARGET protobuf::libprotobuf) add_library(protobuf::libprotobuf ALIAS libprotobuf) @@ -47,4 +47,6 @@ if (NOT is_imported) endif() set(Protobuf_PROTOC_EXECUTABLE protoc) -set(Protobuf_VERSION 3.11.4) \ No newline at end of file +set(Protobuf_VERSION 3.11.4) + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file diff --git a/thirdparty/recycle b/thirdparty/recycle deleted file mode 160000 index c542570..0000000 --- a/thirdparty/recycle +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c5425709b2273ef6371647247d1a1d86aa75c2e6 diff --git a/thirdparty/recycle/Modules/Findrecycle.cmake b/thirdparty/recycle/Modules/Findrecycle.cmake new file mode 100644 index 0000000..84dde3a --- /dev/null +++ b/thirdparty/recycle/Modules/Findrecycle.cmake @@ -0,0 +1 @@ +set(recycle_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/recycle/build-recycle.cmake b/thirdparty/recycle/build-recycle.cmake new file mode 100644 index 0000000..a15f814 --- /dev/null +++ b/thirdparty/recycle/build-recycle.cmake @@ -0,0 +1,4 @@ +add_subdirectory(thirdparty/recycle/recycle EXCLUDE_FROM_ALL) +add_library(steinwurf::recycle ALIAS recycle) + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) diff --git a/thirdparty/simpleini b/thirdparty/simpleini deleted file mode 160000 index 17e8f9f..0000000 --- a/thirdparty/simpleini +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 17e8f9f4bebe0b78cf037629d1fb44495b471eb4 diff --git a/thirdparty/simpleini/Modules/Findsimpleini.cmake b/thirdparty/simpleini/Modules/Findsimpleini.cmake new file mode 100644 index 0000000..aa8def2 --- /dev/null +++ b/thirdparty/simpleini/Modules/Findsimpleini.cmake @@ -0,0 +1 @@ +set(simpleini_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/simpleini/build-simpleini.cmake b/thirdparty/simpleini/build-simpleini.cmake new file mode 100644 index 0000000..afe5f6f --- /dev/null +++ b/thirdparty/simpleini/build-simpleini.cmake @@ -0,0 +1,6 @@ +add_library(simpleini INTERFACE) +target_include_directories(simpleini INTERFACE ${CMAKE_CURRENT_LIST_DIR}/simpleini) + +add_library(simpleini::simpleini ALIAS simpleini) + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file diff --git a/thirdparty/tclap b/thirdparty/tclap deleted file mode 160000 index eeb9fbb..0000000 --- a/thirdparty/tclap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit eeb9fbb65d4dca42889da8b81bcedb4d51baed3c diff --git a/thirdparty/tclap/Modules/Findtclap.cmake b/thirdparty/tclap/Modules/Findtclap.cmake new file mode 100644 index 0000000..878f3bf --- /dev/null +++ b/thirdparty/tclap/Modules/Findtclap.cmake @@ -0,0 +1 @@ +set(tclap_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/tclap/build-tclap.cmake b/thirdparty/tclap/build-tclap.cmake new file mode 100644 index 0000000..45551fd --- /dev/null +++ b/thirdparty/tclap/build-tclap.cmake @@ -0,0 +1,6 @@ +add_library(tclap INTERFACE) +target_include_directories(tclap INTERFACE ${CMAKE_CURRENT_LIST_DIR}/tclap/include) + +add_library(tclap::tclap ALIAS tclap) + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file diff --git a/thirdparty/tcp_pubsub b/thirdparty/tcp_pubsub deleted file mode 160000 index eae093f..0000000 --- a/thirdparty/tcp_pubsub +++ /dev/null @@ -1 +0,0 @@ -Subproject commit eae093f12b8fc0a40050f3cd5db3843f647e4370 diff --git a/thirdparty/tcp_pubsub/Modules/Findtcp_pubsub.cmake b/thirdparty/tcp_pubsub/Modules/Findtcp_pubsub.cmake new file mode 100644 index 0000000..8f8e0a5 --- /dev/null +++ b/thirdparty/tcp_pubsub/Modules/Findtcp_pubsub.cmake @@ -0,0 +1 @@ +set(tcp_pubsub_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/tcp_pubsub/build-tcp_pubsub.cmake b/thirdparty/tcp_pubsub/build-tcp_pubsub.cmake new file mode 100644 index 0000000..9a3dead --- /dev/null +++ b/thirdparty/tcp_pubsub/build-tcp_pubsub.cmake @@ -0,0 +1,5 @@ +add_subdirectory(thirdparty/tcp_pubsub/tcp_pubsub/tcp_pubsub EXCLUDE_FROM_ALL) +set_property(TARGET tcp_pubsub PROPERTY FOLDER lib/tcp_pubsub) +add_library(tcp_pubsub::tcp_pubsub ALIAS tcp_pubsub) + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file diff --git a/thirdparty/udpcap b/thirdparty/udpcap deleted file mode 160000 index 4175ecf..0000000 --- a/thirdparty/udpcap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4175ecf4de933082af8d19e6315ecc81413b07f1 diff --git a/thirdparty/udpcap/Modules/Findudpcap.cmake b/thirdparty/udpcap/Modules/Findudpcap.cmake new file mode 100644 index 0000000..5a86153 --- /dev/null +++ b/thirdparty/udpcap/Modules/Findudpcap.cmake @@ -0,0 +1 @@ +set(udpcap_FOUND TRUE) \ No newline at end of file diff --git a/thirdparty/build-udpcap.cmake b/thirdparty/udpcap/build-udpcap.cmake similarity index 83% rename from thirdparty/build-udpcap.cmake rename to thirdparty/udpcap/build-udpcap.cmake index f242dd9..61b2e2d 100644 --- a/thirdparty/build-udpcap.cmake +++ b/thirdparty/udpcap/build-udpcap.cmake @@ -16,7 +16,7 @@ set(UDPCAP_THIRDPARTY_ENABLED ON) set(UDPCAP_THIRDPARTY_USE_BUILTIN_ASIO OFF) # Add udpcap library from subdirectory -add_subdirectory(thirdparty/udpcap/ EXCLUDE_FROM_ALL) +add_subdirectory(thirdparty/udpcap/udpcap EXCLUDE_FROM_ALL) add_library(udpcap::udpcap ALIAS udpcap) # Reset static / shared libs to old value @@ -24,4 +24,6 @@ set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_OLD}) unset(BUILD_SHARED_LIBS_OLD) # move the udpcap target to a subdirectory in the IDE -set_property(TARGET udpcap PROPERTY FOLDER lib/udpcap) \ No newline at end of file +set_property(TARGET udpcap PROPERTY FOLDER lib/udpcap) + +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Modules) \ No newline at end of file From 951901142920d9a0dc797eda0e6b084a611e5435 Mon Sep 17 00:00:00 2001 From: rex-schilasky <49162693+rex-schilasky@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:53:11 +0100 Subject: [PATCH 2/2] submodules added --- .gitmodules | 2 +- DEPENDENCIES.txt | 22 ++++++++++++++++------ README.md | 1 + thirdparty/asio/asio | 1 + thirdparty/gtest/googletest | 1 + thirdparty/protobuf/protobuf | 1 + thirdparty/recycle/recycle | 1 + thirdparty/simpleini/simpleini | 1 + thirdparty/tclap/tclap | 1 + thirdparty/tcp_pubsub/tcp_pubsub | 1 + thirdparty/udpcap/udpcap | 1 + 11 files changed, 26 insertions(+), 7 deletions(-) create mode 160000 thirdparty/asio/asio create mode 160000 thirdparty/gtest/googletest create mode 160000 thirdparty/protobuf/protobuf create mode 160000 thirdparty/recycle/recycle create mode 160000 thirdparty/simpleini/simpleini create mode 160000 thirdparty/tclap/tclap create mode 160000 thirdparty/tcp_pubsub/tcp_pubsub create mode 160000 thirdparty/udpcap/udpcap diff --git a/.gitmodules b/.gitmodules index de593d4..ac2a262 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,7 +18,7 @@ url = https://github.com/eclipse-ecal/udpcap [submodule "thirdparty/tcp_pubsub/tcp_pubsub"] path = thirdparty/tcp_pubsub/tcp_pubsub - url = https://github.com/continental/tcp_pubsub.git + url = https://github.com/eclipse-ecal/tcp_pubsub.git [submodule "thirdparty/recycle/recycle"] path = thirdparty/recycle/recycle url = https://github.com/steinwurf/recycle.git diff --git a/DEPENDENCIES.txt b/DEPENDENCIES.txt index 22543cf..049c56a 100644 --- a/DEPENDENCIES.txt +++ b/DEPENDENCIES.txt @@ -1,29 +1,39 @@ -ASIO: +asio: path = thirdparty/asio/asio url = https://github.com/chriskohlhoff/asio.git version = 1.29.0 -Googletest: +googletest: path = thirdparty/gtest/googletest url = https://github.com/google/googletest.git version = v1.14.0 -Protobuf: +protobuf: path = thirdparty/protobuf/protobuf url = https://github.com/protocolbuffers/protobuf.git version = v3.11.0 + +recycle: + path = thirdparty/recycle/recycle + url = https://github.com/steinwurf/recycle.git + version = 7.0.0 -Simpleini: +simpleini: path = thirdparty/simpleini/simpleini url = https://github.com/brofield/simpleini.git version = v4.20 -Tclap: +tclap: path = thirdparty/tclap/tclap url = https://github.com/xguerin/tclap.git version = v1.2.5 + +tcp_pubsub: + path = thirdparty/tcp_pubsub/tcp_pubsub + url = https://github.com/eclipse-ecal/tcp_pubsub.git + version = 1.0.4 -Udpcap: +udpcap: path = thirdparty/udpcap/udpcap url = https://github.com/eclipse-ecal/udpcap version = 1.0.2 diff --git a/README.md b/README.md index 9844b9c..3e9f71a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This eCAL core version has an reduced functionality compared to [Eclipse-eCAL](h Visit the eCAL Documentation at 🌐 https://ecal.io for more information. ## Differences to Eclipse-eCAL + - internal communication (discovery) independent from google::protobuf library - communication core only, no additional eco system tools - C++ and C language support only - reduced API functionality (removal of all deprecated functions) diff --git a/thirdparty/asio/asio b/thirdparty/asio/asio new file mode 160000 index 0000000..814f67e --- /dev/null +++ b/thirdparty/asio/asio @@ -0,0 +1 @@ +Subproject commit 814f67e730e154547aea3f4d99f709cbdf1ea4a0 diff --git a/thirdparty/gtest/googletest b/thirdparty/gtest/googletest new file mode 160000 index 0000000..f8d7d77 --- /dev/null +++ b/thirdparty/gtest/googletest @@ -0,0 +1 @@ +Subproject commit f8d7d77c06936315286eb55f8de22cd23c188571 diff --git a/thirdparty/protobuf/protobuf b/thirdparty/protobuf/protobuf new file mode 160000 index 0000000..ab5b61b --- /dev/null +++ b/thirdparty/protobuf/protobuf @@ -0,0 +1 @@ +Subproject commit ab5b61bf2f0fb1ac485be1b82fffca153c2509ed diff --git a/thirdparty/recycle/recycle b/thirdparty/recycle/recycle new file mode 160000 index 0000000..3f3d27e --- /dev/null +++ b/thirdparty/recycle/recycle @@ -0,0 +1 @@ +Subproject commit 3f3d27ecdee87af9167adf1d2c2345ca2cbe1c94 diff --git a/thirdparty/simpleini/simpleini b/thirdparty/simpleini/simpleini new file mode 160000 index 0000000..382ddbb --- /dev/null +++ b/thirdparty/simpleini/simpleini @@ -0,0 +1 @@ +Subproject commit 382ddbb4b92c0b26aa1b32cefba2002119a5b1f2 diff --git a/thirdparty/tclap/tclap b/thirdparty/tclap/tclap new file mode 160000 index 0000000..349170a --- /dev/null +++ b/thirdparty/tclap/tclap @@ -0,0 +1 @@ +Subproject commit 349170aef10dc30d692616b525e8f05cb41df048 diff --git a/thirdparty/tcp_pubsub/tcp_pubsub b/thirdparty/tcp_pubsub/tcp_pubsub new file mode 160000 index 0000000..993a4fe --- /dev/null +++ b/thirdparty/tcp_pubsub/tcp_pubsub @@ -0,0 +1 @@ +Subproject commit 993a4fe1536f80ed4fe59f3ef69cac545b7c5ca8 diff --git a/thirdparty/udpcap/udpcap b/thirdparty/udpcap/udpcap new file mode 160000 index 0000000..a462630 --- /dev/null +++ b/thirdparty/udpcap/udpcap @@ -0,0 +1 @@ +Subproject commit a462630423677d19f7c56b02e2df1cbcda1257df