From e16cf67b94e067f9b64052146eeb6573da79ce13 Mon Sep 17 00:00:00 2001 From: Dennis Klein Date: Tue, 22 Mar 2022 12:58:10 +0100 Subject: [PATCH] feat!: Remove deprecated components sdk, sdk_commands, dds_plugin BREAKING CHANGE: Components have been moved to ODC project, see https://github.com/FairRootGroup/FairMQ/discussions/392 for details. --- CMakeLists.txt | 24 +- FairMQTest.cmake | 5 +- README.md | 6 +- cmake/FairMQDependencies.cmake | 26 +- cmake/FairMQSummary.cmake | 35 +- docs/Plugins.md | 32 +- docs/SDK.md | 34 - fairmq/CMakeLists.txt | 15 +- fairmq/SDK.h | 26 - fairmq/plugins/DDS/CMakeLists.txt | 24 - fairmq/plugins/DDS/DDS.cxx | 457 ------- fairmq/plugins/DDS/DDS.h | 199 --- fairmq/plugins/PMIx/CMakeLists.txt | 6 +- fairmq/plugins/PMIx/PMIxPlugin.cxx | 108 +- fairmq/plugins/PMIx/PMIxPlugin.h | 6 +- fairmq/plugins/PMIx/runPMIxCommandUI.cxx | 89 +- fairmq/sdk/AsioAsyncOp.h | 223 ---- fairmq/sdk/AsioBase.h | 73 -- fairmq/sdk/CMakeLists.txt | 117 -- fairmq/sdk/DDSAgent.h | 78 -- fairmq/sdk/DDSCollection.h | 46 - fairmq/sdk/DDSEnvironment.cxx | 86 -- fairmq/sdk/DDSEnvironment.h | 44 - fairmq/sdk/DDSInfo.h.in | 29 - fairmq/sdk/DDSSession.cxx | 389 ------ fairmq/sdk/DDSSession.h | 118 -- fairmq/sdk/DDSTask.h | 49 - fairmq/sdk/DDSTopology.cxx | 116 -- fairmq/sdk/DDSTopology.h | 75 -- fairmq/sdk/Error.h | 20 - fairmq/sdk/Topology.cxx | 31 - fairmq/sdk/Topology.h | 1367 -------------------- fairmq/sdk/Traits.h | 48 - fairmq/sdk/commands/CMakeLists.txt | 60 - fairmq/sdk/commands/Commands.cxx | 479 ------- fairmq/sdk/commands/Commands.h | 414 ------ fairmq/sdk/commands/CommandsFormat.fbs | 89 -- fairmq/sdk/commands/CommandsFormatDef.h.in | 18 - fairmq/sdk/runDDSCommandUI.cxx | 256 ---- test/CMakeLists.txt | 55 +- test/DDSToolsAPIStabilityTest.cmake.in | 24 - test/commands/_commands.cxx | 241 ---- test/sdk/Fixtures.h | 169 --- test/sdk/_async_op.cxx | 118 -- test/sdk/_dds.cxx | 50 - test/sdk/_topology.cxx | 589 --------- test/sdk/test_topo.xml | 42 - 47 files changed, 23 insertions(+), 6582 deletions(-) delete mode 100644 docs/SDK.md delete mode 100644 fairmq/SDK.h delete mode 100644 fairmq/plugins/DDS/CMakeLists.txt delete mode 100644 fairmq/plugins/DDS/DDS.cxx delete mode 100644 fairmq/plugins/DDS/DDS.h delete mode 100644 fairmq/sdk/AsioAsyncOp.h delete mode 100644 fairmq/sdk/AsioBase.h delete mode 100644 fairmq/sdk/CMakeLists.txt delete mode 100644 fairmq/sdk/DDSAgent.h delete mode 100644 fairmq/sdk/DDSCollection.h delete mode 100644 fairmq/sdk/DDSEnvironment.cxx delete mode 100644 fairmq/sdk/DDSEnvironment.h delete mode 100644 fairmq/sdk/DDSInfo.h.in delete mode 100644 fairmq/sdk/DDSSession.cxx delete mode 100644 fairmq/sdk/DDSSession.h delete mode 100644 fairmq/sdk/DDSTask.h delete mode 100644 fairmq/sdk/DDSTopology.cxx delete mode 100644 fairmq/sdk/DDSTopology.h delete mode 100644 fairmq/sdk/Error.h delete mode 100644 fairmq/sdk/Topology.cxx delete mode 100644 fairmq/sdk/Topology.h delete mode 100644 fairmq/sdk/Traits.h delete mode 100644 fairmq/sdk/commands/CMakeLists.txt delete mode 100644 fairmq/sdk/commands/Commands.cxx delete mode 100644 fairmq/sdk/commands/Commands.h delete mode 100644 fairmq/sdk/commands/CommandsFormat.fbs delete mode 100644 fairmq/sdk/commands/CommandsFormatDef.h.in delete mode 100644 fairmq/sdk/runDDSCommandUI.cxx delete mode 100644 test/DDSToolsAPIStabilityTest.cmake.in delete mode 100644 test/commands/_commands.cxx delete mode 100644 test/sdk/Fixtures.h delete mode 100644 test/sdk/_async_op.cxx delete mode 100644 test/sdk/_dds.cxx delete mode 100644 test/sdk/_topology.cxx delete mode 100644 test/sdk/test_topo.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 85d43e64..02cf5fc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,16 +29,10 @@ fairmq_build_option(BUILD_TESTING "Build tests." DEFAULT OFF REQUIRES "BUILD_FAIRMQ") fairmq_build_option(BUILD_OFI_TRANSPORT "Build experimental OFI transport." DEFAULT OFF REQUIRES "BUILD_FAIRMQ") -fairmq_build_option(BUILD_SDK_COMMANDS "Build the FairMQ SDK commands." - DEFAULT OFF) -fairmq_build_option(BUILD_DDS_PLUGIN "Build DDS plugin." - DEFAULT OFF REQUIRES "BUILD_FAIRMQ;BUILD_SDK_COMMANDS") fairmq_build_option(BUILD_PMIX_PLUGIN "Build PMIx plugin." - DEFAULT OFF REQUIRES "BUILD_FAIRMQ;BUILD_SDK_COMMANDS") + DEFAULT OFF REQUIRES "BUILD_FAIRMQ") fairmq_build_option(BUILD_EXAMPLES "Build FairMQ examples." DEFAULT ON REQUIRES "BUILD_FAIRMQ") -fairmq_build_option(BUILD_SDK "Build the FairMQ controller SDK." - DEFAULT OFF REQUIRES "BUILD_DDS_PLUGIN;BUILD_SDK_COMMANDS") fairmq_build_option(BUILD_TIDY_TOOL "Build the fairmq-tidy tool." DEFAULT OFF) fairmq_build_option(BUILD_DOCS "Build FairMQ documentation." @@ -57,7 +51,7 @@ include(FairMQDependencies) # Targets ###################################################################### -if(BUILD_FAIRMQ OR BUILD_SDK) +if(BUILD_FAIRMQ) add_subdirectory(fairmq) endif() @@ -92,9 +86,6 @@ endif() if(BUILD_TESTING) list(APPEND PROJECT_PACKAGE_COMPONENTS tests) endif() -if(BUILD_DDS_PLUGIN) - list(APPEND PROJECT_PACKAGE_COMPONENTS dds_plugin) -endif() if(BUILD_PMIX_PLUGIN) list(APPEND PROJECT_PACKAGE_COMPONENTS pmix_plugin) endif() @@ -107,12 +98,6 @@ endif() if(BUILD_DOCS) list(APPEND PROJECT_PACKAGE_COMPONENTS docs) endif() -if(BUILD_SDK) - list(APPEND PROJECT_PACKAGE_COMPONENTS sdk) -endif() -if(BUILD_SDK_COMMANDS) - list(APPEND PROJECT_PACKAGE_COMPONENTS sdk_commands) -endif() if(BUILD_TIDY_TOOL) list(APPEND PROJECT_PACKAGE_COMPONENTS tidy_tool) endif() @@ -125,11 +110,6 @@ if(BUILD_FAIRMQ) DESTINATION ${PROJECT_INSTALL_CMAKEMODDIR} ) endif() -if(BUILD_SDK OR BUILD_DDS_PLUGIN) - install(FILES cmake/Findasio.cmake - DESTINATION ${PROJECT_INSTALL_CMAKEMODDIR} - ) -endif() if(BUILD_DOCS) install(DIRECTORY ${CMAKE_BINARY_DIR}/doxygen/html DESTINATION ${PROJECT_INSTALL_DATADIR}/docs diff --git a/FairMQTest.cmake b/FairMQTest.cmake index fb9128f5..568f634c 100644 --- a/FairMQTest.cmake +++ b/FairMQTest.cmake @@ -42,11 +42,8 @@ endif() ctest_start(Continuous) list(APPEND options "-DDISABLE_COLOR=ON" "-DBUILD_EXAMPLES=ON" "-DBUILD_TESTING=ON") -if(HAS_ASIO AND HAS_DDS) - list(APPEND options "-DBUILD_SDK_COMMANDS=ON" "-DBUILD_SDK=ON" "-DBUILD_DDS_PLUGIN=ON") -endif() if(HAS_PMIX) - list(APPEND options "-DBUILD_SDK_COMMANDS=ON" "-DBUILD_PMIX_PLUGIN=ON") + list(APPEND options "-DBUILD_PMIX_PLUGIN=ON") endif() if(HAS_ASIO AND HAS_ASIOFI) list(APPEND options "-DBUILD_OFI_TRANSPORT=ON") diff --git a/README.md b/README.md index dfdb52a4..16e33439 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,9 @@ When building FairMQ, CMake will print a summary table of all available package * [asiofi](https://github.com/FairRootGroup/asiofi) * [Boost](https://www.boost.org/) * [CMake](https://cmake.org/) - * [DDS](http://dds.gsi.de) * [Doxygen](http://www.doxygen.org/) * [FairCMakeModules](https://github.com/FairRootGroup/FairCMakeModules) (optionally bundled) * [FairLogger](https://github.com/FairRootGroup/FairLogger) - * [Flatbuffers](https://google.github.io/flatbuffers/) * [GTest](https://github.com/google/googletest) (optionally bundled) * [PMIx](https://pmix.org/) * [ZeroMQ](http://zeromq.org/) @@ -161,6 +159,4 @@ After the `find_package(FairMQ)` call the following CMake variables are defined: 1. [Usage](docs/Plugins.md#71-usage) 2. [Development](docs/Plugins.md#72-development) 3. [Provided Plugins](docs/Plugins.md#73-provided-plugins) - 1. [DDS](docs/Plugins.md#731-dds) - 2. [PMIx](docs/Plugins.md#732-pmix) -8. [Controller SDK](docs/SDK.md) + 2. [PMIx](docs/Plugins.md#731-pmix) diff --git a/cmake/FairMQDependencies.cmake b/cmake/FairMQDependencies.cmake index ef8c9b6f..d553cf3c 100644 --- a/cmake/FairMQDependencies.cmake +++ b/cmake/FairMQDependencies.cmake @@ -12,7 +12,7 @@ include(FairCMakeModules) include(FairFindPackage2) include(FairMQBundlePackageHelper) -if(BUILD_FAIRMQ OR BUILD_SDK) +if(BUILD_FAIRMQ) set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package2(PUBLIC Threads REQUIRED) set(Threads_PREFIX "") @@ -23,28 +23,18 @@ if(BUILD_OFI_TRANSPORT) find_package2(PRIVATE OFI REQUIRED) endif() -if(BUILD_SDK_COMMANDS) - find_package2(PRIVATE Flatbuffers REQUIRED) -endif() - -if(BUILD_DDS_PLUGIN OR BUILD_SDK) - find_package2(PRIVATE DDS REQUIRED VERSION 3.5.13.7) - set(DDS_Boost_COMPONENTS system log log_setup regex filesystem thread) - set(DDS_Boost_VERSION 1.67) -endif() - if(BUILD_PMIX_PLUGIN) find_package2(PRIVATE PMIx REQUIRED VERSION 2.1.4) endif() -if(BUILD_FAIRMQ OR BUILD_SDK OR BUILD_TIDY_TOOL) +if(BUILD_FAIRMQ OR BUILD_TIDY_TOOL) find_package2(PUBLIC FairLogger REQUIRED VERSION 1.6.0) find_package2(PUBLIC Boost REQUIRED VERSION 1.66 COMPONENTS container program_options filesystem date_time regex ) endif() -if(BUILD_OFI_TRANSPORT OR BUILD_SDK OR BUILD_DDS_PLUGIN) +if(BUILD_OFI_TRANSPORT) set(__old ${CMAKE_FIND_PACKAGE_PREFER_CONFIG}) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) find_package2(PUBLIC asio REQUIRED VERSION 1.18) @@ -101,8 +91,6 @@ if(PROJECT_PACKAGE_DEPENDENCIES) if(NOT asiofi_PREFIX AND asiofi_ROOT) set(asiofi_PREFIX ${asiofi_ROOT}) endif() - elseif(${dep} STREQUAL DDS) - set(DDS_PREFIX "${DDS_INSTALL_PREFIX}") elseif(${dep} STREQUAL Boost) if(TARGET Boost::headers) get_target_property(boost_include Boost::headers INTERFACE_INCLUDE_DIRECTORIES) @@ -116,14 +104,6 @@ if(PROJECT_PACKAGE_DEPENDENCIES) get_filename_component(Doxygen_PREFIX ${doxygen_bin} DIRECTORY) get_filename_component(Doxygen_PREFIX ${Doxygen_PREFIX}/.. ABSOLUTE) unset(doxygen_bin) - elseif(${dep} STREQUAL Flatbuffers) - if(TARGET flatbuffers::flatbuffers) - get_target_property(flatbuffers_include flatbuffers::flatbuffers INTERFACE_INCLUDE_DIRECTORIES) - else() - get_target_property(flatbuffers_include flatbuffers::flatbuffers_shared INTERFACE_INCLUDE_DIRECTORIES) - endif() - get_filename_component(Flatbuffers_PREFIX ${flatbuffers_include}/.. ABSOLUTE) - unset(flatbuffers_include) elseif(NOT ${dep}_PREFIX) # try to guess if(TARGET ${dep}::${dep}) diff --git a/cmake/FairMQSummary.cmake b/cmake/FairMQSummary.cmake index 8b302815..89f4cec9 100644 --- a/cmake/FairMQSummary.cmake +++ b/cmake/FairMQSummary.cmake @@ -1,5 +1,5 @@ ################################################################################ -# Copyright (C) 2018-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # +# Copyright (C) 2018-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # # # This software is distributed under the terms of the # # GNU Lesser General Public Licence (LGPL) version 3, # @@ -33,12 +33,6 @@ macro(fairmq_summary_components) set(ofi_summary "${BRed} NO${CR} EXPERIMENTAL (default, enable with ${BMagenta}-DBUILD_OFI_TRANSPORT=ON${CR})") endif() message(STATUS " ${BWhite}ofi_transport${CR} ${ofi_summary}") - if(BUILD_DDS_PLUGIN) - set(dds_summary "${BGreen}YES${CR} DEPRECATED (disable with ${BMagenta}-DBUILD_DDS_PLUGIN=OFF${CR})") - else() - set(dds_summary "${BRed} NO${CR} DEPRECATED (default, enable with ${BMagenta}-DBUILD_DDS_PLUGIN=ON${CR})") - endif() - message(STATUS " ${BWhite}dds_plugin${CR} ${dds_summary}") if(BUILD_PMIX_PLUGIN) set(pmix_summary "${BGreen}YES${CR} EXPERIMENTAL (disable with ${BMagenta}-DBUILD_PMIX_PLUGIN=OFF${CR})") else() @@ -57,39 +51,12 @@ macro(fairmq_summary_components) set(docs_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_DOCS=ON${CR})") endif() message(STATUS " ${BWhite}docs${CR} ${docs_summary}") - if(BUILD_SDK) - set(sdk_summary "${BGreen}YES${CR} DEPRECATED (disable with ${BMagenta}-DBUILD_SDK=OFF${CR})") - else() - set(sdk_summary "${BRed} NO${CR} DEPRECATED (default, enable with ${BMagenta}-DBUILD_SDK=ON${CR})") - endif() - message(STATUS " ${BWhite}sdk${CR} ${sdk_summary}") - if(BUILD_SDK_COMMANDS) - set(sdk_commands_summary "${BGreen}YES${CR} DEPRECATED (disable with ${BMagenta}-DBUILD_SDK_COMMANDS=OFF${CR})") - else() - set(sdk_commands_summary "${BRed} NO${CR} DEPRECATED (default, enable with ${BMagenta}-DBUILD_SDK_COMMANDS=ON${CR})") - endif() - message(STATUS " ${BWhite}sdk_commands${CR} ${sdk_commands_summary}") if(BUILD_TIDY_TOOL) set(sdk_tidy_summary "${BGreen}YES${CR} (disable with ${BMagenta}-DBUILD_TIDY_TOOL=OFF${CR})") else() set(sdk_tidy_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_TIDY_TOOL=ON${CR})") endif() message(STATUS " ${BWhite}tidy_tool${CR} ${sdk_tidy_summary}") - - set(_deprecated) - if(BUILD_SDK) - list(APPEND _deprecated sdk) - endif() - if(BUILD_SDK_COMMANDS) - list(APPEND _deprecated sdk_commands) - endif() - if(BUILD_DDS_PLUGIN) - list(APPEND _deprecated dds_plugin) - endif() - list(JOIN _deprecated ", " _deprecated) - if(_deprecated) - message(DEPRECATION "You have selected to build deprecated components '${_deprecated}' which will be removed in a future release. See https://github.com/FairRootGroup/FairMQ/discussions/392 for more information. Use '-Wno-deprecated' to silence deprecation warnings.") - endif() endmacro() macro(fairmq_summary_static_analysis) diff --git a/docs/Plugins.md b/docs/Plugins.md index 4c2e6a43..57ee384b 100644 --- a/docs/Plugins.md +++ b/docs/Plugins.md @@ -54,37 +54,7 @@ A more complete example which may serve as a start including example CMake code ## 7.3 Provided Plugins -### 7.3.1 DDS - -When launching a FairMQ topology via [DDS](http://dds.gsi.de/) the DDS plugin enables FairMQ devices to interact with DDS' custom command and property subsystems - enable the plugin by passing `-P dds` on the command line. - -Via the property subsystem a FairMQ topology may exchange channel connection data (essentially to do service discovery) needed to connect/bind all FairMQ channels appropriately. DDS is highly optimized for this use case. See [examples/dds](examples/dds/README.md) for more details. - -Via the custom command subsystem a FairMQ device can receive a number of commands. FairMQ provides a convenient command line tool `fairmq-dds-command-ui` that allows interactive or scripted control of a running FairMQ topology managed via DDS. If one develops directly against the custom command DDS API, the following table lists all the commands the DDS plugin currently understands: - -| Custom Command | Response | Error | Description | -| --- | --- | --- | --- | -| `check-state` | `: (pid: )` | n/a | Query current device state, see state machine for possible states | -| `dump-config` | `(: -> \n)+` | n/a | Query current device config (list property key/value pairs) | -| `INIT DEVICE` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `BIND` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `CONNECT` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `INIT TASK` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `RUN` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `STOP` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `RESET TASK` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `RESET DEVICE` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `END` | `: queued transition` | `: could not queue transition` | Initiate state transition | -| `subscribe-to-heartbeats` | `heartbeat-subscription: ,OK` | n/a | Subscribe to heartbeats | -| on heartbeat subscription | `heartbeat: ,` | n/a | Heartbeat every 100ms | -| `unsubscribe-from-heartbeats` | `heartbeat-unsubscription: ,OK` | n/a | Unsubscribe from heartbeats | -| `subscribe-to-state-changes` | `state-changes-subscription: ,OK` | n/a | Subscribe to state changes | -| on state changes subscription | `state-change: ,` | n/a | State change notification | -| `unsubscribe-from-state-changes` | `state-changes-unsubscription: ,OK` | n/a | Unsubscribe from state changes | - -If unknown commands are received the plugin will print a warning. - -### 7.3.2 PMIx +### 7.3.1 PMIx The [PMIx](https://pmix.org/) plugin enables launching a FairMQ topology with any PMIx capable launcher, e.g. the [Open Run-Time Environment (ORTE) of OpenMPI](https://www.open-mpi.org/doc/v4.0/man1/mpirun.1.php) or the [Slurm workload manager](https://slurm.schedmd.com/srun.html). This plugin is not (yet) very mature and serves as a proof of concept at the moment. diff --git a/docs/SDK.md b/docs/SDK.md deleted file mode 100644 index 724631e9..00000000 --- a/docs/SDK.md +++ /dev/null @@ -1,34 +0,0 @@ -← [Back](../README.md) - -# 8. Controller SDK - -The FairMQ Controller Software Development Kit (`-DBUILD_SDK=ON`) contains a (as of today still experimental) set of C++ APIs that provide essential functionality to the implementer of a global controller. - -The FairMQ core library only provides two local controllers - `static` (a fixed sequence of state transitions) and `interactive` (a read-eval-print-loop which reads keyboard commands from standard input). A local controller only knows how steer a single [FairMQ device](Device.md) - in fact, it runs in a thread within the device process. - -A global controller has knowledge about the full topology of connected FairMQ devices. Its responsibility is to facilitate the lifecycle of a distributed FairMQ-based application (*executing a topology*), such as - -* allocating/releasing compute resources from a resource management system, -* launching/setting up the run-time environment and the FairMQ devices, -* driving the device state machines in lock-step across the full topology, -* pushing the device configuration, -* monitoring (some aspects of the application's) operation, -* and handling/reporting (some) error cases. - -The low-level hook to integrate FairMQ devices with such a global contoller is the [plugin mechanism](Plugins.md) in the FairMQ core library. The FairMQ Controller SDK provides C++ APIs that communicate to the endpoints exposed by such a FairMQ plugin. - -At the moment, the Controller SDK only supports [DDS](https://dds.gsi.de) as resource manager and run-time environment. A second implementation based on [PMIx](https://pmix.org/) (targeting its implementation in [Slurm](https://slurm.schedmd.com/documentation.html) and [OpenRTE](https://www-lb.open-mpi.org/papers/euro-pvmmpi-2005-orte/)) is in development. - -The following section give a short overview on the APIs provided. - -## RMS and run-time environment - -The classes [`fair::mq::sdk::DDSEnvironment`](../fairmq/sdk/DDSEnvironment.h), [`fair::mq::sdk::DDSSession`](../fairmq/sdk/DDSSession.h), and [`fair::mq::sdk::DDSTopology`](../fairmq/sdk/DDSTopology.h) are thin wrappers of most of the synchronous APIs exposed by DDS ([`dds::tools_api`](http://dds.gsi.de/doc/api-docs/DDS/html/namespacedds_1_1tools__api.html) and [`dds::topology_api`](http://dds.gsi.de/doc/api-docs/DDS/html/namespacedds_1_1topology__api.html)). E.g. they allow to [start a DDS session](https://github.com/FairRootGroup/FairMQ/blob/077eb0ef691940d764cfd1852bf3981dc812ddbd/main.cpp#L26-L28), [allocate resources](https://github.com/FairRootGroup/FairMQ/blob/077eb0ef691940d764cfd1852bf3981dc812ddbd/main.cpp#L34) and [launch a topology](https://github.com/FairRootGroup/FairMQ/blob/077eb0ef691940d764cfd1852bf3981dc812ddbd/main.cpp#L39) from a C++ program. - -## Driving the global state machine - -The class [`fair::mq::sdk::Topology`](../fairmq/sdk/Topology.h) adds a FairMQ-specific view on an existing DDS session that is executing a topology of FairMQ devices. One can e.g. [initiate a state transition on all devices in the topology simultaneously](https://github.com/FairRootGroup/FairMQ/blob/077eb0ef691940d764cfd1852bf3981dc812ddbd/main.cpp#L48-L49). This topology transition completes once a topology-wide barrier is passed (all devices completed the transition). This effectively exposes the device state machine as a topology state machine. The implementation is based on remote procedure calls over the [DDS intercom service](http://dds.gsi.de/doc/api-docs/DDS/html/namespacedds_1_1intercom__api.html) between the controller and the DDS plugin shipped with FairMQ (`-DBUILD_DDS_PLUGIN=ON`). - -For future versions of the SDK new APIs are planned to inspect and modify the device configurations and also operate only on subsets of a given topology. - -← [Back](../README.md) diff --git a/fairmq/CMakeLists.txt b/fairmq/CMakeLists.txt index 5ff7b7d4..a28b109d 100644 --- a/fairmq/CMakeLists.txt +++ b/fairmq/CMakeLists.txt @@ -1,12 +1,12 @@ ################################################################################ -# Copyright (C) 2012-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # +# Copyright (C) 2012-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # # # This software is distributed under the terms of the # # GNU Lesser General Public Licence (LGPL) version 3, # # copied verbatim in the file "LICENSE" # ################################################################################ -if(BUILD_FAIRMQ OR BUILD_SDK) +if(BUILD_FAIRMQ) if(BUILD_TIDY_TOOL) include(FairMQTidy) @@ -452,20 +452,9 @@ if(BUILD_FAIRMQ) endforeach() endif() -if(BUILD_SDK_COMMANDS) - add_subdirectory(sdk/commands) -endif() - -if(BUILD_SDK) - add_subdirectory(sdk) -endif() - #################### # external plugins # #################### -if(BUILD_DDS_PLUGIN) - add_subdirectory(plugins/DDS) -endif() if(BUILD_PMIX_PLUGIN) add_subdirectory(plugins/PMIx) endif() diff --git a/fairmq/SDK.h b/fairmq/SDK.h deleted file mode 100644 index c1c32157..00000000 --- a/fairmq/SDK.h +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_H -#define FAIR_MQ_SDK_H - -// IWYU pragma: begin_exports -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// IWYU pragma: end_exports - -#endif // FAIR_MQ_SDK_H diff --git a/fairmq/plugins/DDS/CMakeLists.txt b/fairmq/plugins/DDS/CMakeLists.txt deleted file mode 100644 index 5efc2572..00000000 --- a/fairmq/plugins/DDS/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Copyright (C) 2012-2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # -# # -# This software is distributed under the terms of the # -# GNU Lesser General Public Licence (LGPL) version 3, # -# copied verbatim in the file "LICENSE" # -################################################################################ - -set(plugin FairMQPlugin_dds) -add_library(${plugin} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/DDS.cxx ${CMAKE_CURRENT_SOURCE_DIR}/DDS.h) -target_compile_features(${plugin} PUBLIC cxx_std_17) -target_link_libraries(${plugin} PUBLIC FairMQ StateMachine DDS::dds_intercom_lib DDS::dds_protocol_lib Boost::boost PRIVATE Commands asio::asio) -target_include_directories(${plugin} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(${plugin} PROPERTIES CXX_VISIBILITY_PRESET hidden) -set_target_properties(${plugin} PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/fairmq -) - -install(TARGETS ${plugin} - EXPORT ${PROJECT_EXPORT_SET} - LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR} -) diff --git a/fairmq/plugins/DDS/DDS.cxx b/fairmq/plugins/DDS/DDS.cxx deleted file mode 100644 index f17d4342..00000000 --- a/fairmq/plugins/DDS/DDS.cxx +++ /dev/null @@ -1,457 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "DDS.h" - -#include - -#include -#include -#include -#include - -#include -#include -#include - -using namespace std; -using fair::mq::tools::ToString; - -namespace fair::mq::plugins -{ - -DDS::DDS(const string& name, - const Plugin::Version version, - const string& maintainer, - const string& homepage, - PluginServices* pluginServices) - : Plugin(name, version, maintainer, homepage, pluginServices) - , fDDSTaskId(dds::env_prop()) - , fCurrentState(DeviceState::Idle) - , fLastState(DeviceState::Idle) - , fDeviceTerminationRequested(false) - , fLastExternalController(0) - , fExitingAckedByLastExternalController(false) - , fUpdatesAllowed(false) - , fWorkGuard(fWorkerQueue.get_executor()) -{ - try { - TakeDeviceControl(); - - string deviceId(GetProperty("id")); - if (deviceId.empty()) { - SetProperty("id", dds::env_prop()); - } - string sessionId(GetProperty("session")); - if (sessionId == "default") { - SetProperty("session", dds::env_prop()); - } - - auto control = GetProperty("control"); - if (control == "static") { - LOG(error) << "DDS Plugin: static mode is not supported"; - throw invalid_argument("DDS Plugin: static mode is not supported"); - } else if (control == "dynamic" || control == "external" || control == "interactive") { - LOG(debug) << "Running DDS controller: external"; - } else { - LOG(error) << "Unrecognized control mode '" << control << "' requested. " << "Ignoring and starting in external control mode."; - } - - SubscribeForCustomCommands(); - SubscribeForConnectingChannels(); - - // subscribe to device state changes, pushing new state changes into the event queue - SubscribeToDeviceStateChange([&](DeviceState newState) { - switch (newState) { - case DeviceState::Bound: { - // Receive addresses of connecting channels from DDS - // and propagate addresses of bound channels to DDS. - FillChannelContainers(); - - // allow updates from key value after channel containers are filled - { - lock_guard lk(fUpdateMutex); - fUpdatesAllowed = true; - } - fUpdateCondition.notify_one(); - - // publish bound addresses via DDS at keys corresponding to the channel - // prefixes, e.g. 'data' in data[i] - PublishBoundChannels(); - } break; - case DeviceState::ResettingDevice: { - { - lock_guard lk(fUpdateMutex); - fUpdatesAllowed = false; - } - - EmptyChannelContainers(); - } break; - case DeviceState::Exiting: { - if (!fControllerThread.joinable()) { - fControllerThread = thread(&DDS::WaitForExitingAck, this); - } - fWorkGuard.reset(); - fDeviceTerminationRequested = true; - UnsubscribeFromDeviceStateChange(); - ReleaseDeviceControl(); - } break; - default: - break; - } - - using namespace sdk::cmd; - auto now = chrono::steady_clock::now(); - string id = GetProperty("id"); - fLastState = fCurrentState; - fCurrentState = newState; - - lock_guard lock{fStateChangeSubscriberMutex}; - for (auto it = fStateChangeSubscribers.cbegin(); it != fStateChangeSubscribers.end();) { - // if a subscriber did not send a heartbeat in more than 3 times the promised interval, - // remove it from the subscriber list - if (chrono::duration(now - it->second.first).count() > 3 * it->second.second) { - LOG(warn) << "Controller '" << it->first - << "' did not send heartbeats since over 3 intervals (" - << 3 * it->second.second << " ms), removing it."; - fStateChangeSubscribers.erase(it++); - } else { - LOG(debug) << "Publishing state-change: " << fLastState << "->" << fCurrentState << " to " << it->first; - Cmds cmds(make(id, fDDSTaskId, fLastState, fCurrentState)); - fDDS.Send(cmds.Serialize(), to_string(it->first)); - ++it; - } - } - }); - - StartWorkerThread(); - - fDDS.Start(); - } catch (PluginServices::DeviceControlError& e) { - LOG(debug) << e.what(); - } catch (exception& e) { - LOG(error) << "Error in plugin initialization: " << e.what(); - } -} - -void DDS::EmptyChannelContainers() -{ - fBindingChans.clear(); - fConnectingChans.clear(); -} - -auto DDS::StartWorkerThread() -> void -{ - fWorkerThread = thread([this]() { - fWorkerQueue.run(); - }); -} - -auto DDS::WaitForExitingAck() -> void -{ - unique_lock lock(fStateChangeSubscriberMutex); - auto timeout = GetProperty("wait-for-exiting-ack-timeout"); - fExitingAcked.wait_for(lock, chrono::milliseconds(timeout), [this]() { - return fExitingAckedByLastExternalController || fStateChangeSubscribers.empty(); - }); -} - -auto DDS::FillChannelContainers() -> void -{ - try { - unordered_map channelInfo(GetChannelInfo()); - - // fill binding and connecting chans - for (const auto& c : channelInfo) { - string methodKey{"chans." + c.first + "." + to_string(c.second - 1) + ".method"}; - if (GetProperty(methodKey) == "bind") { - fBindingChans.insert(make_pair(c.first, vector())); - for (int i = 0; i < c.second; ++i) { - fBindingChans.at(c.first).push_back(GetProperty(string{"chans." + c.first + "." + to_string(i) + ".address"})); - } - } else if (GetProperty(methodKey) == "connect") { - fConnectingChans.insert(make_pair(c.first, DDSConfig())); - LOG(debug) << "preparing to connect: " << c.first << " with " << c.second << " sub-channels."; - fConnectingChans.at(c.first).fNumSubChannels = c.second; - } else { - LOG(error) << "Cannot update address configuration. Channel method (bind/connect) not specified."; - return; - } - } - - // save properties that will have multiple values arriving (with only some of them to be used) - vector iValues; - if (PropertyExists("dds-i")) { - iValues = GetProperty>("dds-i"); - } - vector inValues; - if (PropertyExists("dds-i-n")) { - inValues = GetProperty>("dds-i-n"); - } - - for (const auto& vi : iValues) { - size_t pos = vi.find(":"); - string chanName = vi.substr(0, pos); - - // check if provided name is a valid channel name - if (fConnectingChans.find(chanName) == fConnectingChans.end()) { - throw invalid_argument(ToString("channel provided to dds-i is not an actual connecting channel of this device: ", chanName)); - } - - int i = stoi(vi.substr(pos + 1)); - LOG(debug) << "dds-i: adding " << chanName << " -> i of " << i; - fI.insert(make_pair(chanName, i)); - } - - for (const auto& vi : inValues) { - size_t pos = vi.find(":"); - string chanName = vi.substr(0, pos); - - // check if provided name is a valid channel name - if (fConnectingChans.find(chanName) == fConnectingChans.end()) { - throw invalid_argument(ToString("channel provided to dds-i-n is not an actual connecting channel of this device: ", chanName)); - } - - string i_n = vi.substr(pos + 1); - pos = i_n.find("-"); - int i = stoi(i_n.substr(0, pos)); - int n = stoi(i_n.substr(pos + 1)); - LOG(debug) << "dds-i-n: adding " << chanName << " -> i: " << i << " n: " << n; - fIofN.insert(make_pair(chanName, IofN(i, n))); - } - } catch (const exception& e) { - LOG(error) << "Error filling channel containers: " << e.what(); - } -} - -auto DDS::SubscribeForConnectingChannels() -> void -{ - LOG(debug) << "Subscribing for DDS properties."; - - fDDS.SubscribeKeyValue([&] (const string& key, const string& value, uint64_t senderTaskID) { - LOG(debug) << "Received property: key=" << key << ", value=" << value << ", senderTaskID=" << senderTaskID; - - if (key.compare(0, 8, "fmqchan_") != 0) { - LOG(debug) << "property update is not a channel info update: " << key; - return; - } - string channelName = key.substr(8); - LOG(info) << "Update for channel name: " << channelName; - - asio::post(fWorkerQueue, [=]() { - try { - { - unique_lock lk(fUpdateMutex); - fUpdateCondition.wait(lk, [&]{ return fUpdatesAllowed; }); - } - - if (fConnectingChans.find(channelName) == fConnectingChans.end()) { - LOG(error) << "Received an update for a connecting channel, but either no channel with given channel name exists or it has already been configured: '" << channelName << "', ignoring..."; - return; - } - - string val = value; - // check if it is to handle as one out of multiple values - auto it = fIofN.find(channelName); - if (it != fIofN.end()) { - it->second.fEntries.push_back(value); - if (it->second.fEntries.size() == it->second.fN) { - sort(it->second.fEntries.begin(), it->second.fEntries.end()); - val = it->second.fEntries.at(it->second.fI); - } else { - LOG(debug) << "received " << it->second.fEntries.size() << " values for " << channelName << ", expecting total of " << it->second.fN; - return; - } - } - - vector connectionStrings; - boost::algorithm::split(connectionStrings, val, boost::algorithm::is_any_of(",")); - if (connectionStrings.size() > 1) { // multiple bound channels received - auto it2 = fI.find(channelName); - if (it2 != fI.end()) { - LOG(debug) << "adding connecting channel " << channelName << " : " << connectionStrings.at(it2->second); - fConnectingChans.at(channelName).fDDSValues.insert({senderTaskID, connectionStrings.at(it2->second).c_str()}); - } else { - LOG(error) << "multiple bound channels received, but no task index specified, only assigning the first"; - fConnectingChans.at(channelName).fDDSValues.insert({senderTaskID, connectionStrings.at(0).c_str()}); - } - } else { // only one bound channel received - fConnectingChans.at(channelName).fDDSValues.insert({senderTaskID, val.c_str()}); - } - - for (const auto& mi : fConnectingChans) { - if (mi.second.fNumSubChannels == mi.second.fDDSValues.size()) { - int i = 0; - for (const auto& e : mi.second.fDDSValues) { - auto result = UpdateProperty(string{"chans." + mi.first + "." + to_string(i) + ".address"}, e.second); - if (!result) { - LOG(error) << "UpdateProperty failed for: " << "chans." << mi.first << "." << to_string(i) << ".address" << " - property does not exist"; - } - ++i; - } - } - } - } catch (const exception& e) { - LOG(error) << "Error handling DDS property: key=" << key << ", value=" << value << ", senderTaskID=" << senderTaskID << ": " << e.what(); - } - }); - }); -} - -auto DDS::PublishBoundChannels() -> void -{ - for (const auto& chan : fBindingChans) { - string joined = boost::algorithm::join(chan.second, ","); - LOG(debug) << "Publishing bound addresses (" << chan.second.size() << ") of channel '" << chan.first << "' to DDS under '" << "fmqchan_" + chan.first << "' property name."; - fDDS.PutValue("fmqchan_" + chan.first, joined); - } -} - -auto DDS::SubscribeForCustomCommands() -> void -{ - LOG(debug) << "Subscribing for DDS custom commands."; - - string id = GetProperty("id"); - - fDDS.SubscribeCustomCmd([id, this](const string& cmdStr, const string& cond, uint64_t senderId) { - // LOG(info) << "Received command: '" << cmdStr << "' from " << senderId; - sdk::cmd::Cmds inCmds; - inCmds.Deserialize(cmdStr); - for (const auto& cmd : inCmds) { - HandleCmd(id, *cmd, cond, senderId); - } - }); -} - -auto DDS::HandleCmd(const string& id, sdk::cmd::Cmd& cmd, const string& cond, uint64_t senderId) -> void -{ - using namespace fair::mq::sdk; - using namespace fair::mq::sdk::cmd; - // LOG(info) << "Received command type: '" << cmd.GetType() << "' from " << senderId; - switch (cmd.GetType()) { - case Type::check_state: { - fDDS.Send(Cmds(make(id, GetCurrentDeviceState())).Serialize(), to_string(senderId)); - } break; - case Type::change_state: { - Transition transition = static_cast(cmd).GetTransition(); - if (ChangeDeviceState(transition)) { - Cmds outCmds(make(id, fDDSTaskId, Result::Ok, transition, GetCurrentDeviceState())); - fDDS.Send(outCmds.Serialize(), to_string(senderId)); - } else { - Cmds outCmds(make(id, fDDSTaskId, Result::Failure, transition, GetCurrentDeviceState())); - fDDS.Send(outCmds.Serialize(), to_string(senderId)); - } - { - lock_guard lock{fStateChangeSubscriberMutex}; - fLastExternalController = senderId; - } - } break; - case Type::dump_config: { - stringstream ss; - for (const auto& pKey : GetPropertyKeys()) { - ss << id << ": " << pKey << " -> " << GetPropertyAsString(pKey) << "\n"; - } - Cmds outCmds(make(id, ss.str())); - fDDS.Send(outCmds.Serialize(), to_string(senderId)); - } break; - case Type::state_change_exiting_received: { - { - lock_guard lock{fStateChangeSubscriberMutex}; - if (fLastExternalController == senderId) { - fExitingAckedByLastExternalController = true; - } - } - fExitingAcked.notify_one(); - } break; - case Type::subscribe_to_state_change: { - auto _cmd = static_cast(cmd); - lock_guard lock{fStateChangeSubscriberMutex}; - fStateChangeSubscribers.emplace(senderId, make_pair(chrono::steady_clock::now(), _cmd.GetInterval())); - - LOG(debug) << "Publishing state-change: " << fLastState << "->" << fCurrentState << " to " << senderId; - - Cmds outCmds(make(id, fDDSTaskId, Result::Ok), - make(id, fDDSTaskId, fLastState, fCurrentState)); - - fDDS.Send(outCmds.Serialize(), to_string(senderId)); - } break; - case Type::subscription_heartbeat: { - try { - auto _cmd = static_cast(cmd); - lock_guard lock{fStateChangeSubscriberMutex}; - fStateChangeSubscribers.at(senderId) = make_pair(chrono::steady_clock::now(), _cmd.GetInterval()); - } catch(out_of_range& oor) { - LOG(warn) << "Received subscription heartbeat from an unknown controller with id '" << senderId << "'"; - } - } break; - case Type::unsubscribe_from_state_change: { - { - lock_guard lock{fStateChangeSubscriberMutex}; - fStateChangeSubscribers.erase(senderId); - } - Cmds outCmds(make(id, fDDSTaskId, Result::Ok)); - fDDS.Send(outCmds.Serialize(), to_string(senderId)); - } break; - case Type::get_properties: { - auto _cmd = static_cast(cmd); - auto const request_id(_cmd.GetRequestId()); - auto result(Result::Ok); - vector> props; - try { - for (auto const& prop : GetPropertiesAsString(_cmd.GetQuery())) { - props.push_back({prop.first, prop.second}); - } - } catch (exception const& e) { - LOG(warn) << "Getting properties (request id: " << request_id << ") failed: " << e.what(); - result = Result::Failure; - } - Cmds const outCmds(make(id, request_id, result, props)); - fDDS.Send(outCmds.Serialize(), to_string(senderId)); - } break; - case Type::set_properties: { - auto _cmd(static_cast(cmd)); - auto const request_id(_cmd.GetRequestId()); - auto result(Result::Ok); - try { - fair::mq::Properties props; - for (auto const& prop : _cmd.GetProps()) { - props.insert({prop.first, fair::mq::Property(prop.second)}); - } - // TODO Handle builtin keys with different value type than string - SetProperties(props); - } catch (exception const& e) { - LOG(warn) << "Setting properties (request id: " << request_id << ") failed: " << e.what(); - result = Result::Failure; - } - Cmds const outCmds(make(id, request_id, result)); - fDDS.Send(outCmds.Serialize(), to_string(senderId)); - } break; - default: - LOG(warn) << "Unexpected/unknown command received: " << cmd.GetType(); - LOG(warn) << "Origin: " << senderId; - LOG(warn) << "Destination: " << cond; - break; - } -} - -DDS::~DDS() -{ - UnsubscribeFromDeviceStateChange(); - ReleaseDeviceControl(); - - if (fControllerThread.joinable()) { - fControllerThread.join(); - } - - fWorkGuard.reset(); - if (fWorkerThread.joinable()) { - fWorkerThread.join(); - } -} - -} // namespace fair::mq::plugins diff --git a/fairmq/plugins/DDS/DDS.h b/fairmq/plugins/DDS/DDS.h deleted file mode 100644 index fdea789e..00000000 --- a/fairmq/plugins/DDS/DDS.h +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_PLUGINS_DDS -#define FAIR_MQ_PLUGINS_DDS - -#include -#include -#include -#include - -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#undef BOOST_BIND_GLOBAL_PLACEHOLDERS - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // pair -#include - -namespace fair::mq::plugins -{ - -struct DDSConfig -{ - // container of sub channel addresses - unsigned int fNumSubChannels; - // dds values for the channel - std::map fDDSValues; -}; - -struct DDSSubscription -{ - DDSSubscription() - : fDDSCustomCmd(fService) - , fDDSKeyValue(fService) - { - LOG(debug) << "$DDS_TASK_PATH: " << dds::env_prop(); - LOG(debug) << "$DDS_GROUP_NAME: " << dds::env_prop(); - LOG(debug) << "$DDS_COLLECTION_NAME: " << dds::env_prop(); - LOG(debug) << "$DDS_TASK_NAME: " << dds::env_prop(); - LOG(debug) << "$DDS_TASK_INDEX: " << dds::env_prop(); - LOG(debug) << "$DDS_COLLECTION_INDEX: " << dds::env_prop(); - LOG(debug) << "$DDS_TASK_ID: " << dds::env_prop(); - LOG(debug) << "$DDS_LOCATION: " << dds::env_prop(); - std::string dds_session_id(dds::env_prop()); - LOG(debug) << "$DDS_SESSION_ID: " << dds_session_id; - - // subscribe for DDS service errors. - fService.subscribeOnError([](const dds::intercom_api::EErrorCode errorCode, const std::string& errorMsg) { - LOG(error) << "DDS Error received: error code: " << errorCode << ", error message: " << errorMsg; - }); - - // fDDSCustomCmd.subscribe([](const std::string& cmd, const std::string& cond, uint64_t senderId) { - // LOG(debug) << "cmd: " << cmd << ", cond: " << cond << ", senderId: " << senderId; - // }); - assert(!dds_session_id.empty()); - } - - auto Start() -> void { - fService.start(dds::env_prop()); - } - - ~DDSSubscription() { - fDDSKeyValue.unsubscribe(); - fDDSCustomCmd.unsubscribe(); - } - - template - auto SubscribeCustomCmd(Args&&... args) -> void - { - fDDSCustomCmd.subscribe(std::forward(args)...); - } - - template - auto SubscribeKeyValue(Args&&... args) -> void - { - fDDSKeyValue.subscribe(std::forward(args)...); - } - - template - auto Send(Args&&... args) -> void - { - fDDSCustomCmd.send(std::forward(args)...); - } - - template - auto PutValue(Args&&... args) -> void - { - fDDSKeyValue.putValue(std::forward(args)...); - } - - private: - dds::intercom_api::CIntercomService fService; - dds::intercom_api::CCustomCmd fDDSCustomCmd; - dds::intercom_api::CKeyValue fDDSKeyValue; -}; - -struct IofN -{ - IofN(int i, int n) - : fI(i) - , fN(n) - {} - - unsigned int fI; - unsigned int fN; - std::vector fEntries; -}; - -class DDS : public Plugin -{ - public: - DDS(const std::string& name, const Plugin::Version version, const std::string& maintainer, const std::string& homepage, PluginServices* pluginServices); - - ~DDS(); - - private: - auto WaitForExitingAck() -> void; - auto StartWorkerThread() -> void; - - auto FillChannelContainers() -> void; - auto EmptyChannelContainers() -> void; - - auto SubscribeForConnectingChannels() -> void; - auto PublishBoundChannels() -> void; - auto SubscribeForCustomCommands() -> void; - auto HandleCmd(const std::string& id, sdk::cmd::Cmd& cmd, const std::string& cond, uint64_t senderId) -> void; - - DDSSubscription fDDS; - size_t fDDSTaskId; - - std::unordered_map> fBindingChans; - std::unordered_map fConnectingChans; - - std::unordered_map fI; - std::unordered_map fIofN; - - std::thread fControllerThread; - DeviceState fCurrentState, fLastState; - - std::atomic fDeviceTerminationRequested; - - std::unordered_map> fStateChangeSubscribers; - uint64_t fLastExternalController; - bool fExitingAckedByLastExternalController; - std::condition_variable fExitingAcked; - std::mutex fStateChangeSubscriberMutex; - - bool fUpdatesAllowed; - std::mutex fUpdateMutex; - std::condition_variable fUpdateCondition; - - std::thread fWorkerThread; - asio::io_context fWorkerQueue; - asio::executor_work_guard fWorkGuard; -}; - -Plugin::ProgOptions DDSProgramOptions() -{ - boost::program_options::options_description options{"DDS Plugin"}; - options.add_options() - ("dds-i", boost::program_options::value>()->multitoken()->composing(), "Task index for chosing connection target (single channel n to m). When all values come via same update.") - ("dds-i-n", boost::program_options::value>()->multitoken()->composing(), "Task index for chosing connection target (one out of n values to take). When values come as independent updates.") - ("wait-for-exiting-ack-timeout", boost::program_options::value()->default_value(1000), "Wait timeout for EXITING state-change acknowledgement by external controller in milliseconds."); - - return options; -} - -REGISTER_FAIRMQ_PLUGIN( - DDS, // Class name - dds, // Plugin name (string, lower case chars only) - (Plugin::Version{FAIRMQ_VERSION_MAJOR, - FAIRMQ_VERSION_MINOR, - FAIRMQ_VERSION_PATCH}), // Version - "FairRootGroup ", // Maintainer - "https://github.com/FairRootGroup/FairMQ", // Homepage - DDSProgramOptions // custom program options for the plugin -) - -} // namespace fair::mq::plugins - -#endif /* FAIR_MQ_PLUGINS_DDS */ diff --git a/fairmq/plugins/PMIx/CMakeLists.txt b/fairmq/plugins/PMIx/CMakeLists.txt index 9a4682f6..6abe5cc3 100644 --- a/fairmq/plugins/PMIx/CMakeLists.txt +++ b/fairmq/plugins/PMIx/CMakeLists.txt @@ -1,5 +1,5 @@ ################################################################################ -# Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # +# Copyright (C) 2019-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # # # This software is distributed under the terms of the # # GNU Lesser General Public Licence (LGPL) version 3, # @@ -14,7 +14,7 @@ add_library(${plugin} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/PMIx.hpp ) target_compile_features(${plugin} PUBLIC cxx_std_17) -target_link_libraries(${plugin} PUBLIC FairMQ PMIx::libpmix PRIVATE Commands) +target_link_libraries(${plugin} PUBLIC FairMQ PMIx::libpmix) target_include_directories(${plugin} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) set_target_properties(${plugin} PROPERTIES CXX_VISIBILITY_PRESET hidden @@ -24,7 +24,7 @@ set_target_properties(${plugin} PROPERTIES set(exe fairmq-pmix-command-ui) add_executable(${exe} ${CMAKE_CURRENT_SOURCE_DIR}/runPMIxCommandUI.cxx) -target_link_libraries(${exe} FairMQ Commands StateMachine PMIx::libpmix) +target_link_libraries(${exe} FairMQ StateMachine PMIx::libpmix) target_include_directories(${exe} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) install(TARGETS ${plugin} ${exe} diff --git a/fairmq/plugins/PMIx/PMIxPlugin.cxx b/fairmq/plugins/PMIx/PMIxPlugin.cxx index a848f72f..3e4e6502 100644 --- a/fairmq/plugins/PMIx/PMIxPlugin.cxx +++ b/fairmq/plugins/PMIx/PMIxPlugin.cxx @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * Copyright (C) 2019-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * * * This software is distributed under the terms of the * * GNU Lesser General Public Licence (LGPL) version 3, * @@ -8,7 +8,6 @@ #include "PMIxPlugin.h" -#include #include #include @@ -16,7 +15,6 @@ #include // UINT32_MAX using namespace std; -using namespace fair::mq::sdk::cmd; namespace fair::mq::plugins { @@ -31,8 +29,7 @@ PMIxPlugin::PMIxPlugin(const string& name, , fPid(getpid()) , fPMIxClient(tools::ToString("PMIx client(pid=", fPid, ") ")) , fDeviceId(string(fProcess.nspace) + "_" + to_string(fProcess.rank)) - , fCommands(fProcess) - , fLastExternalController(UINT32_MAX) + // , fLastExternalController(UINT32_MAX) , fExitingAckedByLastExternalController(false) , fCurrentState(DeviceState::Idle) , fLastState(DeviceState::Idle) @@ -42,12 +39,6 @@ PMIxPlugin::PMIxPlugin(const string& name, SetProperty("id", fDeviceId); Fence("pmix::init"); - SubscribeForCommands(); - Fence("subscribed"); - - // fCommands.Send("test1"); - // fCommands.Send("test2", 0); - // fCommands.Send("test3", 0); // LOG(info) << "PMIX_EXTERNAL_ERR_BASE: " << PMIX_EXTERNAL_ERR_BASE; @@ -101,11 +92,9 @@ PMIxPlugin::PMIxPlugin(const string& name, lock_guard lock{fStateChangeSubscriberMutex}; fLastState = fCurrentState; fCurrentState = newState; - for (auto subscriberId : fStateChangeSubscribers) { - LOG(debug) << "Publishing state-change: " << fLastState << "->" << newState << " to " << subscriberId; - Cmds cmds(make(fDeviceId, 0, fLastState, fCurrentState)); - fCommands.Send(cmds.Serialize(Format::JSON), static_cast(subscriberId)); - } + // for (auto subscriberId : fStateChangeSubscribers) { + // LOG(debug) << "Publishing state-change: " << fLastState << "->" << newState << " to " << subscriberId; + // } }); } @@ -113,7 +102,6 @@ PMIxPlugin::~PMIxPlugin() { LOG(debug) << "Destroying PMIxPlugin"; ReleaseDeviceControl(); - fCommands.Unsubscribe(); while (pmix::initialized()) { try { pmix::finalize(); @@ -124,92 +112,6 @@ PMIxPlugin::~PMIxPlugin() } } -auto PMIxPlugin::SubscribeForCommands() -> void -{ - fCommands.Subscribe([this](const string& cmdStr, const pmix::proc& sender) { - // LOG(info) << "PMIx Plugin received message: '" << cmdStr << "', from " << sender; - - Cmds inCmds; - inCmds.Deserialize(cmdStr, Format::JSON); - - for (const auto& cmd : inCmds) { - LOG(info) << "Received command type: '" << cmd->GetType() << "' from " << sender; - switch (cmd->GetType()) { - case Type::check_state: - fCommands.Send(Cmds(make(fDeviceId, GetCurrentDeviceState())) - .Serialize(Format::JSON), - {sender}); - break; - case Type::change_state: { - Transition transition = static_cast(*cmd).GetTransition(); - if (ChangeDeviceState(transition)) { - fCommands.Send( - Cmds(make(fDeviceId, 0, Result::Ok, transition, GetCurrentDeviceState())) - .Serialize(Format::JSON), - {sender}); - } else { - fCommands.Send( - Cmds(make(fDeviceId, 0, Result::Failure, transition, GetCurrentDeviceState())) - .Serialize(Format::JSON), - {sender}); - } - { - lock_guard lock{fStateChangeSubscriberMutex}; - fLastExternalController = sender.rank; - } - } - break; - case Type::subscribe_to_state_change: { - { - lock_guard lock{fStateChangeSubscriberMutex}; - fStateChangeSubscribers.insert(sender.rank); - } - - LOG(debug) << "Publishing state-change: " << fLastState << "->" << fCurrentState - << " to " << sender; - Cmds outCmds(make(fDeviceId, fProcess.rank, Result::Ok), - make(fDeviceId, 0, fLastState, fCurrentState)); - fCommands.Send(outCmds.Serialize(Format::JSON), {sender}); - } - break; - case Type::unsubscribe_from_state_change: { - { - lock_guard lock{fStateChangeSubscriberMutex}; - fStateChangeSubscribers.erase(sender.rank); - } - fCommands.Send(Cmds(make(fDeviceId, fProcess.rank, Result::Ok)) - .Serialize(Format::JSON), - {sender}); - } - break; - case Type::state_change_exiting_received: { - { - lock_guard lock{fStateChangeSubscriberMutex}; - if (fLastExternalController == sender.rank) { - fExitingAckedByLastExternalController = true; - } - } - fExitingAcked.notify_one(); - } - break; - case Type::dump_config: { - stringstream ss; - for (const auto& k: GetPropertyKeys()) { - ss << fDeviceId << ": " << k << " -> " << GetPropertyAsString(k) << "\n"; - } - fCommands.Send(Cmds(make(fDeviceId, ss.str())).Serialize(Format::JSON), - {sender}); - } - break; - default: - LOG(warn) << "Unexpected/unknown command received: " << cmdStr; - LOG(warn) << "Origin: " << sender; - break; - } - } - }); -} - auto PMIxPlugin::Init() -> pmix::proc { if (!pmix::initialized()) { diff --git a/fairmq/plugins/PMIx/PMIxPlugin.h b/fairmq/plugins/PMIx/PMIxPlugin.h index f6e8ee4e..dfcc8847 100644 --- a/fairmq/plugins/PMIx/PMIxPlugin.h +++ b/fairmq/plugins/PMIx/PMIxPlugin.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * Copyright (C) 2019-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * * * This software is distributed under the terms of the * * GNU Lesser General Public Licence (LGPL) version 3, * @@ -44,10 +44,9 @@ class PMIxPlugin : public Plugin pid_t fPid; std::string fPMIxClient; std::string fDeviceId; - pmix::Commands fCommands; std::set fStateChangeSubscribers; - uint32_t fLastExternalController; + // uint32_t fLastExternalController; bool fExitingAckedByLastExternalController; std::condition_variable fExitingAcked; std::mutex fStateChangeSubscriberMutex; @@ -61,7 +60,6 @@ class PMIxPlugin : public Plugin auto Fence(const std::string& label) -> void; auto Lookup() -> void; - auto SubscribeForCommands() -> void; auto WaitForExitingAck() -> void; }; diff --git a/fairmq/plugins/PMIx/runPMIxCommandUI.cxx b/fairmq/plugins/PMIx/runPMIxCommandUI.cxx index c88e99cb..181e231a 100644 --- a/fairmq/plugins/PMIx/runPMIxCommandUI.cxx +++ b/fairmq/plugins/PMIx/runPMIxCommandUI.cxx @@ -1,12 +1,11 @@ /******************************************************************************** - * Copyright (C) 2014-2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * Copyright (C) 2014-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * * * This software is distributed under the terms of the * * GNU Lesser General Public Licence (LGPL) version 3, * * copied verbatim in the file "LICENSE" * ********************************************************************************/ -#include #include #include @@ -29,7 +28,6 @@ #include using namespace std; -using namespace fair::mq::sdk::cmd; namespace bpo = boost::program_options; const std::map expected = @@ -46,23 +44,6 @@ const std::map expected = { fair::mq::Transition::End, fair::mq::State::Exiting } }; -struct StateSubscription -{ - pmix::Commands& fCommands; - - explicit StateSubscription(pmix::Commands& commands) - : fCommands(commands) - { - fCommands.Send(Cmds(make(600000)).Serialize(Format::JSON)); - } - - ~StateSubscription() - { - fCommands.Send(Cmds(make()).Serialize(Format::JSON)); - this_thread::sleep_for(chrono::milliseconds(100)); // give PMIx a chance to complete request - } -}; - struct MiniTopo { explicit MiniTopo(unsigned int n) @@ -141,74 +122,6 @@ int main(int argc, char* argv[]) LOG(warn) << "pmix::fence() [pmix::init] OK"; MiniTopo topo(numDevices); - pmix::Commands commands(process); - - commands.Subscribe([&](const string& msg, const pmix::proc& sender) { - // LOG(info) << "Received '" << msg << "' from " << sender; - Cmds cmds; - cmds.Deserialize(msg, Format::JSON); - // cout << "Received " << cmds.Size() << " command(s) with total size of " << msg.length() << " bytes: " << endl; - for (const auto& cmd : cmds) { - // cout << " > " << cmd->GetType() << endl; - switch (cmd->GetType()) { - case Type::state_change: { - cout << "Received state_change from " << static_cast(*cmd).GetDeviceId() << ": " << static_cast(*cmd).GetLastState() << "->" << static_cast(*cmd).GetCurrentState() << endl; - topo.Update(sender.rank, static_cast(*cmd).GetCurrentState()); - if (static_cast(*cmd).GetCurrentState() == fair::mq::State::Exiting) { - commands.Send(Cmds(make()).Serialize(Format::JSON), {sender}); - } - } - break; - case Type::state_change_subscription: - if (static_cast(*cmd).GetResult() != Result::Ok) { - cout << "State change subscription failed for " << static_cast(*cmd).GetDeviceId() << endl; - } - break; - case Type::state_change_unsubscription: - if (static_cast(*cmd).GetResult() != Result::Ok) { - cout << "State change unsubscription failed for " << static_cast(*cmd).GetDeviceId() << endl; - } - break; - case Type::transition_status: { - if (static_cast(*cmd).GetResult() == Result::Ok) { - cout << "Device " << static_cast(*cmd).GetDeviceId() << " started to transition with " << static_cast(*cmd).GetTransition() << endl; - } else { - cout << "Device " << static_cast(*cmd).GetDeviceId() << " cannot transition with " << static_cast(*cmd).GetTransition() << endl; - } - } - break; - case Type::current_state: - cout << "Device " << static_cast(*cmd).GetDeviceId() << " is in " << static_cast(*cmd).GetCurrentState() << " state" << endl; - break; - case Type::config: - cout << "Received config for device " << static_cast(*cmd).GetDeviceId() << ":\n" << static_cast(*cmd).GetConfig() << endl; - break; - default: - cout << "Unexpected/unknown command received: " << cmd->GetType() << endl; - cout << "Origin: " << sender << endl; - break; - } - } - }); - - pmix::fence({all}); - LOG(warn) << "pmix::fence() [subscribed] OK"; - - StateSubscription stateSubscription(commands); - - for (auto transition : { fair::mq::Transition::InitDevice, - fair::mq::Transition::CompleteInit, - fair::mq::Transition::Bind, - fair::mq::Transition::Connect, - fair::mq::Transition::InitTask, - fair::mq::Transition::Run, - fair::mq::Transition::Stop, - fair::mq::Transition::ResetTask, - fair::mq::Transition::ResetDevice, - fair::mq::Transition::End }) { - commands.Send(Cmds(make(transition)).Serialize(Format::JSON)); - topo.WaitFor(expected.at(transition)); - } } catch (exception& e) { LOG(error) << "Error: " << e.what(); return EXIT_FAILURE; diff --git a/fairmq/sdk/AsioAsyncOp.h b/fairmq/sdk/AsioAsyncOp.h deleted file mode 100644 index a1a76a27..00000000 --- a/fairmq/sdk/AsioAsyncOp.h +++ /dev/null @@ -1,223 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_ASIOASYNCOP_H -#define FAIR_MQ_SDK_ASIOASYNCOP_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#ifndef FAIR_LOG -#define FAIR_LOG LOG -#endif /* ifndef FAIR_LOG */ - -namespace fair::mq::sdk -{ - -template -struct AsioAsyncOpImplBase -{ - virtual auto Complete(std::error_code, SignatureArgTypes...) -> void = 0; - virtual auto IsCompleted() const -> bool = 0; -}; - -/** - * @tparam Executor1 Associated I/O executor, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.associated_i_o_executor - * @tparam Allocator1 Default allocation strategy, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage - */ -template -struct AsioAsyncOpImpl : AsioAsyncOpImplBase -{ - /// See https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage - using Allocator2 = typename asio::associated_allocator::type; - - /// See https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.associated_completion_handler_executor - using Executor2 = typename asio::associated_executor::type; - - /// Ctor - AsioAsyncOpImpl(const Executor1& ex1, Allocator1 alloc1, Handler&& handler) - : fWork1(ex1) - , fWork2(asio::get_associated_executor(handler, ex1)) - , fHandler(std::move(handler)) - , fAlloc1(std::move(alloc1)) - {} - - auto GetAlloc2() const -> Allocator2 { return asio::get_associated_allocator(fHandler, fAlloc1); } - auto GetEx2() const -> Executor2 { return asio::get_associated_executor(fWork2); } - - auto Complete(std::error_code ec, SignatureArgTypes... args) -> void override - { - if (IsCompleted()) { - throw RuntimeError("Async operation already completed"); - } - - asio::dispatch(GetEx2(), - [=, handler = std::move(fHandler)]() mutable { - try { - handler(ec, args...); - } catch (const std::exception& e) { - FAIR_LOG(error) << "Uncaught exception in AsioAsyncOp completion handler: " << e.what(); - } catch (...) { - FAIR_LOG(error) << "Unknown uncaught exception in AsioAsyncOp completion handler."; - } - }); - - fWork1.reset(); - fWork2.reset(); - } - - auto IsCompleted() const -> bool override - { - return !fWork1.owns_work() && !fWork2.owns_work(); - } - - private: - /// See https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.outstanding_work - asio::executor_work_guard fWork1; - asio::executor_work_guard fWork2; - Handler fHandler; - Allocator1 fAlloc1; -}; - -/** - * @class AsioAsyncOp AsioAsyncOp.h - * @tparam Executor Associated I/O executor, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.associated_i_o_executor - * @tparam Allocator Default allocation strategy, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage - * @tparam CompletionSignature - * @brief Interface for Asio-compliant asynchronous operation, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html - * - * @par Thread Safety - * @e Distinct @e objects: Safe.@n - * @e Shared @e objects: Unsafe. - * - * primary template - */ -template -struct AsioAsyncOp -{ -}; - -/** - * @tparam Executor See primary template - * @tparam Allocator See primary template - * @tparam SignatureReturnType Return type of CompletionSignature, see primary template - * @tparam SignatureFirstArgType Type of first argument of CompletionSignature, see primary template - * @tparam SignatureArgTypes Types of the rest of arguments of CompletionSignature - * - * partial specialization to deconstruct CompletionSignature - */ -template -struct AsioAsyncOp -{ - static_assert(std::is_void::value, - "return value of CompletionSignature must be void"); - static_assert(std::is_same::value, - "first argument of CompletionSignature must be std::error_code"); - using Duration = std::chrono::milliseconds; - - private: - using Impl = AsioAsyncOpImplBase; - using ImplPtr = std::unique_ptr>; - ImplPtr fImpl; - - public: - /// Default Ctor - AsioAsyncOp() - : fImpl(nullptr) - {} - - /// Ctor with handler - template - AsioAsyncOp(Executor ex1, Allocator alloc1, Handler&& handler) - : AsioAsyncOp() - { - // Async operation type to be allocated and constructed - using Op = AsioAsyncOpImpl; - - // Create allocator for concrete op type - // Allocator2, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage - using OpAllocator = - typename std::allocator_traits::template rebind_alloc; - OpAllocator opAlloc; - - // Allocate memory - auto mem(std::allocator_traits::allocate(opAlloc, 1)); - - // Construct object - auto ptr(new (mem) Op(std::move(ex1), - std::move(alloc1), - std::forward(handler))); - - // Assign ownership to this object - fImpl = ImplPtr(ptr, [opAlloc](Impl* p) mutable { - std::allocator_traits::deallocate(opAlloc, static_cast(p), 1); - }); - } - - /// Ctor with handler #2 - template - AsioAsyncOp(Executor ex1, Handler&& handler) - : AsioAsyncOp(std::move(ex1), Allocator(), std::forward(handler)) - {} - - /// Ctor with handler #3 - template - explicit AsioAsyncOp(Handler&& handler) - : AsioAsyncOp(asio::system_executor(), std::forward(handler)) - {} - - auto IsCompleted() -> bool { return (fImpl == nullptr) || fImpl->IsCompleted(); } - - auto Complete(std::error_code ec, SignatureArgTypes... args) -> void - { - if(IsCompleted()) { - throw RuntimeError("Async operation already completed"); - } - - fImpl->Complete(ec, args...); - fImpl.reset(nullptr); - } - - auto Complete(SignatureArgTypes... args) -> void - { - Complete(std::error_code(), args...); - } - - auto Cancel(SignatureArgTypes... args) -> void - { - Complete(MakeErrorCode(ErrorCode::OperationCanceled), args...); - } - - auto Timeout(SignatureArgTypes... args) -> void - { - Complete(MakeErrorCode(ErrorCode::OperationTimeout), args...); - } -}; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_ASIOASYNCOP_H */ diff --git a/fairmq/sdk/AsioBase.h b/fairmq/sdk/AsioBase.h deleted file mode 100644 index 6170e263..00000000 --- a/fairmq/sdk/AsioBase.h +++ /dev/null @@ -1,73 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_ASIOBASE_H -#define FAIR_MQ_SDK_ASIOBASE_H - -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -using DefaultExecutor = asio::any_io_executor; -using DefaultAllocator = std::allocator; - -/** - * @class AsioBase AsioBase.h - * @tparam Executor Associated I/O executor - * @tparam Allocator Associated default allocator - * @brief Base for creating Asio-enabled I/O objects - * - * @par Thread Safety - * @e Distinct @e objects: Safe.@n - * @e Shared @e objects: Unsafe. - */ -template -class AsioBase -{ - public: - /// Member type of associated I/O executor - using ExecutorType = Executor; - /// Get associated I/O executor - auto GetExecutor() const noexcept -> ExecutorType { return fExecutor; } - - /// Member type of associated default allocator - using AllocatorType = Allocator; - /// Get associated default allocator - auto GetAllocator() const noexcept -> AllocatorType { return fAllocator; } - - /// NO default ctor - AsioBase() = delete; - - /// Construct with associated I/O executor - explicit AsioBase(Executor ex, Allocator alloc) - : fExecutor(std::move(ex)) - , fAllocator(std::move(alloc)) - {} - - /// NOT copyable - AsioBase(const AsioBase&) = delete; - AsioBase& operator=(const AsioBase&) = delete; - - /// movable - AsioBase(AsioBase&&) noexcept = default; - AsioBase& operator=(AsioBase&&) noexcept = default; - - ~AsioBase() = default; - - private: - ExecutorType fExecutor; - AllocatorType fAllocator; -}; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_ASIOBASE_H */ diff --git a/fairmq/sdk/CMakeLists.txt b/fairmq/sdk/CMakeLists.txt deleted file mode 100644 index 79b1c267..00000000 --- a/fairmq/sdk/CMakeLists.txt +++ /dev/null @@ -1,117 +0,0 @@ -################################################################################ -# Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # -# # -# This software is distributed under the terms of the # -# GNU Lesser General Public Licence (LGPL) version 3, # -# copied verbatim in the file "LICENSE" # -################################################################################ - -if(BUILD_TIDY_TOOL) - include(FairMQTidy) -endif() - -################# -# libFairMQ_SDK # -################# -configure_file(DDSInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/DDSInfo.h @ONLY) - -set(target SDK) - -set(SDK_PUBLIC_HEADER_FILES - ../SDK.h - AsioAsyncOp.h - AsioBase.h - DDSAgent.h - DDSCollection.h - DDSEnvironment.h - DDSSession.h - DDSTask.h - DDSTopology.h - Error.h - Topology.h - Traits.h -) - -set(SDK_PRIVATE_HEADER_FILES - ${CMAKE_CURRENT_BINARY_DIR}/DDSInfo.h -) - -set(SDK_SOURCE_FILES - DDSEnvironment.cxx - DDSSession.cxx - DDSTopology.cxx - Topology.cxx -) - -add_library(${target} - ${SDK_SOURCE_FILES} - ${SDK_PUBLIC_HEADER_FILES} # for IDE integration - ${SDK_PRIVATE_HEADER_FILES} # for IDE integration -) -target_compile_features(${target} PUBLIC cxx_std_17) -set_target_properties(${target} PROPERTIES LABELS coverage) -target_include_directories(${target} - PUBLIC - $ - $ - $ -) -target_link_libraries(${target} - PUBLIC - asio::asio - Boost::boost - Boost::filesystem - FairLogger::FairLogger - Threads::Threads - Tools - StateMachine - Commands - - PRIVATE - DDS::dds_intercom_lib - DDS::dds_tools_lib - DDS::dds_topology_lib -) -set_target_properties(${target} PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" - OUTPUT_NAME FairMQ_${target} -) -if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY) - fairmq_target_tidy(TARGET ${target}) -endif() - -############### -# executables # -############### -add_executable(fairmq-dds-command-ui ${CMAKE_CURRENT_SOURCE_DIR}/runDDSCommandUI.cxx) -target_link_libraries(fairmq-dds-command-ui - FairMQ - Commands - SDK - StateMachine -) - -install( - TARGETS - SDK - fairmq-dds-command-ui - - EXPORT ${PROJECT_EXPORT_SET} - RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR} - LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${PROJECT_INSTALL_LIBDIR} -) - -# preserve relative path and prepend fairmq -foreach(HEADER IN LISTS SDK_PUBLIC_HEADER_FILES) - get_filename_component(_path ${HEADER} DIRECTORY) - file(TO_CMAKE_PATH ${PROJECT_INSTALL_INCDIR}/sdk/${_path} _destination) - install(FILES ${HEADER} - DESTINATION ${_destination} - ) -endforeach() - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DDSInfo.h - DESTINATION ${PROJECT_INSTALL_INCDIR}/sdk -) diff --git a/fairmq/sdk/DDSAgent.h b/fairmq/sdk/DDSAgent.h deleted file mode 100644 index a3db515c..00000000 --- a/fairmq/sdk/DDSAgent.h +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_DDSSAGENT_H -#define FAIR_MQ_SDK_DDSSAGENT_H - -#include - -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -/** - * @class DDSAgent - * @brief Represents a DDS agent - */ -class DDSAgent -{ - public: - using Id = uint64_t; - using Pid = uint32_t; - - explicit DDSAgent(DDSSession session, - Id id, - Pid pid, - std::string path, - std::string host, - std::chrono::milliseconds startupTime, - std::string username) - : fSession(std::move(session)) - , fId(id) - , fPid(pid) - , fDDSPath(std::move(path)) - , fHost(std::move(host)) - , fStartupTime(startupTime) - , fUsername(std::move(username)) - {} - - DDSSession GetSession() const { return fSession; } - Id GetId() const { return fId; } - Pid GetPid() const { return fPid; } - std::string GetHost() const { return fHost; } - std::string GetDDSPath() const { return fDDSPath; } - std::chrono::milliseconds GetStartupTime() const { return fStartupTime; } - std::string GetUsername() const { return fUsername; } - - friend auto operator<<(std::ostream& os, const DDSAgent& agent) -> std::ostream& - { - return os << "DDSAgent id: " << agent.fId - << ", pid: " << agent.fPid - << ", path: " << agent.fDDSPath - << ", host: " << agent.fHost - << ", startupTime: " << agent.fStartupTime.count() - << ", username: " << agent.fUsername; - } - - private: - DDSSession fSession; - Id fId; - Pid fPid; - std::string fDDSPath; - std::string fHost; - std::chrono::milliseconds fStartupTime; - std::string fUsername; -}; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_DDSSAGENT_H */ diff --git a/fairmq/sdk/DDSCollection.h b/fairmq/sdk/DDSCollection.h deleted file mode 100644 index 2a56700c..00000000 --- a/fairmq/sdk/DDSCollection.h +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_DDSCOLLECTION_H -#define FAIR_MQ_SDK_DDSCOLLECTION_H - -// #include - -#include -#include - -namespace fair::mq::sdk -{ - -/** - * @class DDSCollection - * @brief Represents a DDS collection - */ -class DDSCollection -{ - public: - using Id = std::uint64_t; - - explicit DDSCollection(Id id) - : fId(id) - {} - - Id GetId() const { return fId; } - - friend auto operator<<(std::ostream& os, const DDSCollection& collection) -> std::ostream& - { - return os << "DDSCollection id: " << collection.fId; - } - - private: - Id fId; -}; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_DDSCOLLECTION_H */ diff --git a/fairmq/sdk/DDSEnvironment.cxx b/fairmq/sdk/DDSEnvironment.cxx deleted file mode 100644 index 939c3f52..00000000 --- a/fairmq/sdk/DDSEnvironment.cxx +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "DDSEnvironment.h" - -#include -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#undef BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -struct DDSEnvironment::Impl -{ - explicit Impl(Path configHome) - : fLocation(DDSInstallPrefix) - , fConfigHome(std::move(configHome)) - { - SetupPath(); - SetupConfigHome(); - } - - auto SetupConfigHome() -> void - { - if (fConfigHome.empty()) { - fConfigHome = GetEnv("HOME"); - } else { - setenv("HOME", fConfigHome.c_str(), 1); - } - } - - auto SetupPath() -> void - { - std::string path(GetEnv("PATH")); - Path ddsExecDir = (fLocation == DDSInstallPrefix) ? DDSExecutableDir : fLocation / Path("bin"); - path = ddsExecDir.string() + std::string(":") + path; - setenv("PATH", path.c_str(), 1); - } - - auto GetEnv(const std::string& key) const -> std::string - { - auto value = std::getenv(key.c_str()); - if (value) { - return {value}; - } - return {}; - } - - struct Tag {}; - friend auto operator<<(std::ostream& os, Tag) -> std::ostream& { return os << "DDSEnvironment"; } - tools::InstanceLimiter fCount; - - Path fLocation; - Path fConfigHome; -}; - -DDSEnvironment::DDSEnvironment() - : DDSEnvironment(Path()) -{} - -DDSEnvironment::DDSEnvironment(Path configHome) - : fImpl(std::make_shared(std::move(configHome))) -{} - -auto DDSEnvironment::GetLocation() const -> Path { return fImpl->fLocation; } - -auto DDSEnvironment::GetConfigHome() const -> Path { return fImpl->fConfigHome; } - -auto operator<<(std::ostream& os, DDSEnvironment env) -> std::ostream& -{ - return os << "$DDS_LOCATION: " << env.GetLocation() << ", " - << "$DDS_CONFIG_HOME: " << env.GetConfigHome() / DDSEnvironment::Path(".DDS"); -} - -} // namespace fair::mq::sdk diff --git a/fairmq/sdk/DDSEnvironment.h b/fairmq/sdk/DDSEnvironment.h deleted file mode 100644 index 529f1ba1..00000000 --- a/fairmq/sdk/DDSEnvironment.h +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_DDSENVIRONMENT_H -#define FAIR_MQ_SDK_DDSENVIRONMENT_H - -#include -#include -#include - -namespace fair::mq::sdk -{ - -/** - * @class DDSEnvironment DDSSession.h - * @brief Sets up the DDS environment (object helper) - */ -class DDSEnvironment -{ - public: - using Path = boost::filesystem::path; - - DDSEnvironment(); - explicit DDSEnvironment(Path); - - auto GetLocation() const -> Path; - auto GetConfigHome() const -> Path; - - friend auto operator<<(std::ostream& os, DDSEnvironment env) -> std::ostream&; - private: - struct Impl; - std::shared_ptr fImpl; -}; - -using DDSEnv = DDSEnvironment; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_DDSENVIRONMENT_H */ diff --git a/fairmq/sdk/DDSInfo.h.in b/fairmq/sdk/DDSInfo.h.in deleted file mode 100644 index e3c9d723..00000000 --- a/fairmq/sdk/DDSInfo.h.in +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_DDSINFO_H -#define FAIR_MQ_SDK_DDSINFO_H - -#include - -namespace dds::tools_api { class CSession; } -namespace dds::topology_api { class CTopology; } - -namespace fair::mq::sdk -{ - -const std::string DDSVersion("@DDS_VERSION@"); -const std::string DDSInstallPrefix("@DDS_INSTALL_PREFIX@"); -const std::string DDSExecutableDir("@DDS_BINDIR@"); -const std::string DDSIncludeDir("@DDS_INCDIR@"); -const std::string DDSLibraryDir("@DDS_LIBDIR@"); -const std::string DDSPluginDir("@DDS_PLUGINDIR@"); - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_DDSINFO_H */ diff --git a/fairmq/sdk/DDSSession.cxx b/fairmq/sdk/DDSSession.cxx deleted file mode 100644 index 02da02ed..00000000 --- a/fairmq/sdk/DDSSession.cxx +++ /dev/null @@ -1,389 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "DDSSession.h" - -#include -#include -#include -#include -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#undef BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -auto operator<<(std::ostream& os, DDSRMSPlugin plugin) -> std::ostream& -{ - switch (plugin) { - case DDSRMSPlugin::ssh: - return os << "ssh"; - case DDSRMSPlugin::localhost: - return os << "localhost"; - default: - __builtin_unreachable(); - } -} - -auto operator>>(std::istream& is, DDSRMSPlugin& plugin) -> std::istream& -{ - std::string value; - if (is >> value) { - if (value == "ssh") { - plugin = DDSRMSPlugin::ssh; - } else if (value == "localhost") { - plugin = DDSRMSPlugin::localhost; - } else { - throw std::runtime_error("Unknown or unsupported DDSRMSPlugin"); - } - } - return is; -} - -struct DDSSession::Impl -{ - explicit Impl(DDSEnvironment env) - : fEnv(std::move(env)) - , fRMSPlugin(DDSRMSPlugin::localhost) - , fSession(std::make_shared()) - , fDDSCustomCmd(fDDSService) - , fId(to_string(fSession->create())) - , fStopOnDestruction(false) - { - fDDSService.subscribeOnError([](const dds::intercom_api::EErrorCode errorCode, const std::string& msg) { - std::cerr << "DDS error, error code: " << errorCode << ", error message: " << msg << std::endl; - }); - } - - explicit Impl(Id existing, DDSEnvironment env) - : fEnv(std::move(env)) - , fRMSPlugin(DDSRMSPlugin::localhost) - , fSession(std::make_shared()) - , fDDSCustomCmd(fDDSService) - , fId(std::move(existing)) - , fStopOnDestruction(false) - { - fSession->attach(fId); - - fDDSService.subscribeOnError([](const dds::intercom_api::EErrorCode errorCode, const std::string& msg) { - std::cerr << "DDS error, error code: " << errorCode << ", error message: " << msg << std::endl; - }); - } - - explicit Impl(std::shared_ptr nativeSession, DDSEnv env) - : fEnv(std::move(env)) - , fRMSPlugin(DDSRMSPlugin::localhost) - , fSession(std::move(nativeSession)) - , fDDSCustomCmd(fDDSService) - , fId(to_string(fSession->getSessionID())) - , fStopOnDestruction(false) - { - // Sanity check - if (!fSession->IsRunning()) { - throw std::runtime_error("Given CSession must be running"); - } - } - - ~Impl() - { - if (fStopOnDestruction) { - fSession->shutdown(); - } - } - - Impl() = delete; - Impl(const Impl&) = delete; - Impl& operator=(const Impl&) = delete; - Impl(Impl&&) = delete; - Impl& operator=(Impl&&) = delete; - - DDSEnvironment fEnv; - DDSRMSPlugin fRMSPlugin; - Path fRMSConfig; - std::shared_ptr fSession; - dds::intercom_api::CIntercomService fDDSService; - dds::intercom_api::CCustomCmd fDDSCustomCmd; - Id fId; - bool fStopOnDestruction; -}; - -DDSSession::DDSSession(DDSEnvironment env) - : fImpl(std::make_shared(std::move(env))) -{} - -DDSSession::DDSSession(Id existing, DDSEnvironment env) - : fImpl(std::make_shared(std::move(existing), std::move(env))) -{} - -DDSSession::DDSSession(std::shared_ptr nativeSession, DDSEnv env) - : fImpl(std::make_shared(std::move(nativeSession), std::move(env))) -{} - -auto DDSSession::GetEnv() const -> DDSEnvironment { return fImpl->fEnv; } - -auto DDSSession::IsRunning() const -> bool { return fImpl->fSession->IsRunning(); } - -auto DDSSession::GetId() const -> Id { return fImpl->fId; } - -auto DDSSession::Stop() -> void { return fImpl->fSession->shutdown(); } - -auto DDSSession::GetRMSPlugin() const -> DDSRMSPlugin { return fImpl->fRMSPlugin; } - -auto DDSSession::SetRMSPlugin(DDSRMSPlugin plugin) -> void { fImpl->fRMSPlugin = plugin; } - -auto DDSSession::GetRMSConfig() const -> Path { return fImpl->fRMSConfig; } - -auto DDSSession::SetRMSConfig(Path configFile) const -> void -{ - fImpl->fRMSConfig = std::move(configFile); -} - -auto DDSSession::IsStoppedOnDestruction() const -> bool { return fImpl->fStopOnDestruction; } - -auto DDSSession::StopOnDestruction(bool stop) -> void { fImpl->fStopOnDestruction = stop; } - -auto DDSSession::SubmitAgents(Quantity agents) -> void -{ - // Requesting to submit 0 agents is not meaningful - assert(agents > 0); - - using namespace dds::tools_api; - - SSubmitRequestData submitInfo; - submitInfo.m_rms = tools::ToString(GetRMSPlugin()); - submitInfo.m_instances = 1; - submitInfo.m_slots = agents; // TODO new api: get slots from agents - submitInfo.m_config = GetRMSConfig().string(); - - tools::SharedSemaphore blocker; - auto submitRequest = SSubmitRequest::makeRequest(submitInfo); - submitRequest->setMessageCallback([](const SMessageResponseData& message){ - LOG(debug) << message.m_msg; - }); - submitRequest->setDoneCallback([agents, blocker]() mutable { - LOG(debug) << agents << " Agents submitted"; - blocker.Signal(); - }); - - fImpl->fSession->sendRequest(submitRequest); - blocker.Wait(); - - WaitForIdleAgents(agents); -} - -auto DDSSession::RequestAgentCount() -> AgentCount -{ - using namespace dds::tools_api; - - SAgentCountRequest::response_t res; - fImpl->fSession->syncSendRequest(SAgentCountRequest::request_t(), res); - - AgentCount count; - count.active = res.m_activeSlotsCount; - count.idle = res.m_idleSlotsCount; - count.executing = res.m_executingSlotsCount; - - return count; -} - -auto DDSSession::RequestAgentInfo() -> std::vector -{ - using namespace dds::tools_api; - - SAgentInfoRequest::responseVector_t res; - fImpl->fSession->syncSendRequest(SAgentInfoRequest::request_t(), res); - - std::vector agentInfo; - agentInfo.reserve(res.size()); - for (const auto& a : res) { - agentInfo.emplace_back( - *this, - a.m_agentID, - a.m_agentPid, - a.m_DDSPath, - a.m_host, - a.m_startUpTime, - a.m_username - // a.m_nSlots - ); - } - - return agentInfo; -} - -auto DDSSession::RequestTaskInfo() -> std::vector -{ - using namespace dds::tools_api; - - SAgentInfoRequest::responseVector_t res; - fImpl->fSession->syncSendRequest(SAgentInfoRequest::request_t(), res); - - std::vector taskInfo; - taskInfo.reserve(res.size()); - for (auto& a : res) { - //taskInfo.emplace_back(a.m_taskID, 0); - (void)a; - taskInfo.emplace_back(0, 0); - } - - return taskInfo; -} - -auto DDSSession::RequestCommanderInfo() -> CommanderInfo -{ - using namespace dds::tools_api; - - SCommanderInfoRequestData commanderInfo; - tools::SharedSemaphore blocker; - std::string error; - auto commanderInfoRequest = SCommanderInfoRequest::makeRequest(commanderInfo); - CommanderInfo info; - commanderInfoRequest->setResponseCallback([&info](const SCommanderInfoResponseData& _response) { - info.pid = _response.m_pid; - info.activeTopologyName = _response.m_activeTopologyName; - }); - commanderInfoRequest->setMessageCallback([&](const SMessageResponseData& _message) { - if (_message.m_severity == dds::intercom_api::EMsgSeverity::error) { - error = _message.m_msg; - blocker.Signal(); - } else { - LOG(debug) << _message.m_msg; - } - }); - commanderInfoRequest->setDoneCallback([blocker]() mutable { blocker.Signal(); }); - fImpl->fSession->sendRequest(commanderInfoRequest); - blocker.Wait(); - - if (!error.empty()) { - throw std::runtime_error(error); - } - - return info; -} - -auto DDSSession::WaitForExecutingAgents(Quantity minCount) -> void -{ - auto count(RequestAgentCount()); - int interval(8); - while (count.executing < minCount) { - std::this_thread::sleep_for(std::chrono::milliseconds(interval)); - interval = std::min(256, interval * 2); - count = RequestAgentCount(); - } -} - -auto DDSSession::WaitForIdleAgents(Quantity minCount) -> void -{ - auto count(RequestAgentCount()); - int interval(8); - while (count.idle < minCount) { - std::this_thread::sleep_for(std::chrono::milliseconds(interval)); - interval = std::min(256, interval * 2); - count = RequestAgentCount(); - } -} - -auto DDSSession::ActivateTopology(DDSTopology topo) -> void -{ - using namespace dds::tools_api; - - STopologyRequestData topologyInfo; - topologyInfo.m_updateType = STopologyRequestData::EUpdateType::ACTIVATE; - topologyInfo.m_topologyFile = topo.GetTopoFile().string(); - - tools::SharedSemaphore blocker; - auto topologyRequest = STopologyRequest::makeRequest(topologyInfo); - topologyRequest->setMessageCallback([](const SMessageResponseData& _message) { - LOG(debug) << _message.m_msg; - }); - topologyRequest->setDoneCallback([blocker]() mutable { blocker.Signal(); }); - fImpl->fSession->sendRequest(topologyRequest); - blocker.Wait(); - - WaitForExecutingAgents(topo.GetNumRequiredAgents()); -} - -auto DDSSession::ActivateTopology(const Path& topoFile) -> void -{ - ActivateTopology(DDSTopology(topoFile, GetEnv())); -} - -void DDSSession::StartDDSService() { fImpl->fDDSService.start(fImpl->fId); } - -void DDSSession::SubscribeToCommands(std::function cb) -{ - fImpl->fDDSCustomCmd.subscribe(cb); - // fImpl->fDDSCustomCmd.subscribeOnReply([](const std::string& reply) { - // LOG(debug) << reply; - // }); -} - -void DDSSession::UnsubscribeFromCommands() -{ - fImpl->fDDSCustomCmd.unsubscribe(); -} - -void DDSSession::SendCommand(const std::string& cmd, const std::string& path /* = "" */) { fImpl->fDDSCustomCmd.send(cmd, path); } - -void DDSSession::SendCommand(const std::string& cmd, DDSChannel::Id recipient) -{ - fImpl->fDDSCustomCmd.send(cmd, std::to_string(recipient)); -} - -auto operator<<(std::ostream& os, const DDSSession& session) -> std::ostream& -{ - return os << "$DDS_SESSION_ID: " << session.GetId(); -} - -auto getMostRecentRunningDDSSession(DDSEnv env) -> DDSSession -{ - boost::process::ipstream pipeStream; - boost::process::child c("dds-session list all", boost::process::std_out > pipeStream); - std::string lastLine; - std::string currentLine; - - while (pipeStream && std::getline(pipeStream, currentLine) && !currentLine.empty()) { - lastLine = currentLine; - } - c.wait(); - std::string sessionId; - - if (!lastLine.empty()) { - std::vector words; - std::istringstream iss(lastLine); - for (std::string s; iss >> s;) { - if (s != "*") { - words.push_back(s); - } - } - if (words.back() == "RUNNING") { - sessionId = words.front(); - } - } - - if (sessionId.empty()) { - throw std::runtime_error("could not find most recent DDS session"); - } - - return DDSSession(DDSSession::Id(sessionId), std::move(env)); -} - -} // namespace fair::mq::sdk diff --git a/fairmq/sdk/DDSSession.h b/fairmq/sdk/DDSSession.h deleted file mode 100644 index 93034b9e..00000000 --- a/fairmq/sdk/DDSSession.h +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_DDSSESSION_H -#define FAIR_MQ_SDK_DDSSESSION_H - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -/** - * @enum DDSRMSPlugin DDSSession.h - * @brief Supported DDS resource management system plugins - */ -enum class DDSRMSPlugin -{ - localhost, - ssh -}; -auto operator<<(std::ostream& os, DDSRMSPlugin plugin) -> std::ostream&; -auto operator>>(std::istream& is, DDSRMSPlugin& plugin) -> std::istream&; - -class DDSTopology; -class DDSAgent; - -class DDSChannel -{ - public: - using Id = std::uint64_t; -}; - -/** - * @class DDSSession DDSSession.h - * @brief Represents a DDS session - */ -class DDSSession -{ - public: - using Id = std::string; - using Quantity = std::uint32_t; - using Path = boost::filesystem::path; - - explicit DDSSession(DDSEnvironment env = DDSEnvironment()); - explicit DDSSession(Id existing, DDSEnvironment env = DDSEnvironment()); - - /// @brief Construct with already existing native DDS API objects - /// @param nativeSession Existing and initialized CSession (either via create() or attach()) - /// @param env Optional DDSEnv - explicit DDSSession(std::shared_ptr nativeSession, DDSEnv env = {}); - - auto GetEnv() const -> DDSEnvironment; - auto GetId() const -> Id; - auto GetRMSPlugin() const -> DDSRMSPlugin; - auto SetRMSPlugin(DDSRMSPlugin) -> void; - auto GetRMSConfig() const -> Path; - auto SetRMSConfig(Path) const -> void; - auto IsStoppedOnDestruction() const -> bool; - auto StopOnDestruction(bool stop = true) -> void; - auto IsRunning() const -> bool; - auto SubmitAgents(Quantity agents) -> void; - struct AgentCount { - Quantity idle = 0; - Quantity active = 0; - Quantity executing = 0; - }; - auto RequestAgentCount() -> AgentCount; - auto RequestAgentInfo() -> std::vector; - auto RequestTaskInfo() -> std::vector; - struct CommanderInfo { - int pid = -1; - std::string activeTopologyName; - }; - auto RequestCommanderInfo() -> CommanderInfo; - auto WaitForIdleAgents(Quantity) -> void; - auto WaitForOnlyIdleAgents() -> void; - auto WaitForExecutingAgents(Quantity) -> void; - auto ActivateTopology(const Path& topoFile) -> void; - auto ActivateTopology(DDSTopology) -> void; - auto Stop() -> void; - - void StartDDSService(); - void SubscribeToCommands(std::function); - void UnsubscribeFromCommands(); - void SendCommand(const std::string&, const std::string& = ""); - void SendCommand(const std::string&, DDSChannel::Id); - auto GetTaskId(DDSChannel::Id) const -> DDSTask::Id; - - friend auto operator<<(std::ostream& os, const DDSSession& session) -> std::ostream&; - - private: - struct Impl; - std::shared_ptr fImpl; -}; - -auto getMostRecentRunningDDSSession(DDSEnv env = {}) -> DDSSession; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_DDSSESSION_H */ diff --git a/fairmq/sdk/DDSTask.h b/fairmq/sdk/DDSTask.h deleted file mode 100644 index 5be55853..00000000 --- a/fairmq/sdk/DDSTask.h +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_DDSTASK_H -#define FAIR_MQ_SDK_DDSTASK_H - -#include - -#include -#include - -namespace fair::mq::sdk -{ - -/** - * @class DDSTask - * @brief Represents a DDS task - */ -class DDSTask -{ - public: - using Id = std::uint64_t; - - explicit DDSTask(Id id, Id collectionId) - : fId(id) - , fCollectionId(collectionId) - {} - - Id GetId() const { return fId; } - DDSCollection::Id GetCollectionId() const { return fCollectionId; } - - friend auto operator<<(std::ostream& os, const DDSTask& task) -> std::ostream& - { - return os << "DDSTask id: " << task.fId << ", collection id: " << task.fCollectionId; - } - - private: - Id fId; - DDSCollection::Id fCollectionId; -}; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_DDSTASK_H */ diff --git a/fairmq/sdk/DDSTopology.cxx b/fairmq/sdk/DDSTopology.cxx deleted file mode 100644 index d4487174..00000000 --- a/fairmq/sdk/DDSTopology.cxx +++ /dev/null @@ -1,116 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "DDSTopology.h" - -#include -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#undef BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#include -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -struct DDSTopology::Impl -{ - explicit Impl(Path topoFile, DDSEnvironment env) - : fEnv(std::move(env)) - , fTopoFile(std::move(topoFile)) - , fTopo(fTopoFile.string()) - {} - - explicit Impl(dds::topology_api::CTopology nativeTopology, DDSEnvironment env) - : fEnv(std::move(env)) - , fTopo(std::move(nativeTopology)) - {} - - DDSEnvironment fEnv; - Path fTopoFile; - dds::topology_api::CTopology fTopo; -}; - -DDSTopology::DDSTopology(Path topoFile, DDSEnvironment env) - : fImpl(std::make_shared(std::move(topoFile), std::move(env))) -{} - -DDSTopology::DDSTopology(dds::topology_api::CTopology nativeTopology, DDSEnv env) - : fImpl(std::make_shared(std::move(nativeTopology), std::move(env))) -{} - -auto DDSTopology::GetEnv() const -> DDSEnvironment { return fImpl->fEnv; } - -auto DDSTopology::GetTopoFile() const -> Path -{ - auto file(fImpl->fTopoFile); - if (file.string().empty()) { - throw std::runtime_error("DDS topology xml spec file unknown"); - } - return file; -} - -auto DDSTopology::GetNumRequiredAgents() const -> std::size_t -{ - return fImpl->fTopo.getRequiredNofAgents(); -} - -auto DDSTopology::GetTasks(const std::string& path /* = "" */) const -> std::vector -{ - std::vector list; - - dds::topology_api::STopoRuntimeTask::FilterIteratorPair_t itPair; - if (path.empty()) { - itPair = fImpl->fTopo.getRuntimeTaskIterator(nullptr); // passing nullptr will get all tasks - } else { - itPair = fImpl->fTopo.getRuntimeTaskIteratorMatchingPath(path); - } - auto tasks = boost::make_iterator_range(itPair.first, itPair.second); - - for (const auto& task : tasks) { - // LOG(debug) << "Found task with id: " << task.first << ", " - // << "Path: " << task.second.m_taskPath << ", " - // << "Collection id: " << task.second.m_taskCollectionId << ", " - // << "Name: " << task.second.m_task->getName() << "_" << task.second.m_taskIndex; - list.emplace_back(task.first, task.second.m_taskCollectionId); - } - - return list; -} - -auto DDSTopology::GetCollections() const -> std::vector -{ - std::vector list; - - auto itPair = fImpl->fTopo.getRuntimeCollectionIterator(nullptr); // passing nullptr will get all collections - auto collections = boost::make_iterator_range(itPair.first, itPair.second); - - for (const auto& c : collections) { - LOG(debug) << "Found collection with id: " << c.first << ", " - << "Index: " << c.second.m_collectionIndex << ", " - << "Path: " << c.second.m_collectionPath; - list.emplace_back(c.first); - } - - return list; -} - -auto DDSTopology::GetName() const -> std::string { return fImpl->fTopo.getName(); } - -auto operator<<(std::ostream& os, const DDSTopology& t) -> std::ostream& -try { - return os << "DDS topology: " << t.GetName() << " (loaded from " << t.GetTopoFile() << ")"; -} catch (std::runtime_error&) { - return os << "DDS topology: " << t.GetName(); -} - -} // namespace fair::mq::sdk diff --git a/fairmq/sdk/DDSTopology.h b/fairmq/sdk/DDSTopology.h deleted file mode 100644 index 9a6a0597..00000000 --- a/fairmq/sdk/DDSTopology.h +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_DDSTOPOLOGY_H -#define FAIR_MQ_SDK_DDSTOPOLOGY_H - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -/** - * @class DDSTopology DDSTopology.h - * @brief Represents a DDS topology - */ -class DDSTopology -{ - public: - using Path = boost::filesystem::path; - - DDSTopology() = delete; - - /// @brief Construct from file - /// @param topoFile DDS topology xml file - /// @param env DDS environment - explicit DDSTopology(Path topoFile, DDSEnvironment env = DDSEnvironment()); - - /// @brief Construct with already existing native DDS API objects - /// @param nativeTopology Existing and initialized CTopology - /// @param env Optional DDSEnv - explicit DDSTopology(dds::topology_api::CTopology nativeTopology, DDSEnv env = {}); - - /// @brief Get associated DDS environment - auto GetEnv() const -> DDSEnvironment; - - /// @brief Get path to DDS topology xml, if it is known - /// @throw std::runtime_error - auto GetTopoFile() const -> Path; - - /// @brief Get number of required agents for this topology - auto GetNumRequiredAgents() const -> std::size_t; - - /// @brief Get list of tasks in this topology, optionally matching provided path - auto GetTasks(const std::string& = "") const -> std::vector; - - /// @brief Get list of tasks in this topology - auto GetCollections() const -> std::vector; - - /// @brief Get the name of the topology - auto GetName() const -> std::string; - - friend auto operator<<(std::ostream&, const DDSTopology&) -> std::ostream&; - - private: - struct Impl; - std::shared_ptr fImpl; -}; - -using DDSTopo = DDSTopology; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_DDSTOPOLOGY_H */ diff --git a/fairmq/sdk/Error.h b/fairmq/sdk/Error.h deleted file mode 100644 index 10a346bd..00000000 --- a/fairmq/sdk/Error.h +++ /dev/null @@ -1,20 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_ERROR_H -#define FAIR_MQ_SDK_ERROR_H - -#include - -namespace fair::mq::sdk { - -using RuntimeError = fair::mq::RuntimeError; - -} /* namespace fair::mq::sdk */ - -#endif /* FAIR_MQ_SDK_ERROR_H */ diff --git a/fairmq/sdk/Topology.cxx b/fairmq/sdk/Topology.cxx deleted file mode 100644 index c911e04e..00000000 --- a/fairmq/sdk/Topology.cxx +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "Topology.h" - -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#undef BOOST_BIND_GLOBAL_PLACEHOLDERS - -namespace fair::mq::sdk -{ - -/// @brief Helper to (Re)Construct a FairMQ topology based on already existing native DDS API objects -/// @param nativeSession Existing and initialized CSession (either via create() or attach()) -/// @param nativeTopo Existing CTopology that is activated on the given nativeSession -/// @param env Optional DDSEnv (needed primarily for unit testing) -/// @param blockUntilConnected if true, ctor will wait for all tasks to confirm subscriptions -auto MakeTopology(dds::topology_api::CTopology nativeTopo, - std::shared_ptr nativeSession, - DDSEnv env, - bool blockUntilConnected) -> Topology -{ - return {DDSTopo(std::move(nativeTopo), env), DDSSession(std::move(nativeSession), env), blockUntilConnected}; -} - -} // namespace fair::mq::sdk diff --git a/fairmq/sdk/Topology.h b/fairmq/sdk/Topology.h deleted file mode 100644 index f55ff21a..00000000 --- a/fairmq/sdk/Topology.h +++ /dev/null @@ -1,1367 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_TOPOLOGY_H -#define FAIR_MQ_SDK_TOPOLOGY_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#ifndef FAIR_LOG -#define FAIR_LOG LOG -#endif /* ifndef FAIR_LOG */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fair::mq::sdk -{ - -using DeviceId = std::string; -using DeviceState = fair::mq::State; -using DeviceTransition = fair::mq::Transition; - -const std::map expectedState = -{ - { DeviceTransition::InitDevice, DeviceState::InitializingDevice }, - { DeviceTransition::CompleteInit, DeviceState::Initialized }, - { DeviceTransition::Bind, DeviceState::Bound }, - { DeviceTransition::Connect, DeviceState::DeviceReady }, - { DeviceTransition::InitTask, DeviceState::Ready }, - { DeviceTransition::Run, DeviceState::Running }, - { DeviceTransition::Stop, DeviceState::Ready }, - { DeviceTransition::ResetTask, DeviceState::DeviceReady }, - { DeviceTransition::ResetDevice, DeviceState::Idle }, - { DeviceTransition::End, DeviceState::Exiting } -}; - -// mirrors DeviceState, but adds a "Mixed" state that represents a topology where devices are currently not in the same state. -enum class AggregatedTopologyState : int -{ - Undefined = static_cast(fair::mq::State::Undefined), - Ok = static_cast(fair::mq::State::Ok), - Error = static_cast(fair::mq::State::Error), - Idle = static_cast(fair::mq::State::Idle), - InitializingDevice = static_cast(fair::mq::State::InitializingDevice), - Initialized = static_cast(fair::mq::State::Initialized), - Binding = static_cast(fair::mq::State::Binding), - Bound = static_cast(fair::mq::State::Bound), - Connecting = static_cast(fair::mq::State::Connecting), - DeviceReady = static_cast(fair::mq::State::DeviceReady), - InitializingTask = static_cast(fair::mq::State::InitializingTask), - Ready = static_cast(fair::mq::State::Ready), - Running = static_cast(fair::mq::State::Running), - ResettingTask = static_cast(fair::mq::State::ResettingTask), - ResettingDevice = static_cast(fair::mq::State::ResettingDevice), - Exiting = static_cast(fair::mq::State::Exiting), - Mixed -}; - -inline auto operator==(DeviceState lhs, AggregatedTopologyState rhs) -> bool -{ - return static_cast(lhs) == static_cast(rhs); -} - -inline auto operator==(AggregatedTopologyState lhs, DeviceState rhs) -> bool -{ - return static_cast(lhs) == static_cast(rhs); -} - -inline std::ostream& operator<<(std::ostream& os, const AggregatedTopologyState& state) -{ - if (state == AggregatedTopologyState::Mixed) { - return os << "MIXED"; - } else { - return os << static_cast(state); - } -} - -inline std::string GetAggregatedTopologyStateName(AggregatedTopologyState s) -{ - if (s == AggregatedTopologyState::Mixed) { - return "MIXED"; - } else { - return GetStateName(static_cast(s)); - } -} - -inline AggregatedTopologyState GetAggregatedTopologyState(const std::string& state) -{ - if (state == "MIXED") { - return AggregatedTopologyState::Mixed; - } else { - return static_cast(GetState(state)); - } -} - -struct DeviceStatus -{ - bool subscribed_to_state_changes; - DeviceState lastState; - DeviceState state; - DDSTask::Id taskId; - DDSCollection::Id collectionId; -}; - -using DeviceProperty = std::pair; /// pair := (key, value) -using DeviceProperties = std::vector; -using DevicePropertyQuery = std::string; /// Boost regex supported -using FailedDevices = std::set; - -struct GetPropertiesResult -{ - struct Device - { - DeviceProperties props; - }; - std::unordered_map devices; - FailedDevices failed; -}; - -using TopologyState = std::vector; -using TopologyStateIndex = std::unordered_map; // task id -> index in the data vector -using TopologyStateByTask = std::unordered_map; -using TopologyStateByCollection = std::unordered_map>; -using TopologyTransition = fair::mq::Transition; - -inline AggregatedTopologyState AggregateState(const TopologyState& topologyState) -{ - DeviceState first = topologyState.begin()->state; - - if (std::all_of(topologyState.cbegin(), topologyState.cend(), [&](TopologyState::value_type i) { - return i.state == first; - })) { - return static_cast(first); - } - - return AggregatedTopologyState::Mixed; -} - -inline bool StateEqualsTo(const TopologyState& topologyState, DeviceState state) -{ - return AggregateState(topologyState) == static_cast(state); -} - -inline TopologyStateByCollection GroupByCollectionId(const TopologyState& topologyState) -{ - TopologyStateByCollection state; - for (const auto& ds : topologyState) { - if (ds.collectionId != 0) { - state[ds.collectionId].push_back(ds); - } - } - - return state; -} - -inline TopologyStateByTask GroupByTaskId(const TopologyState& topologyState) -{ - TopologyStateByTask state; - for (const auto& ds : topologyState) { - state[ds.taskId] = ds; - } - - return state; -} - -/** - * @class BasicTopology Topology.h - * @tparam Executor Associated I/O executor - * @tparam Allocator Associated default allocator - * @brief Represents a FairMQ topology - * - * @par Thread Safety - * @e Distinct @e objects: Safe.@n - * @e Shared @e objects: Safe. - */ -template -class BasicTopology : public AsioBase -{ - public: - /// @brief (Re)Construct a FairMQ topology from an existing DDS topology - /// @param topo DDSTopology - /// @param session DDSSession - /// @param blockUntilConnected if true, ctor will wait for all tasks to confirm subscriptions - BasicTopology(DDSTopology topo, DDSSession session, bool blockUntilConnected = false) - : BasicTopology(asio::system_executor(), std::move(topo), std::move(session), blockUntilConnected) - {} - - /// @brief (Re)Construct a FairMQ topology from an existing DDS topology - /// @param ex I/O executor to be associated - /// @param topo DDSTopology - /// @param session DDSSession - /// @param blockUntilConnected if true, ctor will wait for all tasks to confirm subscriptions - /// @throws RuntimeError - BasicTopology(const Executor& ex, - DDSTopology topo, - DDSSession session, - bool blockUntilConnected = false, - Allocator alloc = DefaultAllocator()) - : AsioBase(ex, std::move(alloc)) - , fDDSSession(std::move(session)) - , fDDSTopo(std::move(topo)) - , fMtx(std::make_unique()) - , fStateChangeSubscriptionsCV(std::make_unique()) - , fNumStateChangePublishers(0) - , fHeartbeatsTimer(asio::system_executor()) - , fHeartbeatInterval(600000) - { - makeTopologyState(); - - std::string activeTopo(fDDSSession.RequestCommanderInfo().activeTopologyName); - std::string givenTopo(fDDSTopo.GetName()); - if (activeTopo != givenTopo) { - throw RuntimeError("Given topology ", givenTopo, " is not activated (active: ", activeTopo, ")"); - } - - SubscribeToCommands(); - - fDDSSession.StartDDSService(); - SubscribeToStateChanges(); - if (blockUntilConnected) { - WaitForPublisherCount(fStateIndex.size()); - } - } - - /// not copyable - BasicTopology(const BasicTopology&) = delete; - BasicTopology& operator=(const BasicTopology&) = delete; - - /// movable - BasicTopology(BasicTopology&&) = default; - BasicTopology& operator=(BasicTopology&&) = default; - - ~BasicTopology() - { - UnsubscribeFromStateChanges(); - - std::lock_guard lk(*fMtx); - fDDSSession.UnsubscribeFromCommands(); - try { - for (auto& op : fChangeStateOps) { - op.second.Complete(MakeErrorCode(ErrorCode::OperationCanceled)); - } - } catch (...) {} - } - - void SubscribeToStateChanges() - { - // FAIR_LOG(debug) << "Subscribing to state change"; - cmd::Cmds cmds(cmd::make(fHeartbeatInterval.count())); - fDDSSession.SendCommand(cmds.Serialize()); - - fHeartbeatsTimer.expires_after(fHeartbeatInterval); - fHeartbeatsTimer.async_wait(std::bind(&BasicTopology::SendSubscriptionHeartbeats, this, std::placeholders::_1)); - } - - void WaitForPublisherCount(unsigned int number) - { - std::unique_lock lk(*fMtx); - fStateChangeSubscriptionsCV->wait(lk, [&](){ - return fNumStateChangePublishers == number; - }); - } - - void SendSubscriptionHeartbeats(const std::error_code& ec) - { - if (!ec) { - // Timer expired. - fDDSSession.SendCommand(cmd::Cmds(cmd::make(fHeartbeatInterval.count())).Serialize()); - // schedule again - fHeartbeatsTimer.expires_after(fHeartbeatInterval); - fHeartbeatsTimer.async_wait(std::bind(&BasicTopology::SendSubscriptionHeartbeats, this, std::placeholders::_1)); - } else if (ec == asio::error::operation_aborted) { - // FAIR_LOG(debug) << "Heartbeats timer canceled"; - } else { - FAIR_LOG(error) << "Timer error: " << ec; - } - } - - void UnsubscribeFromStateChanges() - { - // stop sending heartbeats - fHeartbeatsTimer.cancel(); - - // unsubscribe from state changes - fDDSSession.SendCommand(cmd::Cmds(cmd::make()).Serialize()); - - // wait for all tasks to confirm unsubscription - WaitForPublisherCount(0); - } - - void SubscribeToCommands() - { - fDDSSession.SubscribeToCommands([&](const std::string& msg, const std::string& /* condition */, DDSChannel::Id senderId) { - cmd::Cmds inCmds; - inCmds.Deserialize(msg); - // FAIR_LOG(debug) << "Received " << inCmds.Size() << " command(s) with total size of " << msg.length() << " bytes: "; - - for (const auto& cmd : inCmds) { - // FAIR_LOG(debug) << " > " << cmd->GetType(); - switch (cmd->GetType()) { - case cmd::Type::state_change_subscription: - HandleCmd(static_cast(*cmd)); - break; - case cmd::Type::state_change_unsubscription: - HandleCmd(static_cast(*cmd)); - break; - case cmd::Type::state_change: - HandleCmd(static_cast(*cmd), senderId); - break; - case cmd::Type::transition_status: - HandleCmd(static_cast(*cmd)); - break; - case cmd::Type::properties: - HandleCmd(static_cast(*cmd)); - break; - case cmd::Type::properties_set: - HandleCmd(static_cast(*cmd)); - break; - default: - FAIR_LOG(warn) << "Unexpected/unknown command received: " << cmd->GetType(); - FAIR_LOG(warn) << "Origin: " << senderId; - break; - } - } - }); - } - - auto HandleCmd(cmd::StateChangeSubscription const& cmd) -> void - { - if (cmd.GetResult() == cmd::Result::Ok) { - DDSTask::Id taskId(cmd.GetTaskId()); - - try { - std::unique_lock lk(*fMtx); - DeviceStatus& task = fStateData.at(fStateIndex.at(taskId)); - if (!task.subscribed_to_state_changes) { - task.subscribed_to_state_changes = true; - ++fNumStateChangePublishers; - } else { - FAIR_LOG(warn) << "Task '" << task.taskId << "' sent subscription confirmation more than once"; - } - lk.unlock(); - fStateChangeSubscriptionsCV->notify_one(); - } catch (const std::exception& e) { - FAIR_LOG(error) << "Exception in HandleCmd(cmd::StateChangeSubscription const&): " << e.what(); - FAIR_LOG(error) << "Possibly no task with id '" << taskId << "'?"; - } - } else { - FAIR_LOG(error) << "State change subscription failed for device: " << cmd.GetDeviceId() << ", task id: " << cmd.GetTaskId(); - } - } - - auto HandleCmd(cmd::StateChangeUnsubscription const& cmd) -> void - { - if (cmd.GetResult() == cmd::Result::Ok) { - DDSTask::Id taskId(cmd.GetTaskId()); - - try { - std::unique_lock lk(*fMtx); - DeviceStatus& task = fStateData.at(fStateIndex.at(taskId)); - if (task.subscribed_to_state_changes) { - task.subscribed_to_state_changes = false; - --fNumStateChangePublishers; - } else { - FAIR_LOG(warn) << "Task '" << task.taskId << "' sent unsubscription confirmation more than once"; - } - lk.unlock(); - fStateChangeSubscriptionsCV->notify_one(); - } catch (const std::exception& e) { - FAIR_LOG(error) << "Exception in HandleCmd(cmd::StateChangeUnsubscription const&): " << e.what(); - } - } else { - FAIR_LOG(error) << "State change unsubscription failed for device: " << cmd.GetDeviceId() << ", task id: " << cmd.GetTaskId(); - } - } - - auto HandleCmd(cmd::StateChange const& cmd, DDSChannel::Id const& senderId) -> void - { - if (cmd.GetCurrentState() == DeviceState::Exiting) { - fDDSSession.SendCommand(cmd::Cmds(cmd::make()).Serialize(), senderId); - } - - DDSTask::Id taskId(cmd.GetTaskId()); - - try { - std::lock_guard lk(*fMtx); - DeviceStatus& task = fStateData.at(fStateIndex.at(taskId)); - task.lastState = cmd.GetLastState(); - task.state = cmd.GetCurrentState(); - // if the task is exiting, it will not respond to unsubscription request anymore, set it to false now. - if (task.state == DeviceState::Exiting) { - task.subscribed_to_state_changes = false; - --fNumStateChangePublishers; - } - // FAIR_LOG(debug) << "Updated state entry: taskId=" << taskId << ", state=" << state; - - for (auto& op : fChangeStateOps) { - op.second.Update(taskId, cmd.GetCurrentState()); - } - for (auto& op : fWaitForStateOps) { - op.second.Update(taskId, cmd.GetLastState(), cmd.GetCurrentState()); - } - } catch (const std::exception& e) { - FAIR_LOG(error) << "Exception in HandleCmd(cmd::StateChange const&): " << e.what(); - } - } - - auto HandleCmd(cmd::TransitionStatus const& cmd) -> void - { - if (cmd.GetResult() != cmd::Result::Ok) { - DDSTask::Id taskId(cmd.GetTaskId()); - std::lock_guard lk(*fMtx); - for (auto& op : fChangeStateOps) { - if (!op.second.IsCompleted() && op.second.ContainsTask(taskId)) { - if (fStateData.at(fStateIndex.at(taskId)).state != op.second.GetTargetState()) { - FAIR_LOG(error) << cmd.GetTransition() << " transition failed for " << cmd.GetDeviceId() << ", device is in " << cmd.GetCurrentState() << " state."; - op.second.Complete(MakeErrorCode(ErrorCode::DeviceChangeStateFailed)); - } else { - FAIR_LOG(debug) << cmd.GetTransition() << " transition failed for " << cmd.GetDeviceId() << ", device is already in " << cmd.GetCurrentState() << " state."; - } - } - } - } - } - - auto HandleCmd(cmd::Properties const& cmd) -> void - { - std::unique_lock lk(*fMtx); - try { - auto& op(fGetPropertiesOps.at(cmd.GetRequestId())); - lk.unlock(); - op.Update(cmd.GetDeviceId(), cmd.GetResult(), cmd.GetProps()); - } catch (std::out_of_range& e) { - FAIR_LOG(debug) << "GetProperties operation (request id: " << cmd.GetRequestId() - << ") not found (probably completed or timed out), " - << "discarding reply of device " << cmd.GetDeviceId(); - } - } - - auto HandleCmd(cmd::PropertiesSet const& cmd) -> void - { - std::unique_lock lk(*fMtx); - try { - auto& op(fSetPropertiesOps.at(cmd.GetRequestId())); - lk.unlock(); - op.Update(cmd.GetDeviceId(), cmd.GetResult()); - } catch (std::out_of_range& e) { - FAIR_LOG(debug) << "SetProperties operation (request id: " << cmd.GetRequestId() - << ") not found (probably completed or timed out), " - << "discarding reply of device " << cmd.GetDeviceId(); - } - } - - using Duration = std::chrono::microseconds; - using ChangeStateCompletionSignature = void(std::error_code, TopologyState); - - private: - struct ChangeStateOp - { - using Id = std::size_t; - using Count = unsigned int; - - template - ChangeStateOp(Id id, - const TopologyTransition transition, - std::vector tasks, - TopologyState& stateData, - Duration timeout, - std::mutex& mutex, - Executor const & ex, - Allocator const & alloc, - Handler&& handler) - : fId(id) - , fOp(ex, alloc, std::move(handler)) - , fStateData(stateData) - , fTimer(ex) - , fCount(0) - , fTasks(std::move(tasks)) - , fTargetState(expectedState.at(transition)) - , fMtx(mutex) - { - if (timeout > std::chrono::milliseconds(0)) { - fTimer.expires_after(timeout); - fTimer.async_wait([&](std::error_code ec) { - if (!ec) { - std::lock_guard lk(fMtx); - fOp.Timeout(fStateData); - } - }); - } - if (fTasks.empty()) { - FAIR_LOG(warn) << "ChangeState initiated on an empty set of tasks, check the path argument."; - } - } - ChangeStateOp() = delete; - ChangeStateOp(const ChangeStateOp&) = delete; - ChangeStateOp& operator=(const ChangeStateOp&) = delete; - ChangeStateOp(ChangeStateOp&&) = default; - ChangeStateOp& operator=(ChangeStateOp&&) = default; - ~ChangeStateOp() = default; - - /// precondition: fMtx is locked. - auto ResetCount(const TopologyStateIndex& stateIndex, const TopologyState& stateData) -> void - { - fCount = std::count_if(stateIndex.cbegin(), stateIndex.cend(), [=](const auto& s) { - if (ContainsTask(stateData.at(s.second).taskId)) { - return stateData.at(s.second).state == fTargetState; - } else { - return false; - } - }); - } - - /// precondition: fMtx is locked. - auto Update(const DDSTask::Id taskId, const DeviceState currentState) -> void - { - if (!fOp.IsCompleted() && ContainsTask(taskId)) { - if (currentState == fTargetState) { - ++fCount; - } - TryCompletion(); - } - } - - /// precondition: fMtx is locked. - auto TryCompletion() -> void - { - if (!fOp.IsCompleted() && fCount == fTasks.size()) { - Complete(std::error_code()); - } - } - - /// precondition: fMtx is locked. - auto Complete(std::error_code ec) -> void - { - fTimer.cancel(); - fOp.Complete(ec, fStateData); - } - - /// precondition: fMtx is locked. - auto ContainsTask(DDSTask::Id id) -> bool - { - auto it = std::find_if(fTasks.begin(), fTasks.end(), [id](const DDSTask& t) { return t.GetId() == id; }); - return it != fTasks.end(); - } - - bool IsCompleted() { return fOp.IsCompleted(); } - - auto GetTargetState() const -> DeviceState { return fTargetState; } - - private: - Id const fId; - AsioAsyncOp fOp; - TopologyState& fStateData; - asio::steady_timer fTimer; - Count fCount; - std::vector fTasks; - DeviceState fTargetState; - std::mutex& fMtx; - }; - - public: - /// @brief Initiate state transition on all FairMQ devices in this topology - /// @param transition FairMQ device state machine transition - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - /// - /// @par Usage examples - /// With lambda: - /// @code - /// topo.AsyncChangeState( - /// fair::mq::sdk::TopologyTransition::InitDevice, - /// std::chrono::milliseconds(500), - /// [](std::error_code ec, TopologyState state) { - /// if (!ec) { - /// // success - /// } else if (ec.category().name() == "fairmq") { - /// switch (static_cast(ec.value())) { - /// case fair::mq::ErrorCode::OperationTimeout: - /// // async operation timed out - /// case fair::mq::ErrorCode::OperationCanceled: - /// // async operation canceled - /// case fair::mq::ErrorCode::DeviceChangeStateFailed: - /// // failed to change state of a fairmq device - /// default: - /// } - /// } - /// } - /// ); - /// @endcode - /// With future: - /// @code - /// auto fut = topo.AsyncChangeState(fair::mq::sdk::TopologyTransition::InitDevice, - /// std::chrono::milliseconds(500), - /// asio::use_future); - /// try { - /// fair::mq::sdk::TopologyState state = fut.get(); - /// // success - /// } catch (const std::system_error& ex) { - /// auto ec(ex.code()); - /// if (ec.category().name() == "fairmq") { - /// switch (static_cast(ec.value())) { - /// case fair::mq::ErrorCode::OperationTimeout: - /// // async operation timed out - /// case fair::mq::ErrorCode::OperationCanceled: - /// // async operation canceled - /// case fair::mq::ErrorCode::DeviceChangeStateFailed: - /// // failed to change state of a fairmq device - /// default: - /// } - /// } - /// } - /// @endcode - /// With coroutine (C++20, see https://en.cppreference.com/w/cpp/language/coroutines): - /// @code - /// try { - /// fair::mq::sdk::TopologyState state = co_await - /// topo.AsyncChangeState(fair::mq::sdk::TopologyTransition::InitDevice, - /// std::chrono::milliseconds(500), - /// asio::use_awaitable); - /// // success - /// } catch (const std::system_error& ex) { - /// auto ec(ex.code()); - /// if (ec.category().name() == "fairmq") { - /// switch (static_cast(ec.value())) { - /// case fair::mq::ErrorCode::OperationTimeout: - /// // async operation timed out - /// case fair::mq::ErrorCode::OperationCanceled: - /// // async operation canceled - /// case fair::mq::ErrorCode::DeviceChangeStateFailed: - /// // failed to change state of a fairmq device - /// default: - /// } - /// } - /// } - /// @endcode - template - auto AsyncChangeState(const TopologyTransition transition, - const std::string& path, - Duration timeout, - CompletionToken&& token) - { - return asio::async_initiate([&](auto handler) { - typename ChangeStateOp::Id const id(tools::UuidHash()); - - std::lock_guard lk(*fMtx); - - for (auto it = begin(fChangeStateOps); it != end(fChangeStateOps);) { - if (it->second.IsCompleted()) { - it = fChangeStateOps.erase(it); - } else { - ++it; - } - } - - auto p = fChangeStateOps.emplace( - std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple(id, - transition, - fDDSTopo.GetTasks(path), - fStateData, - timeout, - *fMtx, - AsioBase::GetExecutor(), - AsioBase::GetAllocator(), - std::move(handler))); - - cmd::Cmds cmds(cmd::make(transition)); - fDDSSession.SendCommand(cmds.Serialize(), path); - - p.first->second.ResetCount(fStateIndex, fStateData); - // TODO: make sure following operation properly queues the completion and not doing it directly out of initiation call. - p.first->second.TryCompletion(); - - }, - token); - } - - /// @brief Initiate state transition on all FairMQ devices in this topology - /// @param transition FairMQ device state machine transition - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncChangeState(const TopologyTransition transition, CompletionToken&& token) - { - return AsyncChangeState(transition, "", Duration(0), std::move(token)); - } - - /// @brief Initiate state transition on all FairMQ devices in this topology with a timeout - /// @param transition FairMQ device state machine transition - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncChangeState(const TopologyTransition transition, Duration timeout, CompletionToken&& token) - { - return AsyncChangeState(transition, "", timeout, std::move(token)); - } - - /// @brief Initiate state transition on all FairMQ devices in this topology with a timeout - /// @param transition FairMQ device state machine transition - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncChangeState(const TopologyTransition transition, const std::string& path, CompletionToken&& token) - { - return AsyncChangeState(transition, path, Duration(0), std::move(token)); - } - - /// @brief Perform state transition on FairMQ devices in this topology for a specified topology path - /// @param transition FairMQ device state machine transition - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @throws std::system_error - auto ChangeState(const TopologyTransition transition, const std::string& path = "", Duration timeout = Duration(0)) - -> std::pair - { - tools::SharedSemaphore blocker; - std::error_code ec; - TopologyState state; - AsyncChangeState(transition, path, timeout, [&, blocker](std::error_code _ec, TopologyState _state) mutable { - ec = _ec; - state = _state; - blocker.Signal(); - }); - blocker.Wait(); - return {ec, state}; - } - - /// @brief Perform state transition on all FairMQ devices in this topology with a timeout - /// @param transition FairMQ device state machine transition - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @throws std::system_error - auto ChangeState(const TopologyTransition transition, Duration timeout) - -> std::pair - { - return ChangeState(transition, "", timeout); - } - - /// @brief Returns the current state of the topology - /// @return map of id : DeviceStatus - auto GetCurrentState() const -> TopologyState - { - std::lock_guard lk(*fMtx); - return fStateData; - } - - auto AggregateState() const -> DeviceState { return sdk::AggregateState(GetCurrentState()); } - - auto StateEqualsTo(DeviceState state) const -> bool { return sdk::StateEqualsTo(GetCurrentState(), state); } - - using WaitForStateCompletionSignature = void(std::error_code); - - private: - struct WaitForStateOp - { - using Id = std::size_t; - using Count = unsigned int; - - template - WaitForStateOp(Id id, - DeviceState targetLastState, - DeviceState targetCurrentState, - std::vector tasks, - Duration timeout, - std::mutex& mutex, - Executor const & ex, - Allocator const & alloc, - Handler&& handler) - : fId(id) - , fOp(ex, alloc, std::move(handler)) - , fTimer(ex) - , fCount(0) - , fTasks(std::move(tasks)) - , fTargetLastState(targetLastState) - , fTargetCurrentState(targetCurrentState) - , fMtx(mutex) - { - if (timeout > std::chrono::milliseconds(0)) { - fTimer.expires_after(timeout); - fTimer.async_wait([&](std::error_code ec) { - if (!ec) { - std::lock_guard lk(fMtx); - fOp.Timeout(); - } - }); - } - if (fTasks.empty()) { - FAIR_LOG(warn) << "WaitForState initiated on an empty set of tasks, check the path argument."; - } - } - WaitForStateOp() = delete; - WaitForStateOp(const WaitForStateOp&) = delete; - WaitForStateOp& operator=(const WaitForStateOp&) = delete; - WaitForStateOp(WaitForStateOp&&) = default; - WaitForStateOp& operator=(WaitForStateOp&&) = default; - ~WaitForStateOp() = default; - - /// precondition: fMtx is locked. - auto ResetCount(const TopologyStateIndex& stateIndex, const TopologyState& stateData) -> void - { - fCount = std::count_if(stateIndex.cbegin(), stateIndex.cend(), [=](const auto& s) { - if (ContainsTask(stateData.at(s.second).taskId)) { - return stateData.at(s.second).state == fTargetCurrentState && - (stateData.at(s.second).lastState == fTargetLastState || fTargetLastState == DeviceState::Undefined); - } else { - return false; - } - }); - } - - /// precondition: fMtx is locked. - auto Update(const DDSTask::Id taskId, const DeviceState lastState, const DeviceState currentState) -> void - { - if (!fOp.IsCompleted() && ContainsTask(taskId)) { - if (currentState == fTargetCurrentState && - (lastState == fTargetLastState || fTargetLastState == DeviceState::Undefined)) { - ++fCount; - } - TryCompletion(); - } - } - - /// precondition: fMtx is locked. - auto TryCompletion() -> void - { - if (!fOp.IsCompleted() && fCount == fTasks.size()) { - fTimer.cancel(); - fOp.Complete(); - } - } - - bool IsCompleted() { return fOp.IsCompleted(); } - - private: - Id const fId; - AsioAsyncOp fOp; - asio::steady_timer fTimer; - Count fCount; - std::vector fTasks; - DeviceState fTargetLastState; - DeviceState fTargetCurrentState; - std::mutex& fMtx; - - /// precondition: fMtx is locked. - auto ContainsTask(DDSTask::Id id) -> bool - { - auto it = std::find_if(fTasks.begin(), fTasks.end(), [id](const DDSTask& t) { return t.GetId() == id; }); - return it != fTasks.end(); - } - }; - - public: - /// @brief Initiate waiting for selected FairMQ devices to reach given last & current state in this topology - /// @param targetLastState the target last device state to wait for - /// @param targetCurrentState the target device state to wait for - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncWaitForState(const DeviceState targetLastState, - const DeviceState targetCurrentState, - const std::string& path, - Duration timeout, - CompletionToken&& token) - { - return asio::async_initiate([&](auto handler) { - typename GetPropertiesOp::Id const id(tools::UuidHash()); - - std::lock_guard lk(*fMtx); - - for (auto it = begin(fWaitForStateOps); it != end(fWaitForStateOps);) { - if (it->second.IsCompleted()) { - it = fWaitForStateOps.erase(it); - } else { - ++it; - } - } - - auto p = fWaitForStateOps.emplace( - std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple(id, - targetLastState, - targetCurrentState, - fDDSTopo.GetTasks(path), - timeout, - *fMtx, - AsioBase::GetExecutor(), - AsioBase::GetAllocator(), - std::move(handler))); - p.first->second.ResetCount(fStateIndex, fStateData); - // TODO: make sure following operation properly queues the completion and not doing it directly out of initiation call. - p.first->second.TryCompletion(); - }, - token); - } - - /// @brief Initiate waiting for selected FairMQ devices to reach given last & current state in this topology - /// @param targetLastState the target last device state to wait for - /// @param targetCurrentState the target device state to wait for - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncWaitForState(const DeviceState targetLastState, const DeviceState targetCurrentState, CompletionToken&& token) - { - return AsyncWaitForState(targetLastState, targetCurrentState, "", Duration(0), std::move(token)); - } - - /// @brief Initiate waiting for selected FairMQ devices to reach given current state in this topology - /// @param targetCurrentState the target device state to wait for - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncWaitForState(const DeviceState targetCurrentState, CompletionToken&& token) - { - return AsyncWaitForState(DeviceState::Undefined, targetCurrentState, "", Duration(0), std::move(token)); - } - - /// @brief Wait for selected FairMQ devices to reach given last & current state in this topology - /// @param targetLastState the target last device state to wait for - /// @param targetCurrentState the target device state to wait for - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @throws std::system_error - auto WaitForState(const DeviceState targetLastState, const DeviceState targetCurrentState, const std::string& path = "", Duration timeout = Duration(0)) - -> std::error_code - { - tools::SharedSemaphore blocker; - std::error_code ec; - AsyncWaitForState(targetLastState, targetCurrentState, path, timeout, [&, blocker](std::error_code _ec) mutable { - ec = _ec; - blocker.Signal(); - }); - blocker.Wait(); - return ec; - } - - /// @brief Wait for selected FairMQ devices to reach given current state in this topology - /// @param targetCurrentState the target device state to wait for - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @throws std::system_error - auto WaitForState(const DeviceState targetCurrentState, const std::string& path = "", Duration timeout = Duration(0)) - -> std::error_code - { - return WaitForState(DeviceState::Undefined, targetCurrentState, path, timeout); - } - - using GetPropertiesCompletionSignature = void(std::error_code, GetPropertiesResult); - - private: - struct GetPropertiesOp - { - using Id = std::size_t; - using GetCount = unsigned int; - - template - GetPropertiesOp(Id id, - GetCount expectedCount, - Duration timeout, - std::mutex& mutex, - Executor const & ex, - Allocator const & alloc, - Handler&& handler) - : fId(id) - , fOp(ex, alloc, std::move(handler)) - , fTimer(ex) - , fCount(0) - , fExpectedCount(expectedCount) - , fMtx(mutex) - { - if (timeout > std::chrono::milliseconds(0)) { - fTimer.expires_after(timeout); - fTimer.async_wait([&](std::error_code ec) { - if (!ec) { - std::lock_guard lk(fMtx); - fOp.Timeout(fResult); - } - }); - } - if (expectedCount == 0) { - FAIR_LOG(warn) << "GetProperties initiated on an empty set of tasks, check the path argument."; - } - // FAIR_LOG(debug) << "GetProperties " << fId << " with expected count of " << fExpectedCount << " started."; - } - GetPropertiesOp() = delete; - GetPropertiesOp(const GetPropertiesOp&) = delete; - GetPropertiesOp& operator=(const GetPropertiesOp&) = delete; - GetPropertiesOp(GetPropertiesOp&&) = default; - GetPropertiesOp& operator=(GetPropertiesOp&&) = default; - ~GetPropertiesOp() = default; - - auto Update(const std::string& deviceId, cmd::Result result, DeviceProperties props) -> void - { - std::lock_guard lk(fMtx); - if (cmd::Result::Ok != result) { - fResult.failed.insert(deviceId); - } else { - fResult.devices.insert({deviceId, {std::move(props)}}); - } - ++fCount; - TryCompletion(); - } - - bool IsCompleted() { return fOp.IsCompleted(); } - - private: - Id const fId; - AsioAsyncOp fOp; - asio::steady_timer fTimer; - GetCount fCount; - GetCount const fExpectedCount; - GetPropertiesResult fResult; - std::mutex& fMtx; - - /// precondition: fMtx is locked. - auto TryCompletion() -> void - { - if (!fOp.IsCompleted() && fCount == fExpectedCount) { - fTimer.cancel(); - if (!fResult.failed.empty()) { - fOp.Complete(MakeErrorCode(ErrorCode::DeviceGetPropertiesFailed), std::move(fResult)); - } else { - fOp.Complete(std::move(fResult)); - } - } - } - }; - - public: - /// @brief Initiate property query on selected FairMQ devices in this topology - /// @param query Key(s) to be queried (regex) - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncGetProperties(DevicePropertyQuery const& query, - const std::string& path, - Duration timeout, - CompletionToken&& token) - { - return asio::async_initiate( - [&](auto handler) { - typename GetPropertiesOp::Id const id(tools::UuidHash()); - - std::lock_guard lk(*fMtx); - - for (auto it = begin(fGetPropertiesOps); it != end(fGetPropertiesOps);) { - if (it->second.IsCompleted()) { - it = fGetPropertiesOps.erase(it); - } else { - ++it; - } - } - - fGetPropertiesOps.emplace( - std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple(id, - fDDSTopo.GetTasks(path).size(), - timeout, - *fMtx, - AsioBase::GetExecutor(), - AsioBase::GetAllocator(), - std::move(handler))); - - cmd::Cmds const cmds(cmd::make(id, query)); - fDDSSession.SendCommand(cmds.Serialize(), path); - }, - token); - } - - /// @brief Initiate property query on selected FairMQ devices in this topology - /// @param query Key(s) to be queried (regex) - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncGetProperties(DevicePropertyQuery const& query, CompletionToken&& token) - { - return AsyncGetProperties(query, "", Duration(0), std::move(token)); - } - - /// @brief Query properties on selected FairMQ devices in this topology - /// @param query Key(s) to be queried (regex) - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @throws std::system_error - auto GetProperties(DevicePropertyQuery const& query, const std::string& path = "", Duration timeout = Duration(0)) - -> std::pair - { - tools::SharedSemaphore blocker; - std::error_code ec; - GetPropertiesResult result; - AsyncGetProperties(query, path, timeout, [&, blocker](std::error_code _ec, GetPropertiesResult _result) mutable { - ec = _ec; - result = _result; - blocker.Signal(); - }); - blocker.Wait(); - return {ec, result}; - } - - using SetPropertiesCompletionSignature = void(std::error_code, FailedDevices); - - private: - struct SetPropertiesOp - { - using Id = std::size_t; - using SetCount = unsigned int; - - template - SetPropertiesOp(Id id, - SetCount expectedCount, - Duration timeout, - std::mutex& mutex, - Executor const & ex, - Allocator const & alloc, - Handler&& handler) - : fId(id) - , fOp(ex, alloc, std::move(handler)) - , fTimer(ex) - , fCount(0) - , fExpectedCount(expectedCount) - , fMtx(mutex) - { - if (timeout > std::chrono::milliseconds(0)) { - fTimer.expires_after(timeout); - fTimer.async_wait([&](std::error_code ec) { - if (!ec) { - std::lock_guard lk(fMtx); - fOp.Timeout(fFailedDevices); - } - }); - } - if (expectedCount == 0) { - FAIR_LOG(warn) << "SetProperties initiated on an empty set of tasks, check the path argument."; - } - // FAIR_LOG(debug) << "SetProperties " << fId << " with expected count of " << fExpectedCount << " started."; - } - SetPropertiesOp() = delete; - SetPropertiesOp(const SetPropertiesOp&) = delete; - SetPropertiesOp& operator=(const SetPropertiesOp&) = delete; - SetPropertiesOp(SetPropertiesOp&&) = default; - SetPropertiesOp& operator=(SetPropertiesOp&&) = default; - ~SetPropertiesOp() = default; - - auto Update(const std::string& deviceId, cmd::Result result) -> void - { - std::lock_guard lk(fMtx); - if (cmd::Result::Ok != result) { - fFailedDevices.insert(deviceId); - } - ++fCount; - TryCompletion(); - } - - bool IsCompleted() { return fOp.IsCompleted(); } - - private: - Id const fId; - AsioAsyncOp fOp; - asio::steady_timer fTimer; - SetCount fCount; - SetCount const fExpectedCount; - FailedDevices fFailedDevices; - std::mutex& fMtx; - - /// precondition: fMtx is locked. - auto TryCompletion() -> void - { - if (!fOp.IsCompleted() && fCount == fExpectedCount) { - fTimer.cancel(); - if (!fFailedDevices.empty()) { - fOp.Complete(MakeErrorCode(ErrorCode::DeviceSetPropertiesFailed), fFailedDevices); - } else { - fOp.Complete(fFailedDevices); - } - } - } - }; - - public: - /// @brief Initiate property update on selected FairMQ devices in this topology - /// @param props Properties to set - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncSetProperties(const DeviceProperties& props, - const std::string& path, - Duration timeout, - CompletionToken&& token) - { - return asio::async_initiate( - [&](auto handler) { - typename SetPropertiesOp::Id const id(tools::UuidHash()); - - std::lock_guard lk(*fMtx); - - for (auto it = begin(fGetPropertiesOps); it != end(fGetPropertiesOps);) { - if (it->second.IsCompleted()) { - it = fGetPropertiesOps.erase(it); - } else { - ++it; - } - } - - fSetPropertiesOps.emplace( - std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple(id, - fDDSTopo.GetTasks(path).size(), - timeout, - *fMtx, - AsioBase::GetExecutor(), - AsioBase::GetAllocator(), - std::move(handler))); - - cmd::Cmds const cmds(cmd::make(id, props)); - fDDSSession.SendCommand(cmds.Serialize(), path); - }, - token); - } - - /// @brief Initiate property update on selected FairMQ devices in this topology - /// @param props Properties to set - /// @param token Asio completion token - /// @tparam CompletionToken Asio completion token type - /// @throws std::system_error - template - auto AsyncSetProperties(DeviceProperties const & props, CompletionToken&& token) - { - return AsyncSetProperties(props, "", Duration(0), std::move(token)); - } - - /// @brief Set properties on selected FairMQ devices in this topology - /// @param props Properties to set - /// @param path Select a subset of FairMQ devices in this topology, empty selects all - /// @param timeout Timeout in milliseconds, 0 means no timeout - /// @throws std::system_error - auto SetProperties(DeviceProperties const& properties, const std::string& path = "", Duration timeout = Duration(0)) - -> std::pair - { - tools::SharedSemaphore blocker; - std::error_code ec; - FailedDevices failed; - AsyncSetProperties(properties, path, timeout, [&, blocker](std::error_code _ec, FailedDevices _failed) mutable { - ec = _ec; - failed = _failed; - blocker.Signal(); - }); - blocker.Wait(); - return {ec, failed}; - } - - Duration GetHeartbeatInterval() const { return fHeartbeatInterval; } - void SetHeartbeatInterval(Duration duration) { fHeartbeatInterval = duration; } - - private: - using TransitionedCount = unsigned int; - - DDSSession fDDSSession; - DDSTopology fDDSTopo; - TopologyState fStateData; - TopologyStateIndex fStateIndex; - - mutable std::unique_ptr fMtx; - - std::unique_ptr fStateChangeSubscriptionsCV; - unsigned int fNumStateChangePublishers; - asio::steady_timer fHeartbeatsTimer; - Duration fHeartbeatInterval; - - std::unordered_map fChangeStateOps; - std::unordered_map fWaitForStateOps; - std::unordered_map fSetPropertiesOps; - std::unordered_map fGetPropertiesOps; - - auto makeTopologyState() -> void - { - fStateData.reserve(fDDSTopo.GetTasks().size()); - - int index = 0; - - for (const auto& task : fDDSTopo.GetTasks()) { - fStateData.push_back(DeviceStatus{false, DeviceState::Undefined, DeviceState::Undefined, task.GetId(), task.GetCollectionId()}); - fStateIndex.emplace(task.GetId(), index); - index++; - } - } - - /// precodition: fMtx is locked. - auto GetCurrentStateUnsafe() const -> TopologyState - { - return fStateData; - } -}; - -using Topology = BasicTopology; -using Topo = Topology; - -/// @brief Helper to (Re)Construct a FairMQ topology based on already existing native DDS API objects -/// @param nativeSession Existing and initialized CSession (either via create() or attach()) -/// @param nativeTopo Existing CTopology that is activated on the given nativeSession -/// @param env Optional DDSEnv (needed primarily for unit testing) -/// @param blockUntilConnected if true, ctor will wait for all tasks to confirm subscriptions -auto MakeTopology(dds::topology_api::CTopology nativeTopo, - std::shared_ptr nativeSession, - DDSEnv env = {}, - bool blockUntilConnected = false) -> Topology; - -} // namespace fair::mq::sdk - -#endif /* FAIR_MQ_SDK_TOPOLOGY_H */ diff --git a/fairmq/sdk/Traits.h b/fairmq/sdk/Traits.h deleted file mode 100644 index 8c5929c7..00000000 --- a/fairmq/sdk/Traits.h +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_TRAITS_H -#define FAIR_MQ_SDK_TRAITS_H - -#include -#include -#include - -namespace asio::detail { - -/// Specialize to match our coding conventions -template -struct associated_executor_impl::value>> -{ - using type = typename T::ExecutorType; - - static auto get(const T& obj, const Executor& /*ex = Executor()*/) noexcept -> type - { - return obj.GetExecutor(); - } -}; - -/// Specialize to match our coding conventions -template -struct associated_allocator_impl> -{ - using type = typename T::AllocatorType; - - static auto get(const T& obj, const Allocator& /*alloc = Allocator()*/) noexcept -> type - { - return obj.GetAllocator(); - } -}; - -} /* namespace asio::detail */ - -#endif /* FAIR_MQ_SDK_TRAITS_H */ diff --git a/fairmq/sdk/commands/CMakeLists.txt b/fairmq/sdk/commands/CMakeLists.txt deleted file mode 100644 index 417c2c70..00000000 --- a/fairmq/sdk/commands/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -################################################################################ -# Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # -# # -# This software is distributed under the terms of the # -# GNU Lesser General Public Licence (LGPL) version 3, # -# copied verbatim in the file "LICENSE" # -################################################################################ - -set(target Commands) - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormat.h - COMMAND $ -c -o ${CMAKE_CURRENT_BINARY_DIR} CommandsFormat.fbs - COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormat_generated.h ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormat.h - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -# JSON serialization needs to see the .fbs file at run time, save it as constexpr string instead of locating/opening it every time -file(STRINGS CommandsFormat.fbs tmp) -list(JOIN tmp "\n" commands_format_def_fbs) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CommandsFormatDef.h.in ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormatDef.h) - -add_library(${target} Commands.cxx Commands.h ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormat.h ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormatDef.h) -add_library(FairMQ::${target} ALIAS ${target}) - -set(_flatbuffers flatbuffers::flatbuffers_shared) -if(NOT TARGET flatbuffers::flatbuffers_shared AND TARGET flatbuffers::flatbuffers) - set(_flatbuffers flatbuffers::flatbuffers) -endif() - -target_link_libraries(${target} - PUBLIC - StateMachine - Tools - - PRIVATE - ${_flatbuffers} -) -target_compile_features(${target} PUBLIC cxx_std_17) -set_target_properties(${target} PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" - OUTPUT_NAME FairMQ${target} -) - -target_include_directories(${target} - PUBLIC - $ - $ -) - -install( - TARGETS ${target} - EXPORT ${PROJECT_EXPORT_SET} - DESTINATION ${PROJECT_INSTALL_LIBDIR} -) - -install(FILES Commands.h - DESTINATION ${PROJECT_INSTALL_INCDIR}/sdk/commands -) diff --git a/fairmq/sdk/commands/Commands.cxx b/fairmq/sdk/commands/Commands.cxx deleted file mode 100644 index 0d79d962..00000000 --- a/fairmq/sdk/commands/Commands.cxx +++ /dev/null @@ -1,479 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "Commands.h" - -#include -#include - -#include - -#include - -using namespace std; - -namespace fair::mq::sdk::cmd -{ - -array fbResultToResult = -{ - { - Result::Ok, - Result::Failure - } -}; - -array resultToFBResult = -{ - { - FBResult::FBResult_Ok, - FBResult::FBResult_Failure - } -}; - -array resultNames = -{ - { - "Ok", - "Failure" - } -}; - -array typeNames = -{ - { - "CheckState", - "ChangeState", - "DumpConfig", - "SubscribeToStateChange", - "UnsubscribeFromStateChange", - "StateChangeExitingReceived", - "GetProperties", - "SetProperties", - "SubscriptionHeartbeat", - - "CurrentState", - "TransitionStatus", - "Config", - "StateChangeSubscription", - "StateChangeUnsubscription", - "StateChange", - "Properties", - "PropertiesSet" - } -}; - -array fbStateToMQState = -{ - { - fair::mq::State::Undefined, - fair::mq::State::Ok, - fair::mq::State::Error, - fair::mq::State::Idle, - fair::mq::State::InitializingDevice, - fair::mq::State::Initialized, - fair::mq::State::Binding, - fair::mq::State::Bound, - fair::mq::State::Connecting, - fair::mq::State::DeviceReady, - fair::mq::State::InitializingTask, - fair::mq::State::Ready, - fair::mq::State::Running, - fair::mq::State::ResettingTask, - fair::mq::State::ResettingDevice, - fair::mq::State::Exiting - } -}; - -array mqStateToFBState = -{ - { - sdk::cmd::FBState_Undefined, - sdk::cmd::FBState_Ok, - sdk::cmd::FBState_Error, - sdk::cmd::FBState_Idle, - sdk::cmd::FBState_InitializingDevice, - sdk::cmd::FBState_Initialized, - sdk::cmd::FBState_Binding, - sdk::cmd::FBState_Bound, - sdk::cmd::FBState_Connecting, - sdk::cmd::FBState_DeviceReady, - sdk::cmd::FBState_InitializingTask, - sdk::cmd::FBState_Ready, - sdk::cmd::FBState_Running, - sdk::cmd::FBState_ResettingTask, - sdk::cmd::FBState_ResettingDevice, - sdk::cmd::FBState_Exiting - } -}; - -array fbTransitionToMQTransition = -{ - { - fair::mq::Transition::Auto, - fair::mq::Transition::InitDevice, - fair::mq::Transition::CompleteInit, - fair::mq::Transition::Bind, - fair::mq::Transition::Connect, - fair::mq::Transition::InitTask, - fair::mq::Transition::Run, - fair::mq::Transition::Stop, - fair::mq::Transition::ResetTask, - fair::mq::Transition::ResetDevice, - fair::mq::Transition::End, - fair::mq::Transition::ErrorFound - } -}; - -array mqTransitionToFBTransition = -{ - { - sdk::cmd::FBTransition_Auto, - sdk::cmd::FBTransition_InitDevice, - sdk::cmd::FBTransition_CompleteInit, - sdk::cmd::FBTransition_Bind, - sdk::cmd::FBTransition_Connect, - sdk::cmd::FBTransition_InitTask, - sdk::cmd::FBTransition_Run, - sdk::cmd::FBTransition_Stop, - sdk::cmd::FBTransition_ResetTask, - sdk::cmd::FBTransition_ResetDevice, - sdk::cmd::FBTransition_End, - sdk::cmd::FBTransition_ErrorFound - } -}; - -array typeToFBCmd = -{ - { - FBCmd::FBCmd_check_state, - FBCmd::FBCmd_change_state, - FBCmd::FBCmd_dump_config, - FBCmd::FBCmd_subscribe_to_state_change, - FBCmd::FBCmd_unsubscribe_from_state_change, - FBCmd::FBCmd_state_change_exiting_received, - FBCmd::FBCmd_get_properties, - FBCmd::FBCmd_set_properties, - FBCmd::FBCmd_subscription_heartbeat, - FBCmd::FBCmd_current_state, - FBCmd::FBCmd_transition_status, - FBCmd::FBCmd_config, - FBCmd::FBCmd_state_change_subscription, - FBCmd::FBCmd_state_change_unsubscription, - FBCmd::FBCmd_state_change, - FBCmd::FBCmd_properties, - FBCmd::FBCmd_properties_set - } -}; - -array fbCmdToType = -{ - { - Type::check_state, - Type::change_state, - Type::dump_config, - Type::subscribe_to_state_change, - Type::unsubscribe_from_state_change, - Type::state_change_exiting_received, - Type::get_properties, - Type::set_properties, - Type::subscription_heartbeat, - Type::current_state, - Type::transition_status, - Type::config, - Type::state_change_subscription, - Type::state_change_unsubscription, - Type::state_change, - Type::properties, - Type::properties_set - } -}; - -fair::mq::State GetMQState(const FBState state) { return fbStateToMQState.at(state); } -FBState GetFBState(const fair::mq::State state) { return mqStateToFBState.at(static_cast(state)); } -fair::mq::Transition GetMQTransition(const FBTransition transition) { return fbTransitionToMQTransition.at(transition); } -FBTransition GetFBTransition(const fair::mq::Transition transition) { return mqTransitionToFBTransition.at(static_cast(transition)); } - -Result GetResult(const FBResult result) { return fbResultToResult.at(result); } -FBResult GetFBResult(const Result result) { return resultToFBResult.at(static_cast(result)); } -string GetResultName(const Result result) { return resultNames.at(static_cast(result)); } -string GetTypeName(const Type type) { return typeNames.at(static_cast(type)); } - -FBCmd GetFBCmd(const Type type) { return typeToFBCmd.at(static_cast(type)); } - -string Cmds::Serialize(const Format type) const -{ - flatbuffers::FlatBufferBuilder fbb; - vector> commandOffsets; - - for (auto& cmd : fCmds) { - flatbuffers::Offset cmdOffset; - unique_ptr cmdBuilder; // delay the creation of the builder, because child strings need to be constructed first (which are conditional) - - switch (cmd->GetType()) { - case Type::check_state: { - cmdBuilder = make_unique(fbb); - } - break; - case Type::change_state: { - cmdBuilder = make_unique(fbb); - cmdBuilder->add_transition(GetFBTransition(static_cast(*cmd).GetTransition())); - } - break; - case Type::dump_config: { - cmdBuilder = make_unique(fbb); - } - break; - break; - case Type::subscribe_to_state_change: { - auto _cmd = static_cast(*cmd); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_interval(_cmd.GetInterval()); - } - break; - case Type::unsubscribe_from_state_change: { - cmdBuilder = make_unique(fbb); - } - break; - case Type::state_change_exiting_received: { - cmdBuilder = make_unique(fbb); - } - break; - case Type::get_properties: { - auto _cmd = static_cast(*cmd); - auto query = fbb.CreateString(_cmd.GetQuery()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_request_id(_cmd.GetRequestId()); - cmdBuilder->add_property_query(query); - } - break; - case Type::set_properties: { - auto _cmd = static_cast(*cmd); - std::vector> propsVector; - for (auto const& e : _cmd.GetProps()) { - auto const key(fbb.CreateString(e.first)); - auto const val(fbb.CreateString(e.second)); - propsVector.push_back(CreateFBProperty(fbb, key, val)); - } - auto props = fbb.CreateVector(propsVector); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_request_id(_cmd.GetRequestId()); - cmdBuilder->add_properties(props); - } - break; - case Type::subscription_heartbeat: { - auto _cmd = static_cast(*cmd); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_interval(_cmd.GetInterval()); - } - break; - case Type::current_state: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_current_state(GetFBState(_cmd.GetCurrentState())); - } - break; - case Type::transition_status: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_task_id(_cmd.GetTaskId()); - cmdBuilder->add_result(GetFBResult(_cmd.GetResult())); - cmdBuilder->add_transition(GetFBTransition(_cmd.GetTransition())); - cmdBuilder->add_current_state(GetFBState(_cmd.GetCurrentState())); - } - break; - case Type::config: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - auto config = fbb.CreateString(_cmd.GetConfig()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_config_string(config); - } - break; - case Type::state_change_subscription: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_task_id(_cmd.GetTaskId()); - cmdBuilder->add_result(GetFBResult(_cmd.GetResult())); - } - break; - case Type::state_change_unsubscription: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_task_id(_cmd.GetTaskId()); - cmdBuilder->add_result(GetFBResult(_cmd.GetResult())); - } - break; - case Type::state_change: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_task_id(_cmd.GetTaskId()); - cmdBuilder->add_last_state(GetFBState(_cmd.GetLastState())); - cmdBuilder->add_current_state(GetFBState(_cmd.GetCurrentState())); - } - break; - case Type::properties: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - - std::vector> propsVector; - for (const auto& e : _cmd.GetProps()) { - auto key = fbb.CreateString(e.first); - auto val = fbb.CreateString(e.second); - auto prop = CreateFBProperty(fbb, key, val); - propsVector.push_back(prop); - } - auto props = fbb.CreateVector(propsVector); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_request_id(_cmd.GetRequestId()); - cmdBuilder->add_result(GetFBResult(_cmd.GetResult())); - cmdBuilder->add_properties(props); - } - break; - case Type::properties_set: { - auto _cmd = static_cast(*cmd); - auto deviceId = fbb.CreateString(_cmd.GetDeviceId()); - cmdBuilder = make_unique(fbb); - cmdBuilder->add_device_id(deviceId); - cmdBuilder->add_request_id(_cmd.GetRequestId()); - cmdBuilder->add_result(GetFBResult(_cmd.GetResult())); - } - break; - default: - throw CommandFormatError("unrecognized command type given to fair::mq::cmd::Cmds::Serialize()"); - break; - } - - cmdBuilder->add_command_id(typeToFBCmd.at(static_cast(cmd->GetType()))); - - cmdOffset = cmdBuilder->Finish(); - commandOffsets.push_back(cmdOffset); - } - - auto commands = fbb.CreateVector(commandOffsets); - auto cmds = CreateFBCommands(fbb, commands); - fbb.Finish(cmds); - - if (type == Format::Binary) { - return string(reinterpret_cast(fbb.GetBufferPointer()), fbb.GetSize()); - } else { // Type == Format::JSON - flatbuffers::Parser parser; - if (!parser.Parse(commandsFormatDefFbs)) { - throw CommandFormatError("Serialize couldn't parse commands format"); - } - std::string json; - if (!flatbuffers::GenerateText(parser, fbb.GetBufferPointer(), &json)) { - throw CommandFormatError("Serialize couldn't serialize parsed data to JSON!"); - } - return json; - } -} - -void Cmds::Deserialize(const string& str, const Format type) -{ - fCmds.clear(); - - const flatbuffers::Vector>* cmds = nullptr; - - flatbuffers::Parser parser; - if (type == Format::Binary) { - cmds = cmd::GetFBCommands(const_cast(str.c_str()))->commands(); - } else { // Type == Format::JSON - if (!parser.Parse(commandsFormatDefFbs)) { - throw CommandFormatError("Deserialize couldn't parse commands format"); - } - if (!parser.Parse(str.c_str())) { - throw CommandFormatError("Deserialize couldn't parse incoming JSON string"); - } - cmds = cmd::GetFBCommands(parser.builder_.GetBufferPointer())->commands(); - } - - for (unsigned int i = 0; i < cmds->size(); ++i) { - const fair::mq::sdk::cmd::FBCommand& cmdPtr = *(cmds->Get(i)); - switch (cmdPtr.command_id()) { - case FBCmd_check_state: - fCmds.emplace_back(make()); - break; - case FBCmd_change_state: - fCmds.emplace_back(make(GetMQTransition(cmdPtr.transition()))); - break; - case FBCmd_dump_config: - fCmds.emplace_back(make()); - break; - case FBCmd_subscribe_to_state_change: - fCmds.emplace_back(make(cmdPtr.interval())); - break; - case FBCmd_unsubscribe_from_state_change: - fCmds.emplace_back(make()); - break; - case FBCmd_state_change_exiting_received: - fCmds.emplace_back(make()); - break; - case FBCmd_get_properties: - fCmds.emplace_back(make(cmdPtr.request_id(), cmdPtr.property_query()->str())); - break; - case FBCmd_set_properties: { - std::vector> properties; - auto props = cmdPtr.properties(); - for (unsigned int j = 0; j < props->size(); ++j) { - properties.emplace_back(props->Get(j)->key()->str(), props->Get(j)->value()->str()); - } - fCmds.emplace_back(make(cmdPtr.request_id(), properties)); - } break; - case FBCmd_subscription_heartbeat: - fCmds.emplace_back(make(cmdPtr.interval())); - break; - case FBCmd_current_state: - fCmds.emplace_back(make(cmdPtr.device_id()->str(), GetMQState(cmdPtr.current_state()))); - break; - case FBCmd_transition_status: - fCmds.emplace_back(make(cmdPtr.device_id()->str(), cmdPtr.task_id(), GetResult(cmdPtr.result()), GetMQTransition(cmdPtr.transition()), GetMQState(cmdPtr.current_state()))); - break; - case FBCmd_config: - fCmds.emplace_back(make(cmdPtr.device_id()->str(), cmdPtr.config_string()->str())); - break; - case FBCmd_state_change_subscription: - fCmds.emplace_back(make(cmdPtr.device_id()->str(), cmdPtr.task_id(), GetResult(cmdPtr.result()))); - break; - case FBCmd_state_change_unsubscription: - fCmds.emplace_back(make(cmdPtr.device_id()->str(), cmdPtr.task_id(), GetResult(cmdPtr.result()))); - break; - case FBCmd_state_change: - fCmds.emplace_back(make(cmdPtr.device_id()->str(), cmdPtr.task_id(), GetMQState(cmdPtr.last_state()), GetMQState(cmdPtr.current_state()))); - break; - case FBCmd_properties: { - std::vector> properties; - auto props = cmdPtr.properties(); - for (unsigned int j = 0; j < props->size(); ++j) { - properties.emplace_back(props->Get(j)->key()->str(), props->Get(j)->value()->str()); - } - fCmds.emplace_back(make(cmdPtr.device_id()->str(), cmdPtr.request_id(), GetResult(cmdPtr.result()), properties)); - } break; - case FBCmd_properties_set: - fCmds.emplace_back(make(cmdPtr.device_id()->str(), cmdPtr.request_id(), GetResult(cmdPtr.result()))); - break; - default: - throw CommandFormatError("unrecognized command type given to fair::mq::cmd::Cmds::Deserialize()"); - break; - } - } -} - -} // namespace fair::mq::sdk::cmd diff --git a/fairmq/sdk/commands/Commands.h b/fairmq/sdk/commands/Commands.h deleted file mode 100644 index 48785f85..00000000 --- a/fairmq/sdk/commands/Commands.h +++ /dev/null @@ -1,414 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_SDK_COMMANDFACTORY -#define FAIR_MQ_SDK_COMMANDFACTORY - -#include - -#include -#include -#include -#include -#include -#include // move - -namespace fair::mq::sdk::cmd -{ - -enum class Format : int { - Binary, - JSON -}; - -enum class Result : int { - Ok, - Failure -}; - -enum class Type : int -{ - check_state, // args: { } - change_state, // args: { transition } - dump_config, // args: { } - subscribe_to_state_change, // args: { } - unsubscribe_from_state_change, // args: { } - state_change_exiting_received, // args: { } - get_properties, // args: { request_id, property_query } - set_properties, // args: { request_id, properties } - subscription_heartbeat, // args: { interval } - - current_state, // args: { device_id, current_state } - transition_status, // args: { device_id, task_id, Result, transition, current_state } - config, // args: { device_id, config_string } - state_change_subscription, // args: { device_id, task_id, Result } - state_change_unsubscription, // args: { device_id, task_id, Result } - state_change, // args: { device_id, task_id, last_state, current_state } - properties, // args: { device_id, request_id, Result, properties } - properties_set // args: { device_id, request_id, Result } -}; - -struct Cmd -{ - explicit Cmd(const Type type) : fType(type) {} - virtual ~Cmd() = default; - - Type GetType() const { return fType; } - - private: - Type fType; -}; - -struct CheckState : Cmd -{ - explicit CheckState() : Cmd(Type::check_state) {} -}; - -struct ChangeState : Cmd -{ - explicit ChangeState(Transition transition) - : Cmd(Type::change_state) - , fTransition(transition) - {} - - Transition GetTransition() const { return fTransition; } - void SetTransition(Transition transition) { fTransition = transition; } - - private: - Transition fTransition; -}; - -struct DumpConfig : Cmd -{ - explicit DumpConfig() : Cmd(Type::dump_config) {} -}; - -struct SubscribeToStateChange : Cmd -{ - explicit SubscribeToStateChange(int64_t interval) - : Cmd(Type::subscribe_to_state_change) - , fInterval(interval) - {} - - int64_t GetInterval() const { return fInterval; } - void SetInterval(int64_t interval) { fInterval = interval; } - - private: - int64_t fInterval; -}; - -struct UnsubscribeFromStateChange : Cmd -{ - explicit UnsubscribeFromStateChange() : Cmd(Type::unsubscribe_from_state_change) {} -}; - -struct StateChangeExitingReceived : Cmd -{ - explicit StateChangeExitingReceived() : Cmd(Type::state_change_exiting_received) {} -}; - -struct GetProperties : Cmd -{ - GetProperties(std::size_t request_id, std::string query) - : Cmd(Type::get_properties) - , fRequestId(request_id) - , fQuery(std::move(query)) - {} - - auto GetRequestId() const -> std::size_t { return fRequestId; } - auto SetRequestId(std::size_t requestId) -> void { fRequestId = requestId; } - auto GetQuery() const -> std::string { return fQuery; } - auto SetQuery(std::string query) -> void { fQuery = std::move(query); } - - private: - std::size_t fRequestId; - std::string fQuery; -}; - -struct SetProperties : Cmd -{ - SetProperties(std::size_t request_id, std::vector> properties) - : Cmd(Type::set_properties) - , fRequestId(request_id) - , fProperties(std::move(properties)) - {} - - auto GetRequestId() const -> std::size_t { return fRequestId; } - auto SetRequestId(std::size_t requestId) -> void { fRequestId = requestId; } - auto GetProps() const -> std::vector> { return fProperties; } - auto SetProps(std::vector> properties) -> void { fProperties = std::move(properties); } - - private: - std::size_t fRequestId; - std::vector> fProperties; -}; - -struct SubscriptionHeartbeat : Cmd -{ - explicit SubscriptionHeartbeat(int64_t interval) - : Cmd(Type::subscription_heartbeat) - , fInterval(interval) - {} - - int64_t GetInterval() const { return fInterval; } - void SetInterval(int64_t interval) { fInterval = interval; } - - private: - int64_t fInterval; -}; - -struct CurrentState : Cmd -{ - explicit CurrentState(std::string id, State currentState) - : Cmd(Type::current_state) - , fDeviceId(std::move(id)) - , fCurrentState(currentState) - {} - - std::string GetDeviceId() const { return fDeviceId; } - void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } - fair::mq::State GetCurrentState() const { return fCurrentState; } - void SetCurrentState(fair::mq::State state) { fCurrentState = state; } - - private: - std::string fDeviceId; - fair::mq::State fCurrentState; -}; - -struct TransitionStatus : Cmd -{ - explicit TransitionStatus(std::string deviceId, const uint64_t taskId, const Result result, const Transition transition, State currentState) - : Cmd(Type::transition_status) - , fDeviceId(std::move(deviceId)) - , fTaskId(taskId) - , fResult(result) - , fTransition(transition) - , fCurrentState(currentState) - {} - - std::string GetDeviceId() const { return fDeviceId; } - void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } - uint64_t GetTaskId() const { return fTaskId; } - void SetTaskId(const uint64_t taskId) { fTaskId = taskId; } - Result GetResult() const { return fResult; } - void SetResult(const Result result) { fResult = result; } - Transition GetTransition() const { return fTransition; } - void SetTransition(const Transition transition) { fTransition = transition; } - fair::mq::State GetCurrentState() const { return fCurrentState; } - void SetCurrentState(fair::mq::State state) { fCurrentState = state; } - - private: - std::string fDeviceId; - uint64_t fTaskId; - Result fResult; - Transition fTransition; - fair::mq::State fCurrentState; -}; - -struct Config : Cmd -{ - explicit Config(std::string id, std::string config) - : Cmd(Type::config) - , fDeviceId(std::move(id)) - , fConfig(std::move(config)) - {} - - std::string GetDeviceId() const { return fDeviceId; } - void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } - std::string GetConfig() const { return fConfig; } - void SetConfig(const std::string& config) { fConfig = config; } - - private: - std::string fDeviceId; - std::string fConfig; -}; - -struct StateChangeSubscription : Cmd -{ - explicit StateChangeSubscription(std::string id, const uint64_t taskId, const Result result) - : Cmd(Type::state_change_subscription) - , fDeviceId(std::move(id)) - , fTaskId(taskId) - , fResult(result) - {} - - std::string GetDeviceId() const { return fDeviceId; } - void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } - uint64_t GetTaskId() const { return fTaskId; } - void SetTaskId(const uint64_t taskId) { fTaskId = taskId; } - Result GetResult() const { return fResult; } - void SetResult(const Result result) { fResult = result; } - - private: - std::string fDeviceId; - uint64_t fTaskId; - Result fResult; -}; - -struct StateChangeUnsubscription : Cmd -{ - explicit StateChangeUnsubscription(std::string id, const uint64_t taskId, const Result result) - : Cmd(Type::state_change_unsubscription) - , fDeviceId(std::move(id)) - , fTaskId(taskId) - , fResult(result) - {} - - std::string GetDeviceId() const { return fDeviceId; } - void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } - uint64_t GetTaskId() const { return fTaskId; } - void SetTaskId(const uint64_t taskId) { fTaskId = taskId; } - Result GetResult() const { return fResult; } - void SetResult(const Result result) { fResult = result; } - - private: - std::string fDeviceId; - uint64_t fTaskId; - Result fResult; -}; - -struct StateChange : Cmd -{ - explicit StateChange(std::string deviceId, const uint64_t taskId, const State lastState, const State currentState) - : Cmd(Type::state_change) - , fDeviceId(std::move(deviceId)) - , fTaskId(taskId) - , fLastState(lastState) - , fCurrentState(currentState) - {} - - std::string GetDeviceId() const { return fDeviceId; } - void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } - uint64_t GetTaskId() const { return fTaskId; } - void SetTaskId(const uint64_t taskId) { fTaskId = taskId; } - fair::mq::State GetLastState() const { return fLastState; } - void SetLastState(const fair::mq::State state) { fLastState = state; } - fair::mq::State GetCurrentState() const { return fCurrentState; } - void SetCurrentState(const fair::mq::State state) { fCurrentState = state; } - - private: - std::string fDeviceId; - uint64_t fTaskId; - fair::mq::State fLastState; - fair::mq::State fCurrentState; -}; - -struct Properties : Cmd -{ - Properties(std::string deviceId, std::size_t requestId, const Result result, std::vector> properties) - : Cmd(Type::properties) - , fDeviceId(std::move(deviceId)) - , fRequestId(requestId) - , fResult(result) - , fProperties(std::move(properties)) - {} - - auto GetDeviceId() const -> std::string { return fDeviceId; } - auto SetDeviceId(std::string deviceId) -> void { fDeviceId = std::move(deviceId); } - auto GetRequestId() const -> std::size_t { return fRequestId; } - auto SetRequestId(std::size_t requestId) -> void { fRequestId = requestId; } - auto GetResult() const -> Result { return fResult; } - auto SetResult(Result result) -> void { fResult = result; } - auto GetProps() const -> std::vector> { return fProperties; } - auto SetProps(std::vector> properties) -> void { fProperties = std::move(properties); } - - private: - std::string fDeviceId; - std::size_t fRequestId; - Result fResult; - std::vector> fProperties; -}; - -struct PropertiesSet : Cmd { - PropertiesSet(std::string deviceId, std::size_t requestId, Result result) - : Cmd(Type::properties_set) - , fDeviceId(std::move(deviceId)) - , fRequestId(requestId) - , fResult(result) - {} - - auto GetDeviceId() const -> std::string { return fDeviceId; } - auto SetDeviceId(std::string deviceId) -> void { fDeviceId = std::move(deviceId); } - auto GetRequestId() const -> std::size_t { return fRequestId; } - auto SetRequestId(std::size_t requestId) -> void { fRequestId = requestId; } - auto GetResult() const -> Result { return fResult; } - auto SetResult(Result result) -> void { fResult = result; } - - private: - std::string fDeviceId; - std::size_t fRequestId; - Result fResult; -}; - -template -std::unique_ptr make(Args&&... args) -{ - return std::make_unique(std::forward(args)...); -} - -struct Cmds -{ - using container = std::vector>; - struct CommandFormatError : std::runtime_error { using std::runtime_error::runtime_error; }; - - explicit Cmds() = default; - - template - explicit Cmds(std::unique_ptr&& first, Rest&&... rest) - { - Unpack(std::forward&&>(first), std::forward(rest)...); - } - - void Add(std::unique_ptr&& cmd) { fCmds.emplace_back(std::move(cmd)); } - - template - void Add(Args&&... args) - { - static_assert(std::is_base_of::value, "Only types derived from fair::mq::cmd::Cmd are allowed"); - Add(make(std::forward(args)...)); - } - - Cmd& At(size_t i) { return *(fCmds.at(i)); } - - size_t Size() const { return fCmds.size(); } - void Reset() { fCmds.clear(); } - - std::string Serialize(const Format type = Format::Binary) const; - void Deserialize(const std::string&, const Format type = Format::Binary); - - private: - container fCmds; - - void Unpack() {} - - template - void Unpack(std::unique_ptr&& first, Rest&&... rest) - { - fCmds.emplace_back(std::move(first)); - Unpack(std::forward(rest)...); - } - - public: - using iterator = container::iterator; - using const_iterator = container::const_iterator; - - auto begin() -> decltype(fCmds.begin()) { return fCmds.begin(); } - auto end() -> decltype(fCmds.end()) { return fCmds.end(); } - auto cbegin() -> decltype(fCmds.cbegin()) { return fCmds.cbegin(); } - auto cend() -> decltype(fCmds.cend()) { return fCmds.cend(); } -}; - -std::string GetResultName(const Result result); -std::string GetTypeName(const Type type); - -inline std::ostream& operator<<(std::ostream& os, const Result& result) { return os << GetResultName(result); } -inline std::ostream& operator<<(std::ostream& os, const Type& type) { return os << GetTypeName(type); } - -} // namespace fair::mq::sdk::cmd - -#endif /* FAIR_MQ_SDK_COMMANDFACTORY */ diff --git a/fairmq/sdk/commands/CommandsFormat.fbs b/fairmq/sdk/commands/CommandsFormat.fbs deleted file mode 100644 index 59fe752b..00000000 --- a/fairmq/sdk/commands/CommandsFormat.fbs +++ /dev/null @@ -1,89 +0,0 @@ -namespace fair.mq.sdk.cmd; - -enum FBResult:byte { - Ok, - Failure -} - -enum FBState:byte { - Undefined, - Ok, - Error, - Idle, - InitializingDevice, - Initialized, - Binding, - Bound, - Connecting, - DeviceReady, - InitializingTask, - Ready, - Running, - ResettingTask, - ResettingDevice, - Exiting -} - -enum FBTransition:byte { - Auto, - InitDevice, - CompleteInit, - Bind, - Connect, - InitTask, - Run, - Stop, - ResetTask, - ResetDevice, - End, - ErrorFound -} - -table FBProperty { - key:string; - value:string; -} - -enum FBCmd:byte { - check_state, // args: { } - change_state, // args: { transition } - dump_config, // args: { } - subscribe_to_state_change, // args: { interval } - unsubscribe_from_state_change, // args: { } - state_change_exiting_received, // args: { } - get_properties, // args: { request_id, property_query } - set_properties, // args: { request_id, properties } - subscription_heartbeat, // args: { interval } - - current_state, // args: { device_id, current_state } - transition_status, // args: { device_id, task_id, Result, transition, current_state } - config, // args: { device_id, config_string } - state_change_subscription, // args: { device_id, task_id, Result } - state_change_unsubscription, // args: { device_id, task_id, Result } - state_change, // args: { device_id, task_id, last_state, current_state } - properties, // args: { device_id, request_id, Result, properties } - properties_set // args: { device_id, request_id, Result } -} - -table FBCommand { - command_id:FBCmd; - device_id:string; - task_id:uint64; - request_id:uint64; - interval:int64; - state:FBState; - transition:FBTransition; - result:FBResult; - config_string:string; - last_state:FBState; - current_state:FBState; - debug:string; - properties:[FBProperty]; - property_query:string; -} - -table FBCommands { - commands:[FBCommand]; -} - -root_type FBCommands; diff --git a/fairmq/sdk/commands/CommandsFormatDef.h.in b/fairmq/sdk/commands/CommandsFormatDef.h.in deleted file mode 100644 index cfadaf63..00000000 --- a/fairmq/sdk/commands/CommandsFormatDef.h.in +++ /dev/null @@ -1,18 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include - -namespace fair::mq::sdk -{ -namespace cmd { - -constexpr auto commandsFormatDefFbs = R"(@commands_format_def_fbs@)"; - -} // namespace cmd -} // namespace fair::mq::sdk diff --git a/fairmq/sdk/runDDSCommandUI.cxx b/fairmq/sdk/runDDSCommandUI.cxx deleted file mode 100644 index 006e2387..00000000 --- a/fairmq/sdk/runDDSCommandUI.cxx +++ /dev/null @@ -1,256 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2014-2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include -#include -#include -#include - -#include - -#include // raw mode console input -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace fair::mq; -using namespace fair::mq::sdk; -using namespace fair::mq::sdk::cmd; -namespace bpo = boost::program_options; - -struct TerminalConfig -{ - explicit TerminalConfig() - { - termios t; - tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure - t.c_lflag &= ~ICANON; // disable canonical input - // t.c_lflag &= ~ECHO; // do not echo input chars - tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings - } - - ~TerminalConfig() - { - termios t; - tcgetattr(STDIN_FILENO, &t); // get the current terminal I/O structure - t.c_lflag |= ICANON; // re-enable canonical input - // t.c_lflag |= ECHO; // echo input chars - tcsetattr(STDIN_FILENO, TCSANOW, &t); // apply the new settings - } -}; - -void printControlsHelp() -{ - cout << "Use keys to control the devices:" << endl; - cout << "[c] check states, [o] dump config, [h] help, [r] run, [s] stop, [t] reset task, [d] reset device, [q] end, [j] init task, [i] init device, [k] complete init, [b] bind, [x] connect, [p] set property" << endl; - cout << "To quit press Ctrl+C" << endl; -} - -void handleCommand(const string& command, const string& path, unsigned int timeout, Topology& topo, const string& pKey, const string& pVal) -{ - std::pair changeStateResult; - - if (command == "c") { - cout << "> checking state of the devices" << endl; - auto const result = topo.GetCurrentState(); - bool error = false; - for (const auto& d : result) { - cout << d.taskId << " : " << d.state << endl; - if (d.state == sdk::DeviceState::Error) { - error = true; - } - } - if (error) { - throw runtime_error("Some of the devices are in the Error state"); - } - return; - } else if (command == "o") { - cout << "> dumping config of " << (path.empty() ? "all" : path) << endl; - // TODO: extend this regex to return all properties, once command size limitation is removed. - auto const result = topo.GetProperties("^(session|id)$", path, std::chrono::milliseconds(timeout)); - if (result.first != std::error_code()) { - cout << "ERROR: GetProperties failed for '" << path << "': " << result.first.message() << endl; - throw runtime_error(tools::ToString("GetProperties failed for '", path, "': ", result.first.message())); - } - for (const auto& d : result.second.devices) { - for (auto const& p : d.second.props) { - cout << d.first << ": " << p.first << " : " << p.second << endl; - } - } - return; - } else if (command == "p") { - if (pKey.empty() || pVal.empty()) { - cout << "cannot send property with empty key and/or value! given key: '" << pKey << "', value: '" << pVal << "'." << endl; - throw runtime_error(tools::ToString("cannot send property with empty key and/or value! given key: '", pKey, "', value: '", pVal, "'.")); - } - const DeviceProperties props{{pKey, pVal}}; - cout << "> setting properties --> " << (path.empty() ? "all" : path) << endl; - auto const result = topo.SetProperties(props, path); - if (result.first != std::error_code()) { - cout << "ERROR: SetProperties failed for '" << path << "': " << result.first.message() << endl; - throw runtime_error(tools::ToString("SetProperties failed for '", path, "': ", result.first.message())); - } - // give dds time to complete request - this_thread::sleep_for(chrono::milliseconds(100)); - return; - } else if (command == "i") { - cout << "> initiating InitDevice transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::InitDevice, path, std::chrono::milliseconds(timeout)); - } else if (command == "k") { - cout << "> initiating CompleteInit transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::CompleteInit, path, std::chrono::milliseconds(timeout)); - } else if (command == "b") { - cout << "> initiating Bind transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::Bind, path, std::chrono::milliseconds(timeout)); - } else if (command == "x") { - cout << "> initiating Connect transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::Connect, path, std::chrono::milliseconds(timeout)); - } else if (command == "j") { - cout << "> initiating InitTask transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::InitTask, path, std::chrono::milliseconds(timeout)); - } else if (command == "r") { - cout << "> initiating Run transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::Run, path, std::chrono::milliseconds(timeout)); - } else if (command == "s") { - cout << "> initiating Stop transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::Stop, path, std::chrono::milliseconds(timeout)); - } else if (command == "t") { - cout << "> initiating ResetTask transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::ResetTask, path, std::chrono::milliseconds(timeout)); - } else if (command == "d") { - cout << "> initiating ResetDevice transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::ResetDevice, path, std::chrono::milliseconds(timeout)); - } else if (command == "q") { - cout << "> initiating End transition --> " << (path.empty() ? "all" : path) << endl; - changeStateResult = topo.ChangeState(TopologyTransition::End, path, std::chrono::milliseconds(timeout)); - } else if (command == "h") { - cout << "> help" << endl; - printControlsHelp(); - return; - } else { - cout << "\033[01;32mInvalid input: [" << command << "]\033[0m" << endl; - printControlsHelp(); - throw runtime_error(tools::ToString("\033[01;32mInvalid input: [", command, "]\033[0m")); - } - if (changeStateResult.first != std::error_code()) { - cout << "ERROR: ChangeState failed for '" << path << "': " << changeStateResult.first.message() << endl; - throw runtime_error(tools::ToString("ERROR: ChangeState failed for '", path, "': ", changeStateResult.first.message())); - } -} - -void sendCommand(const string& commandIn, const string& path, unsigned int timeout, Topology& topo, const string& pKey, const string& pVal) -{ - if (!commandIn.empty()) { - handleCommand(commandIn, path, timeout, topo, pKey, pVal); - return; - } - - char c = 0; - string command; - TerminalConfig tconfig; - - printControlsHelp(); - cin >> c; - command = c; - - while (true) { - try { - handleCommand(command, path, timeout, topo, pKey, pVal); - } catch(exception& e) { - cout << "Error: " << e.what() << endl; - } - cin >> c; - command = c; - } -} - -int main(int argc, char* argv[]) -try { - string sessionID; - string topoFile; - - string command; - string path; - string targetState; - string pKey; - string pVal; - unsigned int timeout = 0; - - fair::Logger::SetConsoleSeverity("debug"); - fair::Logger::SetConsoleColor(true); - - bpo::options_description options("Common options"); - - auto envSessionId = getenv("DDS_SESSION_ID"); - if (envSessionId) { - options.add_options()("session,s", bpo::value(&sessionID)->default_value(envSessionId), "DDS Session ID (overrides any value in env var $DDS_SESSION_ID)"); - } else { - options.add_options()("session,s", bpo::value(&sessionID)->required(), "DDS Session ID (overrides any value in env var $DDS_SESSION_ID)"); - } - - auto envTopoFile = getenv("FAIRMQ_DDS_TOPO_FILE"); - if (envTopoFile) { - options.add_options()("topology-file,f", bpo::value(&topoFile)->default_value(envTopoFile), "DDS topology file path"); - } else { - options.add_options()("topology-file,f", bpo::value(&topoFile)->required(), "DDS topology file path"); - } - - options.add_options() - ("command,c", bpo::value(&command)->default_value(""), "Command character") - ("path,p", bpo::value(&path)->default_value(""), "DDS Topology path to send command to (empty - send to all tasks)") - ("property-key", bpo::value(&pKey)->default_value(""), "property key to be used with 'p' command") - ("property-value", bpo::value(&pVal)->default_value(""), "property value to be used with 'p' command") - ("wait-for-state,w", bpo::value(&targetState)->default_value(""), "Wait until targeted FairMQ devices reach the given state") - ("timeout,t", bpo::value(&timeout)->default_value(0), "Timeout in milliseconds when waiting for a device state (0 - wait infinitely)") - ("help,h", "Produce help message"); - - bpo::variables_map vm; - bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); - - if (vm.count("help")) { - cout << "FairMQ DDS Command UI" << endl << options << endl; - cout << "Commands: [c] check state, [o] dump config, [h] help, [r] run, [s] stop, [t] reset task, [d] reset device, [q] end, [j] init task, [i] init device, [k] complete init, [b] bind, [x] connect, [p] set property" << endl; - return EXIT_SUCCESS; - } - - bpo::notify(vm); - - DDSEnvironment env; - DDSSession session(sessionID, env); - DDSTopology ddsTopo(DDSTopology::Path(topoFile), env); - - Topology topo(ddsTopo, session, true); - - if (!targetState.empty()) { - if (!command.empty()) { - sendCommand(command, path, timeout, topo, pKey, pVal); - } - size_t pos = targetState.find("->"); - cout << "> waiting for " << (path.empty() ? "all" : path) << " to reach " << targetState << endl; - if (pos == string::npos) { - /* auto ec = */topo.WaitForState(GetState(targetState), path, std::chrono::milliseconds(timeout)); - // cout << "WaitForState(" << targetState << ") result: " << ec.message() << endl; - } else { - /* auto ec = */topo.WaitForState(GetState(targetState.substr(0, pos)), GetState(targetState.substr(pos + 2)), path, std::chrono::milliseconds(timeout)); - // cout << "WaitForState(" << targetState << ") result: " << ec.message() << endl; - } - } else { - sendCommand(command, path, timeout, topo, pKey, pVal); - } - - return EXIT_SUCCESS; -} catch (exception& e) { - cerr << "Error: " << e.what() << endl; - return EXIT_FAILURE; -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2d65e2a8..89dc9f90 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,5 @@ ################################################################################ -# Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # +# Copyright (C) 2014-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # # # This software is distributed under the terms of the # # GNU Lesser General Public Licence (LGPL) version 3, # @@ -292,56 +292,3 @@ add_testsuite(MemoryResources TIMEOUT 5 ${definitions} ) - -if(BUILD_SDK) - # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sdk/test_topo.xml - # ${CMAKE_BINARY_DIR}/test_topo.xml) - # add_testsuite(SDK - # SOURCES - # ${CMAKE_CURRENT_BINARY_DIR}/runner.cxx - # sdk/_async_op.cxx - # sdk/_dds.cxx - # sdk/_topology.cxx - # sdk/Fixtures.h -# - # LINKS - # SDK - # Tools - # DDS::dds_topology_lib - # DDS::dds_tools_lib - # INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} - # ${CMAKE_CURRENT_BINARY_DIR} - # TIMEOUT 30 - # ${definitions} - # ) - - if(DDS_TESTS) - foreach(i RANGE 1 ${DDS_TESTS}) - add_test(NAME DDSToolsAPIStabilityTest_${i} - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/testsuite_SDK --gtest_filter=TopologyHelper.MakeTopology --gtest_also_run_disabled_tests - ) - set_tests_properties(DDSToolsAPIStabilityTest_${i} PROPERTIES TIMEOUT 10) - endforeach() - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/DDSToolsAPIStabilityTest.cmake.in - ${CMAKE_BINARY_DIR}/DDSToolsAPIStabilityTest.cmake - @ONLY - ) - endif() -endif() - -if(BUILD_SDK_COMMANDS) - add_testsuite(Commands - SOURCES - ${CMAKE_CURRENT_BINARY_DIR}/runner.cxx - commands/_commands.cxx - - LINKS - Commands - StateMachine - Tools - INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - TIMEOUT 30 - ${definitions} - ) -endif() diff --git a/test/DDSToolsAPIStabilityTest.cmake.in b/test/DDSToolsAPIStabilityTest.cmake.in deleted file mode 100644 index 468b8b2e..00000000 --- a/test/DDSToolsAPIStabilityTest.cmake.in +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # -# # -# This software is distributed under the terms of the # -# GNU Lesser General Public Licence (LGPL) version 3, # -# copied verbatim in the file "LICENSE" # -################################################################################ - -include(@CMAKE_SOURCE_DIR@/CTestConfig.cmake) - -cmake_host_system_information(RESULT fqdn QUERY FQDN) -set(CTEST_SITE ${fqdn}) -set(CTEST_BUILD_NAME "@CMAKE_SYSTEM@ - @CMAKE_CXX_COMPILER_ID@ @CMAKE_CXX_COMPILER_VERSION@ - DDS Stability Test (@DDS_TESTS@ iterations, DDS: @DDS_VERSION@, FairMQ: @PROJECT_GIT_VERSION@, Boost: @Boost_VERSION@)") -set(CTEST_SOURCE_DIRECTORY @CMAKE_SOURCE_DIR@) -set(CTEST_BINARY_DIRECTORY @CMAKE_BINARY_DIR@) -file(REMOVE_RECURSE ${CTEST_BINARY_DIRECTORY}/test/.DDS) - -ctest_start(Experimental) -ctest_test(INCLUDE "DDSToolsAPIStabilityTest") -ctest_submit() - -set(dds_logs @CMAKE_BINARY_DIR@/dds_logs.tar.gz) -execute_process(COMMAND ${CMAKE_COMMAND} -E tar "cfvz" "${dds_logs}" "@CMAKE_BINARY_DIR@/test/.DDS" OUTPUT_QUIET) -message("DDS logs packed: ${dds_logs}") diff --git a/test/commands/_commands.cxx b/test/commands/_commands.cxx deleted file mode 100644 index d762ec23..00000000 --- a/test/commands/_commands.cxx +++ /dev/null @@ -1,241 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include -#include - -#include - -namespace -{ - -using namespace fair::mq; -using namespace fair::mq::sdk::cmd; - -TEST(Format, Construction) -{ - auto const props(std::vector>({{"k1", "v1"}, {"k2", "v2"}})); - - Cmds checkStateCmds(make()); - Cmds changeStateCmds(make(Transition::Stop)); - Cmds dumpConfigCmds(make()); - Cmds subscribeToStateChangeCmds(make(60000)); - Cmds unsubscribeFromStateChangeCmds(make()); - Cmds stateChangeExitingReceivedCmds(make()); - Cmds getPropertiesCmds(make(66, "k[12]")); - Cmds setPropertiesCmds(make(42, props)); - Cmds subscriptionHeartbeatCmds(make(60000)); - Cmds currentStateCmds(make("somedeviceid", State::Running)); - Cmds transitionStatusCmds(make("somedeviceid", 123456, Result::Ok, Transition::Stop, State::Running)); - Cmds configCmds(make("somedeviceid", "someconfig")); - Cmds stateChangeSubscriptionCmds(make("somedeviceid", 123456, Result::Ok)); - Cmds stateChangeUnsubscriptionCmds(make("somedeviceid", 123456, Result::Ok)); - Cmds stateChangeCmds(make("somedeviceid", 123456, State::Running, State::Ready)); - Cmds propertiesCmds(make("somedeviceid", 66, Result::Ok, props)); - Cmds propertiesSetCmds(make("somedeviceid", 42, Result::Ok)); - - ASSERT_EQ(checkStateCmds.At(0).GetType(), Type::check_state); - ASSERT_EQ(changeStateCmds.At(0).GetType(), Type::change_state); - ASSERT_EQ(static_cast(changeStateCmds.At(0)).GetTransition(), Transition::Stop); - ASSERT_EQ(dumpConfigCmds.At(0).GetType(), Type::dump_config); - ASSERT_EQ(subscribeToStateChangeCmds.At(0).GetType(), Type::subscribe_to_state_change); - ASSERT_EQ(static_cast(subscribeToStateChangeCmds.At(0)).GetInterval(), 60000); - ASSERT_EQ(unsubscribeFromStateChangeCmds.At(0).GetType(), Type::unsubscribe_from_state_change); - ASSERT_EQ(stateChangeExitingReceivedCmds.At(0).GetType(), Type::state_change_exiting_received); - ASSERT_EQ(getPropertiesCmds.At(0).GetType(), Type::get_properties); - ASSERT_EQ(static_cast(getPropertiesCmds.At(0)).GetRequestId(), 66); - ASSERT_EQ(static_cast(getPropertiesCmds.At(0)).GetQuery(), "k[12]"); - ASSERT_EQ(setPropertiesCmds.At(0).GetType(), Type::set_properties); - ASSERT_EQ(static_cast(setPropertiesCmds.At(0)).GetRequestId(), 42); - ASSERT_EQ(static_cast(setPropertiesCmds.At(0)).GetProps(), props); - ASSERT_EQ(subscriptionHeartbeatCmds.At(0).GetType(), Type::subscription_heartbeat); - ASSERT_EQ(static_cast(subscriptionHeartbeatCmds.At(0)).GetInterval(), 60000); - ASSERT_EQ(currentStateCmds.At(0).GetType(), Type::current_state); - ASSERT_EQ(static_cast(currentStateCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(currentStateCmds.At(0)).GetCurrentState(), State::Running); - ASSERT_EQ(transitionStatusCmds.At(0).GetType(), Type::transition_status); - ASSERT_EQ(static_cast(transitionStatusCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(transitionStatusCmds.At(0)).GetTaskId(), 123456); - ASSERT_EQ(static_cast(transitionStatusCmds.At(0)).GetResult(), Result::Ok); - ASSERT_EQ(static_cast(transitionStatusCmds.At(0)).GetTransition(), Transition::Stop); - ASSERT_EQ(static_cast(transitionStatusCmds.At(0)).GetCurrentState(), State::Running); - ASSERT_EQ(configCmds.At(0).GetType(), Type::config); - ASSERT_EQ(static_cast(configCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(configCmds.At(0)).GetConfig(), "someconfig"); - ASSERT_EQ(stateChangeSubscriptionCmds.At(0).GetType(), Type::state_change_subscription); - ASSERT_EQ(static_cast(stateChangeSubscriptionCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(stateChangeSubscriptionCmds.At(0)).GetTaskId(), 123456); - ASSERT_EQ(static_cast(stateChangeSubscriptionCmds.At(0)).GetResult(), Result::Ok); - ASSERT_EQ(stateChangeUnsubscriptionCmds.At(0).GetType(), Type::state_change_unsubscription); - ASSERT_EQ(static_cast(stateChangeUnsubscriptionCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(stateChangeUnsubscriptionCmds.At(0)).GetTaskId(), 123456); - ASSERT_EQ(static_cast(stateChangeUnsubscriptionCmds.At(0)).GetResult(), Result::Ok); - ASSERT_EQ(stateChangeCmds.At(0).GetType(), Type::state_change); - ASSERT_EQ(static_cast(stateChangeCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(stateChangeCmds.At(0)).GetTaskId(), 123456); - ASSERT_EQ(static_cast(stateChangeCmds.At(0)).GetLastState(), State::Running); - ASSERT_EQ(static_cast(stateChangeCmds.At(0)).GetCurrentState(), State::Ready); - ASSERT_EQ(propertiesCmds.At(0).GetType(), Type::properties); - ASSERT_EQ(static_cast(propertiesCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(propertiesCmds.At(0)).GetRequestId(), 66); - ASSERT_EQ(static_cast(propertiesCmds.At(0)).GetResult(), Result::Ok); - ASSERT_EQ(static_cast(propertiesCmds.At(0)).GetProps(), props); - ASSERT_EQ(propertiesSetCmds.At(0).GetType(), Type::properties_set); - ASSERT_EQ(static_cast(propertiesSetCmds.At(0)).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(propertiesSetCmds.At(0)).GetRequestId(), 42); - ASSERT_EQ(static_cast(propertiesSetCmds.At(0)).GetResult(), Result::Ok); -} - -void fillCommands(Cmds& cmds) -{ - auto const props(std::vector>({{"k1", "v1"}, {"k2", "v2"}})); - - cmds.Add(); - cmds.Add(Transition::Stop); - cmds.Add(); - cmds.Add(60000); - cmds.Add(); - cmds.Add(); - cmds.Add(66, "k[12]"); - cmds.Add(42, props); - cmds.Add(60000); - cmds.Add("somedeviceid", State::Running); - cmds.Add("somedeviceid", 123456, Result::Ok, Transition::Stop, State::Running); - cmds.Add("somedeviceid", "someconfig"); - cmds.Add("somedeviceid", 123456, Result::Ok); - cmds.Add("somedeviceid", 123456, Result::Ok); - cmds.Add("somedeviceid", 123456, State::Running, State::Ready); - cmds.Add("somedeviceid", 66, Result::Ok, props); - cmds.Add("somedeviceid", 42, Result::Ok); -} - -void checkCommands(Cmds& cmds) -{ - ASSERT_EQ(cmds.Size(), 17); - - int count = 0; - auto const props(std::vector>({{"k1", "v1"}, {"k2", "v2"}})); - - for (const auto& cmd : cmds) { - switch (cmd->GetType()) { - case Type::check_state: - ++count; - break; - case Type::change_state: - ++count; - ASSERT_EQ(static_cast(*cmd).GetTransition(), Transition::Stop); - break; - case Type::dump_config: - ++count; - break; - case Type::subscribe_to_state_change: - ++count; - ASSERT_EQ(static_cast(*cmd).GetInterval(), 60000); - break; - case Type::unsubscribe_from_state_change: - ++count; - break; - case Type::state_change_exiting_received: - ++count; - break; - case Type::get_properties: - ++count; - ASSERT_EQ(static_cast(*cmd).GetRequestId(), 66); - ASSERT_EQ(static_cast(*cmd).GetQuery(), "k[12]"); - break; - case Type::set_properties: - ++count; - ASSERT_EQ(static_cast(*cmd).GetRequestId(), 42); - ASSERT_EQ(static_cast(*cmd).GetProps(), props); - break; - case Type::subscription_heartbeat: - ++count; - ASSERT_EQ(static_cast(*cmd).GetInterval(), 60000); - break; - case Type::current_state: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetCurrentState(), State::Running); - break; - case Type::transition_status: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetTaskId(), 123456); - ASSERT_EQ(static_cast(*cmd).GetResult(), Result::Ok); - ASSERT_EQ(static_cast(*cmd).GetTransition(), Transition::Stop); - ASSERT_EQ(static_cast(*cmd).GetCurrentState(), State::Running); - break; - case Type::config: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetConfig(), "someconfig"); - break; - case Type::state_change_subscription: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetTaskId(), 123456); - ASSERT_EQ(static_cast(*cmd).GetResult(), Result::Ok); - break; - case Type::state_change_unsubscription: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetTaskId(), 123456); - ASSERT_EQ(static_cast(*cmd).GetResult(), Result::Ok); - break; - case Type::state_change: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetTaskId(), 123456); - ASSERT_EQ(static_cast(*cmd).GetLastState(), State::Running); - ASSERT_EQ(static_cast(*cmd).GetCurrentState(), State::Ready); - break; - case Type::properties: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetRequestId(), 66); - ASSERT_EQ(static_cast(*cmd).GetResult(), Result::Ok); - ASSERT_EQ(static_cast(*cmd).GetProps(), props); - break; - case Type::properties_set: - ++count; - ASSERT_EQ(static_cast(*cmd).GetDeviceId(), "somedeviceid"); - ASSERT_EQ(static_cast(*cmd).GetRequestId(), 42); - ASSERT_EQ(static_cast(*cmd).GetResult(), Result::Ok); - break; - default: - ASSERT_TRUE(false); - break; - } - } - - ASSERT_EQ(count, 17); -} - -TEST(Format, SerializationBinary) -{ - Cmds outCmds; - fillCommands(outCmds); - std::string buffer(outCmds.Serialize()); - - Cmds inCmds; - inCmds.Deserialize(buffer); - checkCommands(inCmds); -} - -TEST(Format, SerializationJSON) -{ - Cmds outCmds; - fillCommands(outCmds); - std::string buffer(outCmds.Serialize(Format::JSON)); - - Cmds inCmds; - inCmds.Deserialize(buffer, Format::JSON); - checkCommands(inCmds); -} - -} // namespace diff --git a/test/sdk/Fixtures.h b/test/sdk/Fixtures.h deleted file mode 100644 index 51407b20..00000000 --- a/test/sdk/Fixtures.h +++ /dev/null @@ -1,169 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#ifndef FAIR_MQ_TEST_FIXTURES -#define FAIR_MQ_TEST_FIXTURES - -#include "TestEnvironment.h" - -#include -#include - -#include - -#include -#include - -#include // for_each -#include -#include -#include -#include - -namespace fair::mq::test -{ - -struct LoggerConfig -{ - LoggerConfig() - { - Logger::SetConsoleSeverity("debug"); - Logger::DefineVerbosity("user1", - fair::VerbositySpec::Make(VerbositySpec::Info::timestamp_us, - VerbositySpec::Info::severity)); - Logger::SetVerbosity("user1"); - Logger::SetConsoleColor(); - - std::string path(std::getenv("PATH")); - path = tools::ToString(FAIRMQ_TEST_ENVIRONMENT, ":", path); - setenv("PATH", path.c_str(), 1); - } -}; - -struct TopologyFixture : ::testing::Test -{ - TopologyFixture() - : mDDSTopoFile(tools::ToString(SDK_TESTSUITE_SOURCE_DIR, "/test_topo.xml")) - , mDDSEnv(CMAKE_CURRENT_BINARY_DIR) - , mDDSSession(mDDSEnv) - , mDDSTopo(sdk::DDSTopology::Path(mDDSTopoFile), mDDSEnv) - { - mDDSSession.StopOnDestruction(); - } - - auto SetUp() -> void override { - LOG(info) << mDDSEnv; - LOG(info) << mDDSSession; - LOG(info) << mDDSTopo; - auto n(mDDSTopo.GetNumRequiredAgents()); - mDDSSession.SubmitAgents(n); - mDDSSession.ActivateTopology(mDDSTopo); - - std::vector agents = mDDSSession.RequestAgentInfo(); - LOG(debug) << "##### AgentInfo:"; - LOG(debug) << "size: " << agents.size(); - for (const auto& a : agents) { - LOG(debug) << a; - } - - std::vector tasks = mDDSSession.RequestTaskInfo(); - LOG(debug) << "##### TaskInfo:"; - LOG(debug) << "size: " << tasks.size(); - for (const auto& t : tasks) { - LOG(debug) << t; - } - - std::vector collections = mDDSTopo.GetCollections(); - LOG(debug) << "##### CollectionInfo:"; - LOG(debug) << "size: " << collections.size(); - for (const auto& c : collections) { - LOG(debug) << c; - } - } - - auto TearDown() -> void override {} - - LoggerConfig mLoggerConfig; - std::string mDDSTopoFile; - sdk::DDSEnvironment mDDSEnv; - sdk::DDSSession mDDSSession; - sdk::DDSTopology mDDSTopo; - asio::io_context mIoContext; -}; - -struct MultipleTopologiesFixture : ::testing::Test -{ - MultipleTopologiesFixture() - : mDDSTopoFile(tools::ToString(SDK_TESTSUITE_SOURCE_DIR, "/test_topo.xml")) - , mDDSEnv(CMAKE_CURRENT_BINARY_DIR) - , mDDSSessions{ sdk::DDSSession(mDDSEnv), - sdk::DDSSession(mDDSEnv) } - , mDDSTopologies{ sdk::DDSTopology(sdk::DDSTopology::Path(mDDSTopoFile), mDDSEnv), - sdk::DDSTopology(sdk::DDSTopology::Path(mDDSTopoFile), mDDSEnv) } - { - std::for_each(mDDSSessions.begin(), mDDSSessions.end(), [](sdk::DDSSession& s) { - s.StopOnDestruction(); - }); - } - - auto SetUp() -> void override - { - LOG(info) << mDDSEnv; - for (int i = 0; i < mNumSessions; ++i) { - LOG(info) << "##### SESSION " << i << " #####"; - LOG(info) << mDDSSessions[i]; - LOG(info) << mDDSTopologies[i]; - auto n(mDDSTopologies[i].GetNumRequiredAgents()); - mDDSSessions[i].SubmitAgents(n); - mDDSSessions[i].ActivateTopology(mDDSTopologies[i]); - - std::vector agents = mDDSSessions[i].RequestAgentInfo(); - LOG(info) << "##### AgentInfo:"; - LOG(info) << "size: " << agents.size(); - for (const auto& a : agents) { - LOG(info) << a; - } - - std::vector tasks = mDDSSessions[i].RequestTaskInfo(); - LOG(info) << "##### TaskInfo:"; - LOG(info) << "size: " << tasks.size(); - for (const auto& t : tasks) { - LOG(info) << t; - } - - std::vector collections = mDDSTopologies[i].GetCollections(); - LOG(info) << "##### CollectionInfo:"; - LOG(info) << "size: " << collections.size(); - for (const auto& c : collections) { - LOG(info) << c; - } - } - } - - auto TearDown() -> void override {} - - static constexpr int mNumSessions = 2; - LoggerConfig mLoggerConfig; - std::string mDDSTopoFile; - sdk::DDSEnvironment mDDSEnv; - std::array mDDSSessions; - std::array mDDSTopologies; -}; - -struct AsyncOpFixture : ::testing::Test -{ - auto SetUp() -> void override {} - auto TearDown() -> void override {} - - LoggerConfig mLoggerConfig; - asio::io_context mIoContext; -}; - -} // namespace fair::mq::test - -#endif /* FAIR_MQ_TEST_FIXTURES */ diff --git a/test/sdk/_async_op.cxx b/test/sdk/_async_op.cxx deleted file mode 100644 index 787495cb..00000000 --- a/test/sdk/_async_op.cxx +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "Fixtures.h" - -#include -#include -#include -#include -#include - -namespace { - -using AsyncOp = fair::mq::test::AsyncOpFixture; - -// template -// class : public AsioBase - -TEST_F(AsyncOp, DefaultConstruction) -{ - using namespace fair::mq::sdk; - - AsioAsyncOp op; - EXPECT_TRUE(op.IsCompleted()); -} - -TEST_F(AsyncOp, ConstructionWithHandler) -{ - using namespace fair::mq::sdk; - - AsioAsyncOp op( - [](std::error_code, int) {}); - EXPECT_FALSE(op.IsCompleted()); -} - -TEST_F(AsyncOp, Complete) -{ - using namespace fair::mq::sdk; - - AsioAsyncOp op( - [](std::error_code ec, int v) { - EXPECT_FALSE(ec); // success - EXPECT_EQ(v, 42); - }); - - EXPECT_FALSE(op.IsCompleted()); - op.Complete(42); - EXPECT_TRUE(op.IsCompleted()); - - EXPECT_THROW(op.Complete(6), RuntimeError); // No double completion! -} - -TEST_F(AsyncOp, Cancel) -{ - using namespace fair::mq; - - sdk::AsioAsyncOp op( - [](std::error_code ec) { - EXPECT_TRUE(ec); // error - EXPECT_EQ(ec, MakeErrorCode(ErrorCode::OperationCanceled)); - }); - - op.Cancel(); -} - -TEST_F(AsyncOp, Timeout) -{ - using namespace fair::mq; - - asio::steady_timer timer(mIoContext.get_executor(), std::chrono::milliseconds(50)); - sdk::AsioAsyncOp op( - mIoContext.get_executor(), - [&timer](std::error_code ec) { - timer.cancel(); - std::cout << "Completion with: " << ec.message() << std::endl; - EXPECT_TRUE(ec); // error - EXPECT_EQ(ec, MakeErrorCode(ErrorCode::OperationTimeout)); - }); - timer.async_wait([&op](asio::error_code ec) { - std::cout << "Timer event" << std::endl; - if (ec != asio::error::operation_aborted) { - op.Timeout(); - } - }); - - mIoContext.run(); - EXPECT_THROW(op.Complete(), sdk::RuntimeError); -} - -TEST_F(AsyncOp, Timeout2) -{ - using namespace fair::mq::sdk; - - asio::steady_timer timer(mIoContext.get_executor(), std::chrono::milliseconds(50)); - AsioAsyncOp op( - mIoContext.get_executor(), - [&timer](std::error_code ec) { - timer.cancel(); - std::cout << "Completion with: " << ec.message() << std::endl; - EXPECT_FALSE(ec); // success - }); - op.Complete(); // Complete before timer - timer.async_wait([&op](asio::error_code ec) { - std::cout << "Timer event" << std::endl; - if (ec != asio::error::operation_aborted) { - op.Timeout(); - } - }); - - mIoContext.run(); - EXPECT_THROW(op.Complete(), RuntimeError); -} -} // namespace diff --git a/test/sdk/_dds.cxx b/test/sdk/_dds.cxx deleted file mode 100644 index 3fab5a32..00000000 --- a/test/sdk/_dds.cxx +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "Fixtures.h" - -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#undef BOOST_BIND_GLOBAL_PLACEHOLDERS - -namespace { - -TEST(DDSEnvironment, Construction) -{ - fair::mq::test::LoggerConfig cfg; - fair::mq::sdk::DDSEnvironment env(CMAKE_CURRENT_BINARY_DIR); - - LOG(debug) << env; -} - -TEST(DDSSession, Construction) -{ - fair::mq::test::LoggerConfig cfg; - fair::mq::sdk::DDSEnvironment env(CMAKE_CURRENT_BINARY_DIR); - - fair::mq::sdk::DDSSession session(env); - session.StopOnDestruction(); - LOG(debug) << session; -} - -TEST(DDSSession, Construction2) -{ - fair::mq::test::LoggerConfig cfg; - fair::mq::sdk::DDSEnvironment env(CMAKE_CURRENT_BINARY_DIR); - - auto nativeSession(std::make_shared()); - nativeSession->create(); - - fair::mq::sdk::DDSSession session(nativeSession, env); - session.StopOnDestruction(); - LOG(debug) << session; - - session.RequestCommanderInfo(); -} - -} // namespace diff --git a/test/sdk/_topology.cxx b/test/sdk/_topology.cxx deleted file mode 100644 index 8a15abf0..00000000 --- a/test/sdk/_topology.cxx +++ /dev/null @@ -1,589 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -#include "Fixtures.h" - -#include -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#undef BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#include -#include - -#include - -namespace { - -using Topology = fair::mq::test::TopologyFixture; -using MultipleTopologies = fair::mq::test::MultipleTopologiesFixture; - -void control(fair::mq::sdk::Topology& topo) -{ - using fair::mq::sdk::TopologyTransition; - - for (auto transition : {TopologyTransition::InitDevice, - TopologyTransition::CompleteInit, - TopologyTransition::Bind, - TopologyTransition::Connect, - TopologyTransition::InitTask, - TopologyTransition::Run, - TopologyTransition::Stop, - TopologyTransition::ResetTask, - TopologyTransition::ResetDevice, - TopologyTransition::End}) { - ASSERT_EQ(topo.ChangeState(transition).first, std::error_code()); - } -} - -TEST(TopologyHelper, MakeTopology) -{ - using namespace fair::mq; - - // This is only needed for this unit test - test::LoggerConfig cfg; - sdk::DDSEnv env(CMAKE_CURRENT_BINARY_DIR); - - std::string topoFile(tools::ToString(SDK_TESTSUITE_SOURCE_DIR, "/test_topo.xml")); - dds::topology_api::CTopology nativeTopo(topoFile); - auto nativeSession(std::make_shared()); - nativeSession->create(); - EXPECT_THROW(sdk::MakeTopology(nativeTopo, nativeSession, env), sdk::RuntimeError); - nativeSession->shutdown(); -} - -TEST_F(MultipleTopologies, Construction) -{ - using namespace fair::mq; - - std::array topos{ - sdk::Topology(mDDSTopologies[0], mDDSSessions[0]), - sdk::Topology(mDDSTopologies[1], mDDSSessions[1]) - }; -} - -TEST_F(MultipleTopologies, ChangeStateFullDeviceLifecycle) -{ - using namespace fair::mq; - - std::array topos{ - sdk::Topology(mDDSTopologies[0], mDDSSessions[0]), - sdk::Topology(mDDSTopologies[1], mDDSSessions[1]) - }; - - for (int i = 0; i < mNumSessions; ++i) { - using fair::mq::sdk::TopologyTransition; - - for (auto transition : {TopologyTransition::InitDevice, - TopologyTransition::CompleteInit, - TopologyTransition::Bind, - TopologyTransition::Connect, - TopologyTransition::InitTask, - TopologyTransition::Run, - TopologyTransition::Stop, - TopologyTransition::ResetTask, - TopologyTransition::ResetDevice, - TopologyTransition::End}) { - ASSERT_EQ(topos[i].ChangeState(transition).first, std::error_code()); - } - } -} - -TEST_F(MultipleTopologies, ChangeStateFullDeviceLifecycleConcurrent) -{ - using namespace fair::mq; - - std::array topos{ - sdk::Topology(mDDSTopologies[0], mDDSSessions[0]), - sdk::Topology(mDDSTopologies[1], mDDSSessions[1]) - }; - - std::thread t0(control, std::ref(topos[0])); - std::thread t1(control, std::ref(topos[1])); - t0.join(); - t1.join(); -} - - -TEST_F(Topology, Construction) -{ - fair::mq::sdk::Topology topo(mDDSTopo, mDDSSession); -} - -TEST_F(Topology, Construction2) -{ - fair::mq::sdk::Topology topo(mIoContext.get_executor(), mDDSTopo, mDDSSession); -} - -TEST_F(Topology, AsyncChangeState) -{ - using namespace fair::mq; - - tools::SharedSemaphore blocker; - sdk::Topology topo(mDDSTopo, mDDSSession); - topo.AsyncChangeState( - sdk::TopologyTransition::InitDevice, - [=](std::error_code ec, sdk::TopologyState) mutable { - LOG(info) << ec; - EXPECT_EQ(ec, std::error_code()); - blocker.Signal(); - }); - blocker.Wait(); -} - -TEST_F(Topology, AsyncChangeStateWithCustomExecutor) -{ - using namespace fair::mq; - - sdk::Topology topo(mIoContext.get_executor(), mDDSTopo, mDDSSession); - topo.AsyncChangeState( - sdk::TopologyTransition::InitDevice, - [](std::error_code ec, sdk::TopologyState) { - LOG(info) << ec; - EXPECT_EQ(ec, std::error_code()); - }); - - mIoContext.run(); -} - -TEST_F(Topology, AsyncChangeStateFuture) -{ - using namespace fair::mq; - - sdk::Topology topo(mIoContext.get_executor(), mDDSTopo, mDDSSession); - auto fut(topo.AsyncChangeState( - sdk::TopologyTransition::InitDevice, - asio::use_future)); - std::thread t([&]() { mIoContext.run(); }); - bool success(false); - - try { - sdk::TopologyState state = fut.get(); - success = true; - } catch (const std::system_error& ex) { - LOG(error) << ex.what(); - } - - EXPECT_TRUE(success); - t.join(); -} - -#if defined(ASIO_HAS_CO_AWAIT) -TEST_F(Topology, AsyncChangeStateCoroutine) -{ - using namespace fair::mq; - - bool success(false); - asio::co_spawn( - mIoContext.get_executor(), - [&]() mutable -> asio::awaitable { - auto executor = co_await asio::this_coro::executor; - sdk::Topology topo(executor, mDDSTopo, mDDSSession); - try { - sdk::TopologyState state = co_await topo.AsyncChangeState( - sdk::TopologyTransition::InitDevice, asio::use_awaitable); - success = true; - } catch (const std::system_error& ex) { - LOG(error) << ex.what(); - } - }, - asio::detached); - - mIoContext.run(); - EXPECT_TRUE(success); -} -#endif - -TEST_F(Topology, ChangeState) -{ - using namespace fair::mq; - - sdk::Topology topo(mDDSTopo, mDDSSession); - auto result(topo.ChangeState(sdk::TopologyTransition::InitDevice)); - LOG(info) << result.first; - - EXPECT_EQ(result.first, std::error_code()); - EXPECT_NO_THROW(sdk::AggregateState(result.second)); - EXPECT_EQ(sdk::StateEqualsTo(result.second, sdk::DeviceState::InitializingDevice), true); - auto const currentState = topo.GetCurrentState(); - EXPECT_NO_THROW(sdk::AggregateState(currentState)); - EXPECT_EQ(sdk::StateEqualsTo(currentState, sdk::DeviceState::InitializingDevice), true); -} - -TEST_F(Topology, MixedState) -{ - using namespace fair::mq; - - sdk::Topology topo(mDDSTopo, mDDSSession); - auto result1(topo.ChangeState(sdk::TopologyTransition::InitDevice, "main/Sampler.*")); - LOG(info) << result1.first; - - EXPECT_EQ(result1.first, std::error_code()); - EXPECT_EQ(sdk::AggregateState(result1.second), sdk::AggregatedTopologyState::Mixed); - EXPECT_EQ(sdk::StateEqualsTo(result1.second, sdk::DeviceState::InitializingDevice), false); - auto const currentState1 = topo.GetCurrentState(); - EXPECT_EQ(sdk::AggregateState(currentState1), sdk::AggregatedTopologyState::Mixed); - EXPECT_EQ(sdk::StateEqualsTo(currentState1, sdk::DeviceState::InitializingDevice), false); - - auto result2(topo.ChangeState(sdk::TopologyTransition::InitDevice, "main/SinkGroup/.*")); - LOG(info) << result2.first; - - EXPECT_EQ(result2.first, std::error_code()); - EXPECT_EQ(sdk::AggregateState(result2.second), sdk::AggregatedTopologyState::InitializingDevice); - EXPECT_EQ(sdk::StateEqualsTo(result2.second, sdk::DeviceState::InitializingDevice), true); - auto const currentState2 = topo.GetCurrentState(); - EXPECT_EQ(sdk::AggregateState(currentState2), sdk::AggregatedTopologyState::InitializingDevice); - EXPECT_EQ(sdk::StateEqualsTo(currentState2, sdk::DeviceState::InitializingDevice), true); -} - -TEST_F(Topology, AsyncChangeStateConcurrent) -{ - using namespace fair::mq; - - sdk::Topology topo(mDDSTopo, mDDSSession); - topo.AsyncChangeState(sdk::TopologyTransition::InitDevice, "main/Sampler.*", - [](std::error_code ec, sdk::TopologyState) mutable { - LOG(info) << "ChangeState for Sampler: " << ec; - EXPECT_EQ(ec, std::error_code()); - }); - topo.AsyncChangeState(sdk::TopologyTransition::InitDevice, "main/SinkGroup/.*", - [](std::error_code ec, sdk::TopologyState) mutable { - LOG(info) << "ChangeState for Sinks: " << ec; - EXPECT_EQ(ec, std::error_code()); - }); - - topo.WaitForState(sdk::DeviceState::InitializingDevice); - auto const currentState = topo.GetCurrentState(); - EXPECT_NO_THROW(sdk::AggregateState(currentState)); - EXPECT_EQ(sdk::StateEqualsTo(currentState, sdk::DeviceState::InitializingDevice), true); -} - -TEST_F(Topology, AsyncChangeStateTimeout) -{ - using namespace fair::mq; - - sdk::Topology topo(mIoContext.get_executor(), mDDSTopo, mDDSSession); - topo.AsyncChangeState(sdk::TopologyTransition::InitDevice, - std::chrono::milliseconds(1), - [](std::error_code ec, sdk::TopologyState) { - LOG(info) << ec; - EXPECT_EQ(ec, MakeErrorCode(ErrorCode::OperationTimeout)); - }); - - mIoContext.run(); -} - -TEST_F(Topology, AsyncChangeStateCollectionView) -{ - using namespace fair::mq; - - tools::SharedSemaphore blocker; - sdk::Topology topo(mDDSTopo, mDDSSession); - topo.AsyncChangeState( - sdk::TopologyTransition::InitDevice, - [=](std::error_code ec, sdk::TopologyState state) mutable { - LOG(info) << ec; - sdk::TopologyStateByCollection cstate(sdk::GroupByCollectionId(state)); - LOG(debug) << "num collections: " << cstate.size(); - ASSERT_EQ(cstate.size(), 5); - for (const auto& c : cstate) { - LOG(debug) << "\t" << c.first; - sdk::AggregatedTopologyState s; - ASSERT_NO_THROW(s = sdk::AggregateState(c.second)); - ASSERT_EQ(s, static_cast(State::InitializingDevice)); - LOG(debug) << "\tAggregated state: " << s; - for (const auto& ds : c.second) { - LOG(debug) << "\t\t" << ds.state; - } - } - EXPECT_EQ(ec, std::error_code()); - blocker.Signal(); - }); - blocker.Wait(); -} - -TEST_F(Topology, ChangeStateFullDeviceLifecycle) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - for (auto transition : {TopologyTransition::InitDevice, - TopologyTransition::CompleteInit, - TopologyTransition::Bind, - TopologyTransition::Connect, - TopologyTransition::InitTask, - TopologyTransition::Run, - TopologyTransition::Stop, - TopologyTransition::ResetTask, - TopologyTransition::ResetDevice, - TopologyTransition::End}) { - ASSERT_EQ(topo.ChangeState(transition).first, std::error_code()); - } -} - -TEST_F(Topology, WaitForStateFullDeviceLifecycle) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - topo.AsyncWaitForState(sdk::DeviceState::ResettingDevice, [](std::error_code ec){ - ASSERT_EQ(ec, std::error_code()); - }); - for (auto transition : {TopologyTransition::InitDevice, - TopologyTransition::CompleteInit, - TopologyTransition::Bind, - TopologyTransition::Connect, - TopologyTransition::InitTask, - TopologyTransition::Run, - TopologyTransition::Stop, - TopologyTransition::ResetTask, - TopologyTransition::ResetDevice, - TopologyTransition::End}) { - topo.ChangeState(transition); - ASSERT_EQ(topo.WaitForState(sdk::expectedState.at(transition)), std::error_code()); - } -} - -TEST_F(Topology, ChangeStateFullDeviceLifecycle2) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - for (int i(0); i < 10; ++i) { - for (auto transition : {TopologyTransition::InitDevice, - TopologyTransition::CompleteInit, - TopologyTransition::Bind, - TopologyTransition::Connect, - TopologyTransition::InitTask, - TopologyTransition::Run}) { - ASSERT_EQ(topo.ChangeState(transition).first, std::error_code()); - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - for (auto transition : {TopologyTransition::Stop, - TopologyTransition::ResetTask, - TopologyTransition::ResetDevice}) { - ASSERT_EQ(topo.ChangeState(transition).first, std::error_code()); - } - } -} - -TEST_F(Topology, SetProperties) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - ASSERT_EQ(topo.ChangeState(TopologyTransition::InitDevice).first, std::error_code()); - - auto const result1 = topo.SetProperties({{"key1", "val1"}}); - LOG(info) << result1.first; - ASSERT_EQ(result1.first, std::error_code()); - ASSERT_EQ(result1.second.size(), 0); - auto const result2 = topo.SetProperties({{"key2", "val2"}, {"key3", "val3"}}); - LOG(info) << result2.first; - ASSERT_EQ(result2.first, std::error_code()); - ASSERT_EQ(result2.second.size(), 0); - - ASSERT_EQ(topo.ChangeState(TopologyTransition::CompleteInit).first, std::error_code()); - ASSERT_EQ(topo.ChangeState(TopologyTransition::ResetDevice).first, std::error_code()); -} - -TEST_F(Topology, AsyncSetPropertiesConcurrent) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - ASSERT_EQ(topo.ChangeState(TopologyTransition::InitDevice).first, std::error_code()); - - tools::SharedSemaphore blocker(2); - topo.AsyncSetProperties( - {{"key1", "val1"}}, - [=](std::error_code ec, sdk::FailedDevices failed) mutable { - LOG(info) << ec; - ASSERT_EQ(ec, std::error_code()); - ASSERT_EQ(failed.size(), 0); - blocker.Signal(); - }); - topo.AsyncSetProperties( - {{"key2", "val2"}, {"key3", "val3"}}, - [=](std::error_code ec, sdk::FailedDevices failed) mutable { - LOG(info) << ec; - ASSERT_EQ(ec, std::error_code()); - ASSERT_EQ(failed.size(), 0); - blocker.Signal(); - }); - blocker.Wait(); - - ASSERT_EQ(topo.ChangeState(TopologyTransition::CompleteInit).first, std::error_code()); - ASSERT_EQ(topo.ChangeState(TopologyTransition::ResetDevice).first, std::error_code()); -} - -TEST_F(Topology, AsyncSetPropertiesTimeout) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - ASSERT_EQ(topo.ChangeState(TopologyTransition::InitDevice).first, std::error_code()); - - topo.AsyncSetProperties({{"key1", "val1"}}, - "", - std::chrono::microseconds(1), - [=](std::error_code ec, sdk::FailedDevices) mutable { - LOG(info) << ec; - EXPECT_EQ(ec, MakeErrorCode(ErrorCode::OperationTimeout)); - }); - - ASSERT_EQ(topo.ChangeState(TopologyTransition::CompleteInit).first, std::error_code()); - ASSERT_EQ(topo.ChangeState(TopologyTransition::ResetDevice).first, std::error_code()); -} - -TEST_F(Topology, SetPropertiesMixed) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - ASSERT_EQ(topo.ChangeState(TopologyTransition::InitDevice).first, std::error_code()); - - tools::SharedSemaphore blocker; - topo.AsyncSetProperties( - {{"key1", "val1"}}, - [=](std::error_code ec, sdk::FailedDevices failed) mutable { - LOG(info) << ec; - ASSERT_EQ(ec, std::error_code()); - ASSERT_EQ(failed.size(), 0); - blocker.Signal(); - }); - - auto result = topo.SetProperties({{"key2", "val2"}}); - LOG(info) << result.first; - ASSERT_EQ(result.first, std::error_code()); - ASSERT_EQ(result.second.size(), 0); - - blocker.Wait(); - - ASSERT_EQ(topo.ChangeState(TopologyTransition::CompleteInit).first, std::error_code()); - ASSERT_EQ(topo.ChangeState(TopologyTransition::ResetDevice).first, std::error_code()); -} - -TEST_F(Topology, GetProperties) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - ASSERT_EQ(topo.ChangeState(TopologyTransition::InitDevice).first, std::error_code()); - - auto const result = topo.GetProperties("^(session|id)$"); - LOG(info) << result.first; - ASSERT_EQ(result.first, std::error_code()); - ASSERT_EQ(result.second.failed.size(), 0); - for (auto const& d : result.second.devices) { - LOG(info) << d.first; - ASSERT_EQ(d.second.props.size(), 2); - for (auto const& p : d.second.props) { - LOG(info) << p.first << " : " << p.second; - } - } - - ASSERT_EQ(topo.ChangeState(TopologyTransition::CompleteInit).first, std::error_code()); - ASSERT_EQ(topo.ChangeState(TopologyTransition::ResetDevice).first, std::error_code()); -} - -TEST_F(Topology, SetAndGetProperties) -{ - using namespace fair::mq; - using fair::mq::sdk::TopologyTransition; - - sdk::Topology topo(mDDSTopo, mDDSSession); - ASSERT_EQ(topo.ChangeState(TopologyTransition::InitDevice).first, std::error_code()); - - sdk::DeviceProperties const props{{"key1", "val1"}, {"key2", "val2"}}; - - auto const result1 = topo.SetProperties(props); - LOG(info) << result1.first; - ASSERT_EQ(result1.first, std::error_code()); - ASSERT_EQ(result1.second.size(), 0); - - auto const result2 = topo.GetProperties("^key.*"); - LOG(info) << result2.first; - ASSERT_EQ(result2.first, std::error_code()); - ASSERT_EQ(result2.second.failed.size(), 0); - ASSERT_EQ(result2.second.devices.size(), 6); - for (auto const& d : result2.second.devices) { - ASSERT_EQ(d.second.props, props); - } - - ASSERT_EQ(topo.ChangeState(TopologyTransition::CompleteInit).first, std::error_code()); - ASSERT_EQ(topo.ChangeState(TopologyTransition::ResetDevice).first, std::error_code()); -} - -TEST(Topology2, AggregatedTopologyStateComparison) -{ - using namespace fair::mq::sdk; - ASSERT_TRUE(DeviceState::Undefined == AggregatedTopologyState::Undefined); - ASSERT_TRUE(AggregatedTopologyState::Undefined == DeviceState::Undefined); - ASSERT_TRUE(DeviceState::Ok == AggregatedTopologyState::Ok); - ASSERT_TRUE(DeviceState::Error == AggregatedTopologyState::Error); - ASSERT_TRUE(DeviceState::Idle == AggregatedTopologyState::Idle); - ASSERT_TRUE(DeviceState::InitializingDevice == AggregatedTopologyState::InitializingDevice); - ASSERT_TRUE(DeviceState::Initialized == AggregatedTopologyState::Initialized); - ASSERT_TRUE(DeviceState::Binding == AggregatedTopologyState::Binding); - ASSERT_TRUE(DeviceState::Bound == AggregatedTopologyState::Bound); - ASSERT_TRUE(DeviceState::Connecting == AggregatedTopologyState::Connecting); - ASSERT_TRUE(DeviceState::DeviceReady == AggregatedTopologyState::DeviceReady); - ASSERT_TRUE(DeviceState::InitializingTask == AggregatedTopologyState::InitializingTask); - ASSERT_TRUE(DeviceState::Ready == AggregatedTopologyState::Ready); - ASSERT_TRUE(DeviceState::Running == AggregatedTopologyState::Running); - ASSERT_TRUE(DeviceState::ResettingTask == AggregatedTopologyState::ResettingTask); - ASSERT_TRUE(DeviceState::ResettingDevice == AggregatedTopologyState::ResettingDevice); - ASSERT_TRUE(DeviceState::Exiting == AggregatedTopologyState::Exiting); - - ASSERT_TRUE(GetAggregatedTopologyState("UNDEFINED") == AggregatedTopologyState::Undefined); - ASSERT_TRUE(GetAggregatedTopologyState("OK") == AggregatedTopologyState::Ok); - ASSERT_TRUE(GetAggregatedTopologyState("ERROR") == AggregatedTopologyState::Error); - ASSERT_TRUE(GetAggregatedTopologyState("IDLE") == AggregatedTopologyState::Idle); - ASSERT_TRUE(GetAggregatedTopologyState("INITIALIZING DEVICE") == AggregatedTopologyState::InitializingDevice); - ASSERT_TRUE(GetAggregatedTopologyState("INITIALIZED") == AggregatedTopologyState::Initialized); - ASSERT_TRUE(GetAggregatedTopologyState("BINDING") == AggregatedTopologyState::Binding); - ASSERT_TRUE(GetAggregatedTopologyState("BOUND") == AggregatedTopologyState::Bound); - ASSERT_TRUE(GetAggregatedTopologyState("CONNECTING") == AggregatedTopologyState::Connecting); - ASSERT_TRUE(GetAggregatedTopologyState("DEVICE READY") == AggregatedTopologyState::DeviceReady); - ASSERT_TRUE(GetAggregatedTopologyState("INITIALIZING TASK") == AggregatedTopologyState::InitializingTask); - ASSERT_TRUE(GetAggregatedTopologyState("READY") == AggregatedTopologyState::Ready); - ASSERT_TRUE(GetAggregatedTopologyState("RUNNING") == AggregatedTopologyState::Running); - ASSERT_TRUE(GetAggregatedTopologyState("RESETTING TASK") == AggregatedTopologyState::ResettingTask); - ASSERT_TRUE(GetAggregatedTopologyState("RESETTING DEVICE") == AggregatedTopologyState::ResettingDevice); - ASSERT_TRUE(GetAggregatedTopologyState("EXITING") == AggregatedTopologyState::Exiting); - ASSERT_TRUE(GetAggregatedTopologyState("MIXED") == AggregatedTopologyState::Mixed); - - ASSERT_TRUE("UNDEFINED" == GetAggregatedTopologyStateName(AggregatedTopologyState::Undefined)); - ASSERT_TRUE("OK" == GetAggregatedTopologyStateName(AggregatedTopologyState::Ok)); - ASSERT_TRUE("ERROR" == GetAggregatedTopologyStateName(AggregatedTopologyState::Error)); - ASSERT_TRUE("IDLE" == GetAggregatedTopologyStateName(AggregatedTopologyState::Idle)); - ASSERT_TRUE("INITIALIZING DEVICE" == GetAggregatedTopologyStateName(AggregatedTopologyState::InitializingDevice)); - ASSERT_TRUE("INITIALIZED" == GetAggregatedTopologyStateName(AggregatedTopologyState::Initialized)); - ASSERT_TRUE("BINDING" == GetAggregatedTopologyStateName(AggregatedTopologyState::Binding)); - ASSERT_TRUE("BOUND" == GetAggregatedTopologyStateName(AggregatedTopologyState::Bound)); - ASSERT_TRUE("CONNECTING" == GetAggregatedTopologyStateName(AggregatedTopologyState::Connecting)); - ASSERT_TRUE("DEVICE READY" == GetAggregatedTopologyStateName(AggregatedTopologyState::DeviceReady)); - ASSERT_TRUE("INITIALIZING TASK" == GetAggregatedTopologyStateName(AggregatedTopologyState::InitializingTask)); - ASSERT_TRUE("READY" == GetAggregatedTopologyStateName(AggregatedTopologyState::Ready)); - ASSERT_TRUE("RUNNING" == GetAggregatedTopologyStateName(AggregatedTopologyState::Running)); - ASSERT_TRUE("RESETTING TASK" == GetAggregatedTopologyStateName(AggregatedTopologyState::ResettingTask)); - ASSERT_TRUE("RESETTING DEVICE" == GetAggregatedTopologyStateName(AggregatedTopologyState::ResettingDevice)); - ASSERT_TRUE("EXITING" == GetAggregatedTopologyStateName(AggregatedTopologyState::Exiting)); - ASSERT_TRUE("MIXED" == GetAggregatedTopologyStateName(AggregatedTopologyState::Mixed)); -} - -} // namespace diff --git a/test/sdk/test_topo.xml b/test/sdk/test_topo.xml deleted file mode 100644 index 0a1a8e65..00000000 --- a/test/sdk/test_topo.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - fairmq-bsampler --color false --channel-config name=data,type=push,method=bind -P dds --msg-rate 10 --severity trace --verbosity veryhigh - - SamplerWorker - - - fmqchan_data - - - - - fairmq-sink --color false --channel-config name=data,type=pull,method=connect -P dds --severity trace --verbosity veryhigh - - SinkWorker - - - fmqchan_data - - - - - - Sink - - - -
- Sampler - - Sinks - -
- -
-