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.
This commit is contained in:
Dennis Klein 2022-03-22 12:58:10 +01:00
parent 69faa63c5b
commit e16cf67b94
47 changed files with 23 additions and 6582 deletions

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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 "<system>")
@ -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})

View File

@ -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)

View File

@ -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` | `<ID>: <STATE> (pid: <PID>)` | n/a | Query current device state, see state machine for possible states |
| `dump-config` | `(<ID>: <PKEY> -> <PVALUE>\n)+` | n/a | Query current device config (list property key/value pairs) |
| `INIT DEVICE` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `BIND` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `CONNECT` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `INIT TASK` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `RUN` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `STOP` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `RESET TASK` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `RESET DEVICE` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `END` | `<ID>: queued <CMD> transition` | `<ID>: could not queue <CMD> transition` | Initiate state transition |
| `subscribe-to-heartbeats` | `heartbeat-subscription: <ID>,OK` | n/a | Subscribe to heartbeats |
| on heartbeat subscription | `heartbeat: <ID>,<PID>` | n/a | Heartbeat every 100ms |
| `unsubscribe-from-heartbeats` | `heartbeat-unsubscription: <ID>,OK` | n/a | Unsubscribe from heartbeats |
| `subscribe-to-state-changes` | `state-changes-subscription: <ID>,OK` | n/a | Subscribe to state changes |
| on state changes subscription | `state-change: <ID>,<STATE>` | n/a | State change notification |
| `unsubscribe-from-state-changes` | `state-changes-unsubscription: <ID>,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.

View File

@ -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)

View File

@ -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()

View File

@ -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 <fairmq/sdk/AsioAsyncOp.h>
#include <fairmq/sdk/AsioBase.h>
#include <fairmq/sdk/DDSAgent.h>
#include <fairmq/sdk/DDSEnvironment.h>
#include <fairmq/sdk/DDSInfo.h>
#include <fairmq/sdk/DDSSession.h>
#include <fairmq/sdk/DDSTask.h>
#include <fairmq/sdk/DDSTopology.h>
#include <fairmq/sdk/Error.h>
#include <fairmq/sdk/Topology.h>
#include <fairmq/sdk/Traits.h>
// IWYU pragma: end_exports
#endif // FAIR_MQ_SDK_H

View File

@ -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}
)

View File

@ -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 <fairmq/tools/Strings.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <asio/post.hpp>
#include <cstdlib>
#include <stdexcept>
#include <sstream>
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<dds::task_id>())
, fCurrentState(DeviceState::Idle)
, fLastState(DeviceState::Idle)
, fDeviceTerminationRequested(false)
, fLastExternalController(0)
, fExitingAckedByLastExternalController(false)
, fUpdatesAllowed(false)
, fWorkGuard(fWorkerQueue.get_executor())
{
try {
TakeDeviceControl();
string deviceId(GetProperty<string>("id"));
if (deviceId.empty()) {
SetProperty<string>("id", dds::env_prop<dds::task_path>());
}
string sessionId(GetProperty<string>("session"));
if (sessionId == "default") {
SetProperty<string>("session", dds::env_prop<dds::dds_session_id>());
}
auto control = GetProperty<string>("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<mutex> 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<mutex> 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<string>("id");
fLastState = fCurrentState;
fCurrentState = newState;
lock_guard<mutex> 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<double>(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<StateChange>(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<mutex> lock(fStateChangeSubscriberMutex);
auto timeout = GetProperty<unsigned int>("wait-for-exiting-ack-timeout");
fExitingAcked.wait_for(lock, chrono::milliseconds(timeout), [this]() {
return fExitingAckedByLastExternalController || fStateChangeSubscribers.empty();
});
}
auto DDS::FillChannelContainers() -> void
{
try {
unordered_map<string, int> 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<string>(methodKey) == "bind") {
fBindingChans.insert(make_pair(c.first, vector<string>()));
for (int i = 0; i < c.second; ++i) {
fBindingChans.at(c.first).push_back(GetProperty<string>(string{"chans." + c.first + "." + to_string(i) + ".address"}));
}
} else if (GetProperty<string>(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<string> iValues;
if (PropertyExists("dds-i")) {
iValues = GetProperty<vector<string>>("dds-i");
}
vector<string> inValues;
if (PropertyExists("dds-i-n")) {
inValues = GetProperty<vector<string>>("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<mutex> 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<string> 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>(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<string>("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<CurrentState>(id, GetCurrentDeviceState())).Serialize(), to_string(senderId));
} break;
case Type::change_state: {
Transition transition = static_cast<ChangeState&>(cmd).GetTransition();
if (ChangeDeviceState(transition)) {
Cmds outCmds(make<TransitionStatus>(id, fDDSTaskId, Result::Ok, transition, GetCurrentDeviceState()));
fDDS.Send(outCmds.Serialize(), to_string(senderId));
} else {
Cmds outCmds(make<TransitionStatus>(id, fDDSTaskId, Result::Failure, transition, GetCurrentDeviceState()));
fDDS.Send(outCmds.Serialize(), to_string(senderId));
}
{
lock_guard<mutex> 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<Config>(id, ss.str()));
fDDS.Send(outCmds.Serialize(), to_string(senderId));
} break;
case Type::state_change_exiting_received: {
{
lock_guard<mutex> lock{fStateChangeSubscriberMutex};
if (fLastExternalController == senderId) {
fExitingAckedByLastExternalController = true;
}
}
fExitingAcked.notify_one();
} break;
case Type::subscribe_to_state_change: {
auto _cmd = static_cast<cmd::SubscribeToStateChange&>(cmd);
lock_guard<mutex> 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<StateChangeSubscription>(id, fDDSTaskId, Result::Ok),
make<StateChange>(id, fDDSTaskId, fLastState, fCurrentState));
fDDS.Send(outCmds.Serialize(), to_string(senderId));
} break;
case Type::subscription_heartbeat: {
try {
auto _cmd = static_cast<cmd::SubscriptionHeartbeat&>(cmd);
lock_guard<mutex> 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<mutex> lock{fStateChangeSubscriberMutex};
fStateChangeSubscribers.erase(senderId);
}
Cmds outCmds(make<StateChangeUnsubscription>(id, fDDSTaskId, Result::Ok));
fDDS.Send(outCmds.Serialize(), to_string(senderId));
} break;
case Type::get_properties: {
auto _cmd = static_cast<cmd::GetProperties&>(cmd);
auto const request_id(_cmd.GetRequestId());
auto result(Result::Ok);
vector<pair<string, string>> 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<cmd::Properties>(id, request_id, result, props));
fDDS.Send(outCmds.Serialize(), to_string(senderId));
} break;
case Type::set_properties: {
auto _cmd(static_cast<cmd::SetProperties&>(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<PropertiesSet>(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

View File

@ -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 <fairmq/Plugin.h>
#include <fairmq/StateQueue.h>
#include <fairmq/Version.h>
#include <fairmq/sdk/commands/Commands.h>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <dds/dds.h>
#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <asio/executor.hpp>
#include <asio/executor_work_guard.hpp>
#include <asio/io_context.hpp>
#include <cassert>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <string>
#include <atomic>
#include <thread>
#include <map>
#include <unordered_map>
#include <utility> // pair
#include <vector>
namespace fair::mq::plugins
{
struct DDSConfig
{
// container of sub channel addresses
unsigned int fNumSubChannels;
// dds values for the channel
std::map<uint64_t, std::string> fDDSValues;
};
struct DDSSubscription
{
DDSSubscription()
: fDDSCustomCmd(fService)
, fDDSKeyValue(fService)
{
LOG(debug) << "$DDS_TASK_PATH: " << dds::env_prop<dds::task_path>();
LOG(debug) << "$DDS_GROUP_NAME: " << dds::env_prop<dds::group_name>();
LOG(debug) << "$DDS_COLLECTION_NAME: " << dds::env_prop<dds::collection_name>();
LOG(debug) << "$DDS_TASK_NAME: " << dds::env_prop<dds::task_name>();
LOG(debug) << "$DDS_TASK_INDEX: " << dds::env_prop<dds::task_index>();
LOG(debug) << "$DDS_COLLECTION_INDEX: " << dds::env_prop<dds::collection_index>();
LOG(debug) << "$DDS_TASK_ID: " << dds::env_prop<dds::task_id>();
LOG(debug) << "$DDS_LOCATION: " << dds::env_prop<dds::dds_location>();
std::string dds_session_id(dds::env_prop<dds::dds_session_id>());
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<dds::dds_session_id>());
}
~DDSSubscription() {
fDDSKeyValue.unsubscribe();
fDDSCustomCmd.unsubscribe();
}
template<typename... Args>
auto SubscribeCustomCmd(Args&&... args) -> void
{
fDDSCustomCmd.subscribe(std::forward<Args>(args)...);
}
template<typename... Args>
auto SubscribeKeyValue(Args&&... args) -> void
{
fDDSKeyValue.subscribe(std::forward<Args>(args)...);
}
template<typename... Args>
auto Send(Args&&... args) -> void
{
fDDSCustomCmd.send(std::forward<Args>(args)...);
}
template<typename... Args>
auto PutValue(Args&&... args) -> void
{
fDDSKeyValue.putValue(std::forward<Args>(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<std::string> 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<std::string, std::vector<std::string>> fBindingChans;
std::unordered_map<std::string, DDSConfig> fConnectingChans;
std::unordered_map<std::string, int> fI;
std::unordered_map<std::string, IofN> fIofN;
std::thread fControllerThread;
DeviceState fCurrentState, fLastState;
std::atomic<bool> fDeviceTerminationRequested;
std::unordered_map<uint64_t, std::pair<std::chrono::steady_clock::time_point, int64_t>> 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<asio::executor> fWorkGuard;
};
Plugin::ProgOptions DDSProgramOptions()
{
boost::program_options::options_description options{"DDS Plugin"};
options.add_options()
("dds-i", boost::program_options::value<std::vector<std::string>>()->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<std::vector<std::string>>()->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<unsigned int>()->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 <fairroot@gsi.de>", // Maintainer
"https://github.com/FairRootGroup/FairMQ", // Homepage
DDSProgramOptions // custom program options for the plugin
)
} // namespace fair::mq::plugins
#endif /* FAIR_MQ_PLUGINS_DDS */

View File

@ -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}

View File

@ -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 <fairmq/sdk/commands/Commands.h>
#include <fairmq/tools/Strings.h>
#include <sstream>
@ -16,7 +15,6 @@
#include <cstdint> // 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<string>("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<mutex> lock{fStateChangeSubscriberMutex};
fLastState = fCurrentState;
fCurrentState = newState;
for (auto subscriberId : fStateChangeSubscribers) {
LOG(debug) << "Publishing state-change: " << fLastState << "->" << newState << " to " << subscriberId;
Cmds cmds(make<StateChange>(fDeviceId, 0, fLastState, fCurrentState));
fCommands.Send(cmds.Serialize(Format::JSON), static_cast<pmix::rank>(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<CurrentState>(fDeviceId, GetCurrentDeviceState()))
.Serialize(Format::JSON),
{sender});
break;
case Type::change_state: {
Transition transition = static_cast<ChangeState&>(*cmd).GetTransition();
if (ChangeDeviceState(transition)) {
fCommands.Send(
Cmds(make<TransitionStatus>(fDeviceId, 0, Result::Ok, transition, GetCurrentDeviceState()))
.Serialize(Format::JSON),
{sender});
} else {
fCommands.Send(
Cmds(make<TransitionStatus>(fDeviceId, 0, Result::Failure, transition, GetCurrentDeviceState()))
.Serialize(Format::JSON),
{sender});
}
{
lock_guard<mutex> lock{fStateChangeSubscriberMutex};
fLastExternalController = sender.rank;
}
}
break;
case Type::subscribe_to_state_change: {
{
lock_guard<mutex> lock{fStateChangeSubscriberMutex};
fStateChangeSubscribers.insert(sender.rank);
}
LOG(debug) << "Publishing state-change: " << fLastState << "->" << fCurrentState
<< " to " << sender;
Cmds outCmds(make<StateChangeSubscription>(fDeviceId, fProcess.rank, Result::Ok),
make<StateChange>(fDeviceId, 0, fLastState, fCurrentState));
fCommands.Send(outCmds.Serialize(Format::JSON), {sender});
}
break;
case Type::unsubscribe_from_state_change: {
{
lock_guard<mutex> lock{fStateChangeSubscriberMutex};
fStateChangeSubscribers.erase(sender.rank);
}
fCommands.Send(Cmds(make<StateChangeUnsubscription>(fDeviceId, fProcess.rank, Result::Ok))
.Serialize(Format::JSON),
{sender});
}
break;
case Type::state_change_exiting_received: {
{
lock_guard<mutex> 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<Config>(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()) {

View File

@ -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<uint32_t> 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;
};

View File

@ -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 <fairmq/sdk/commands/Commands.h>
#include <fairmq/States.h>
#include <fairlogger/Logger.h>
@ -29,7 +28,6 @@
#include <vector>
using namespace std;
using namespace fair::mq::sdk::cmd;
namespace bpo = boost::program_options;
const std::map<fair::mq::Transition, fair::mq::State> expected =
@ -46,23 +44,6 @@ const std::map<fair::mq::Transition, fair::mq::State> 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<SubscribeToStateChange>(600000)).Serialize(Format::JSON));
}
~StateSubscription()
{
fCommands.Send(Cmds(make<UnsubscribeFromStateChange>()).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<StateChange&>(*cmd).GetDeviceId() << ": " << static_cast<StateChange&>(*cmd).GetLastState() << "->" << static_cast<StateChange&>(*cmd).GetCurrentState() << endl;
topo.Update(sender.rank, static_cast<StateChange&>(*cmd).GetCurrentState());
if (static_cast<StateChange&>(*cmd).GetCurrentState() == fair::mq::State::Exiting) {
commands.Send(Cmds(make<StateChangeExitingReceived>()).Serialize(Format::JSON), {sender});
}
}
break;
case Type::state_change_subscription:
if (static_cast<StateChangeSubscription&>(*cmd).GetResult() != Result::Ok) {
cout << "State change subscription failed for " << static_cast<StateChangeSubscription&>(*cmd).GetDeviceId() << endl;
}
break;
case Type::state_change_unsubscription:
if (static_cast<StateChangeUnsubscription&>(*cmd).GetResult() != Result::Ok) {
cout << "State change unsubscription failed for " << static_cast<StateChangeUnsubscription&>(*cmd).GetDeviceId() << endl;
}
break;
case Type::transition_status: {
if (static_cast<TransitionStatus&>(*cmd).GetResult() == Result::Ok) {
cout << "Device " << static_cast<TransitionStatus&>(*cmd).GetDeviceId() << " started to transition with " << static_cast<TransitionStatus&>(*cmd).GetTransition() << endl;
} else {
cout << "Device " << static_cast<TransitionStatus&>(*cmd).GetDeviceId() << " cannot transition with " << static_cast<TransitionStatus&>(*cmd).GetTransition() << endl;
}
}
break;
case Type::current_state:
cout << "Device " << static_cast<CurrentState&>(*cmd).GetDeviceId() << " is in " << static_cast<CurrentState&>(*cmd).GetCurrentState() << " state" << endl;
break;
case Type::config:
cout << "Received config for device " << static_cast<Config&>(*cmd).GetDeviceId() << ":\n" << static_cast<Config&>(*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<ChangeState>(transition)).Serialize(Format::JSON));
topo.WaitFor(expected.at(transition));
}
} catch (exception& e) {
LOG(error) << "Error: " << e.what();
return EXIT_FAILURE;

View File

@ -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 <asio/associated_allocator.hpp>
#include <asio/associated_executor.hpp>
#include <asio/executor_work_guard.hpp>
#include <asio/dispatch.hpp>
#include <asio/system_executor.hpp>
#include <chrono>
#include <exception>
#include <fairmq/sdk/Error.h>
#include <fairmq/sdk/Traits.h>
#include <functional>
#include <memory>
#include <system_error>
#include <type_traits>
#include <utility>
#include <fairlogger/Logger.h>
#ifndef FAIR_LOG
#define FAIR_LOG LOG
#endif /* ifndef FAIR_LOG */
namespace fair::mq::sdk
{
template<typename... SignatureArgTypes>
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<typename Executor1, typename Allocator1, typename Handler, typename... SignatureArgTypes>
struct AsioAsyncOpImpl : AsioAsyncOpImplBase<SignatureArgTypes...>
{
/// 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<Handler, Allocator1>::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<Handler, Executor1>::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<Executor1> fWork1;
asio::executor_work_guard<Executor2> fWork2;
Handler fHandler;
Allocator1 fAlloc1;
};
/**
* @class AsioAsyncOp AsioAsyncOp.h <fairmq/sdk/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<typename Executor, typename Allocator, typename CompletionSignature>
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<typename Executor,
typename Allocator,
typename SignatureReturnType,
typename SignatureFirstArgType,
typename... SignatureArgTypes>
struct AsioAsyncOp<Executor,
Allocator,
SignatureReturnType(SignatureFirstArgType, SignatureArgTypes...)>
{
static_assert(std::is_void<SignatureReturnType>::value,
"return value of CompletionSignature must be void");
static_assert(std::is_same<SignatureFirstArgType, std::error_code>::value,
"first argument of CompletionSignature must be std::error_code");
using Duration = std::chrono::milliseconds;
private:
using Impl = AsioAsyncOpImplBase<SignatureArgTypes...>;
using ImplPtr = std::unique_ptr<Impl, std::function<void(Impl*)>>;
ImplPtr fImpl;
public:
/// Default Ctor
AsioAsyncOp()
: fImpl(nullptr)
{}
/// Ctor with handler
template<typename Handler>
AsioAsyncOp(Executor ex1, Allocator alloc1, Handler&& handler)
: AsioAsyncOp()
{
// Async operation type to be allocated and constructed
using Op = AsioAsyncOpImpl<Executor, Allocator, Handler, SignatureArgTypes...>;
// 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<typename Op::Allocator2>::template rebind_alloc<Op>;
OpAllocator opAlloc;
// Allocate memory
auto mem(std::allocator_traits<OpAllocator>::allocate(opAlloc, 1));
// Construct object
auto ptr(new (mem) Op(std::move(ex1),
std::move(alloc1),
std::forward<Handler>(handler)));
// Assign ownership to this object
fImpl = ImplPtr(ptr, [opAlloc](Impl* p) mutable {
std::allocator_traits<OpAllocator>::deallocate(opAlloc, static_cast<Op*>(p), 1);
});
}
/// Ctor with handler #2
template<typename Handler>
AsioAsyncOp(Executor ex1, Handler&& handler)
: AsioAsyncOp(std::move(ex1), Allocator(), std::forward<Handler>(handler))
{}
/// Ctor with handler #3
template<typename Handler>
explicit AsioAsyncOp(Handler&& handler)
: AsioAsyncOp(asio::system_executor(), std::forward<Handler>(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 */

View File

@ -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 <asio/any_io_executor.hpp>
#include <fairmq/sdk/Traits.h>
#include <memory>
#include <utility>
namespace fair::mq::sdk
{
using DefaultExecutor = asio::any_io_executor;
using DefaultAllocator = std::allocator<int>;
/**
* @class AsioBase AsioBase.h <fairmq/sdk/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<typename Executor, typename Allocator>
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 */

View File

@ -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
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
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
)

View File

@ -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 <fairmq/sdk/DDSSession.h>
#include <ostream>
#include <string>
#include <chrono>
#include <cstdint>
namespace fair::mq::sdk
{
/**
* @class DDSAgent <fairmq/sdk/DDSAgent.h>
* @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 */

View File

@ -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 <fairmq/sdk/DDSAgent.h>
#include <ostream>
#include <cstdint>
namespace fair::mq::sdk
{
/**
* @class DDSCollection <fairmq/sdk/DDSCollection.h>
* @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 */

View File

@ -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 <cstdlib>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <dds/dds.h>
#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <fairlogger/Logger.h>
#include <fairmq/tools/InstanceLimit.h>
#include <fairmq/sdk/DDSInfo.h>
#include <sstream>
#include <utility>
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<Tag, 1> fCount;
Path fLocation;
Path fConfigHome;
};
DDSEnvironment::DDSEnvironment()
: DDSEnvironment(Path())
{}
DDSEnvironment::DDSEnvironment(Path configHome)
: fImpl(std::make_shared<Impl>(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

View File

@ -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 <boost/filesystem.hpp>
#include <memory>
#include <ostream>
namespace fair::mq::sdk
{
/**
* @class DDSEnvironment DDSSession.h <fairmq/sdk/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<Impl> fImpl;
};
using DDSEnv = DDSEnvironment;
} // namespace fair::mq::sdk
#endif /* FAIR_MQ_SDK_DDSENVIRONMENT_H */

View File

@ -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 <string>
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 */

View File

@ -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 <boost/process.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <cassert>
#include <cstdlib>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <dds/dds.h>
#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <fairlogger/Logger.h>
#include <fairmq/sdk/DDSAgent.h>
#include <fairmq/sdk/DDSEnvironment.h>
#include <fairmq/sdk/DDSTopology.h>
#include <fairmq/tools/Semaphore.h>
#include <fairmq/tools/Strings.h>
#include <mutex>
#include <sstream>
#include <thread>
#include <unordered_map>
#include <utility>
#include <vector>
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<dds::tools_api::CSession>())
, 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<dds::tools_api::CSession>())
, 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<dds::tools_api::CSession> 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<dds::tools_api::CSession> fSession;
dds::intercom_api::CIntercomService fDDSService;
dds::intercom_api::CCustomCmd fDDSCustomCmd;
Id fId;
bool fStopOnDestruction;
};
DDSSession::DDSSession(DDSEnvironment env)
: fImpl(std::make_shared<Impl>(std::move(env)))
{}
DDSSession::DDSSession(Id existing, DDSEnvironment env)
: fImpl(std::make_shared<Impl>(std::move(existing), std::move(env)))
{}
DDSSession::DDSSession(std::shared_ptr<dds::tools_api::CSession> nativeSession, DDSEnv env)
: fImpl(std::make_shared<Impl>(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<SSubmitRequest>(submitRequest);
blocker.Wait();
WaitForIdleAgents(agents);
}
auto DDSSession::RequestAgentCount() -> AgentCount
{
using namespace dds::tools_api;
SAgentCountRequest::response_t res;
fImpl->fSession->syncSendRequest<SAgentCountRequest>(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<DDSAgent>
{
using namespace dds::tools_api;
SAgentInfoRequest::responseVector_t res;
fImpl->fSession->syncSendRequest<SAgentInfoRequest>(SAgentInfoRequest::request_t(), res);
std::vector<DDSAgent> 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<DDSTask>
{
using namespace dds::tools_api;
SAgentInfoRequest::responseVector_t res;
fImpl->fSession->syncSendRequest<SAgentInfoRequest>(SAgentInfoRequest::request_t(), res);
std::vector<DDSTask> 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<SCommanderInfoRequest>(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<STopologyRequest>(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<void(const std::string& msg, const std::string& condition, uint64_t senderId)> 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<std::string> 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

View File

@ -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 <fairmq/sdk/DDSEnvironment.h>
#include <fairmq/sdk/DDSInfo.h>
#include <fairmq/sdk/DDSTask.h>
#include <boost/filesystem.hpp>
#include <cstdint>
#include <istream>
#include <memory>
#include <ostream>
#include <stdexcept>
#include <string>
#include <functional>
#include <vector>
namespace fair::mq::sdk
{
/**
* @enum DDSRMSPlugin DDSSession.h <fairmq/sdk/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 <fairmq/sdk/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<dds::tools_api::CSession> 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<DDSAgent>;
auto RequestTaskInfo() -> std::vector<DDSTask>;
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(const std::string& msg, const std::string& condition, uint64_t senderId)>);
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<Impl> fImpl;
};
auto getMostRecentRunningDDSSession(DDSEnv env = {}) -> DDSSession;
} // namespace fair::mq::sdk
#endif /* FAIR_MQ_SDK_DDSSESSION_H */

View File

@ -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 <fairmq/sdk/DDSCollection.h>
#include <ostream>
#include <cstdint>
namespace fair::mq::sdk
{
/**
* @class DDSTask <fairmq/sdk/DDSTask.h>
* @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 */

View File

@ -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 <boost/range/iterator_range.hpp>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <dds/dds.h>
#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <fairlogger/Logger.h>
#include <fairmq/sdk/DDSEnvironment.h>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <utility>
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<Impl>(std::move(topoFile), std::move(env)))
{}
DDSTopology::DDSTopology(dds::topology_api::CTopology nativeTopology, DDSEnv env)
: fImpl(std::make_shared<Impl>(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<DDSTask>
{
std::vector<DDSTask> 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<DDSCollection>
{
std::vector<DDSCollection> 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

View File

@ -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 <boost/filesystem.hpp>
#include <fairmq/sdk/DDSCollection.h>
#include <fairmq/sdk/DDSEnvironment.h>
#include <fairmq/sdk/DDSInfo.h>
#include <fairmq/sdk/DDSTask.h>
#include <memory>
#include <string>
#include <vector>
namespace fair::mq::sdk
{
/**
* @class DDSTopology DDSTopology.h <fairmq/sdk/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<DDSTask>;
/// @brief Get list of tasks in this topology
auto GetCollections() const -> std::vector<DDSCollection>;
/// @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<Impl> fImpl;
};
using DDSTopo = DDSTopology;
} // namespace fair::mq::sdk
#endif /* FAIR_MQ_SDK_DDSTOPOLOGY_H */

View File

@ -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 <fairmq/Error.h>
namespace fair::mq::sdk {
using RuntimeError = fair::mq::RuntimeError;
} /* namespace fair::mq::sdk */
#endif /* FAIR_MQ_SDK_ERROR_H */

View File

@ -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 <dds/dds.h>
#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<dds::tools_api::CSession> nativeSession,
DDSEnv env,
bool blockUntilConnected) -> Topology
{
return {DDSTopo(std::move(nativeTopo), env), DDSSession(std::move(nativeSession), env), blockUntilConnected};
}
} // namespace fair::mq::sdk

File diff suppressed because it is too large Load Diff

View File

@ -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 <asio/associated_allocator.hpp>
#include <asio/associated_executor.hpp>
#include <type_traits>
namespace asio::detail {
/// Specialize to match our coding conventions
template<typename T, typename Executor>
struct associated_executor_impl<T,
Executor,
std::enable_if_t<is_executor<typename T::ExecutorType>::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<typename T, typename Allocator>
struct associated_allocator_impl<T,
Allocator,
std::enable_if_t<T::AllocatorType>>
{
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 */

View File

@ -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 $<TARGET_FILE:flatbuffers::flatc> -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
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
install(
TARGETS ${target}
EXPORT ${PROJECT_EXPORT_SET}
DESTINATION ${PROJECT_INSTALL_LIBDIR}
)
install(FILES Commands.h
DESTINATION ${PROJECT_INSTALL_INCDIR}/sdk/commands
)

View File

@ -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 <fairmq/sdk/commands/CommandsFormatDef.h>
#include <fairmq/sdk/commands/CommandsFormat.h>
#include <flatbuffers/idl.h>
#include <array>
using namespace std;
namespace fair::mq::sdk::cmd
{
array<Result, 2> fbResultToResult =
{
{
Result::Ok,
Result::Failure
}
};
array<FBResult, 2> resultToFBResult =
{
{
FBResult::FBResult_Ok,
FBResult::FBResult_Failure
}
};
array<string, 2> resultNames =
{
{
"Ok",
"Failure"
}
};
array<string, 17> typeNames =
{
{
"CheckState",
"ChangeState",
"DumpConfig",
"SubscribeToStateChange",
"UnsubscribeFromStateChange",
"StateChangeExitingReceived",
"GetProperties",
"SetProperties",
"SubscriptionHeartbeat",
"CurrentState",
"TransitionStatus",
"Config",
"StateChangeSubscription",
"StateChangeUnsubscription",
"StateChange",
"Properties",
"PropertiesSet"
}
};
array<fair::mq::State, 16> 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<sdk::cmd::FBState, 16> 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<fair::mq::Transition, 12> 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<sdk::cmd::FBTransition, 12> 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<FBCmd, 17> 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<Type, 17> 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<int>(state)); }
fair::mq::Transition GetMQTransition(const FBTransition transition) { return fbTransitionToMQTransition.at(transition); }
FBTransition GetFBTransition(const fair::mq::Transition transition) { return mqTransitionToFBTransition.at(static_cast<int>(transition)); }
Result GetResult(const FBResult result) { return fbResultToResult.at(result); }
FBResult GetFBResult(const Result result) { return resultToFBResult.at(static_cast<int>(result)); }
string GetResultName(const Result result) { return resultNames.at(static_cast<int>(result)); }
string GetTypeName(const Type type) { return typeNames.at(static_cast<int>(type)); }
FBCmd GetFBCmd(const Type type) { return typeToFBCmd.at(static_cast<int>(type)); }
string Cmds::Serialize(const Format type) const
{
flatbuffers::FlatBufferBuilder fbb;
vector<flatbuffers::Offset<FBCommand>> commandOffsets;
for (auto& cmd : fCmds) {
flatbuffers::Offset<FBCommand> cmdOffset;
unique_ptr<FBCommandBuilder> 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<FBCommandBuilder>(fbb);
}
break;
case Type::change_state: {
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
cmdBuilder->add_transition(GetFBTransition(static_cast<ChangeState&>(*cmd).GetTransition()));
}
break;
case Type::dump_config: {
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
}
break;
break;
case Type::subscribe_to_state_change: {
auto _cmd = static_cast<SubscribeToStateChange&>(*cmd);
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
cmdBuilder->add_interval(_cmd.GetInterval());
}
break;
case Type::unsubscribe_from_state_change: {
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
}
break;
case Type::state_change_exiting_received: {
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
}
break;
case Type::get_properties: {
auto _cmd = static_cast<GetProperties&>(*cmd);
auto query = fbb.CreateString(_cmd.GetQuery());
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
cmdBuilder->add_request_id(_cmd.GetRequestId());
cmdBuilder->add_property_query(query);
}
break;
case Type::set_properties: {
auto _cmd = static_cast<SetProperties&>(*cmd);
std::vector<flatbuffers::Offset<FBProperty>> 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<FBCommandBuilder>(fbb);
cmdBuilder->add_request_id(_cmd.GetRequestId());
cmdBuilder->add_properties(props);
}
break;
case Type::subscription_heartbeat: {
auto _cmd = static_cast<SubscriptionHeartbeat&>(*cmd);
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
cmdBuilder->add_interval(_cmd.GetInterval());
}
break;
case Type::current_state: {
auto _cmd = static_cast<CurrentState&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
cmdBuilder->add_device_id(deviceId);
cmdBuilder->add_current_state(GetFBState(_cmd.GetCurrentState()));
}
break;
case Type::transition_status: {
auto _cmd = static_cast<TransitionStatus&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
cmdBuilder = make_unique<FBCommandBuilder>(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<Config&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
auto config = fbb.CreateString(_cmd.GetConfig());
cmdBuilder = make_unique<FBCommandBuilder>(fbb);
cmdBuilder->add_device_id(deviceId);
cmdBuilder->add_config_string(config);
}
break;
case Type::state_change_subscription: {
auto _cmd = static_cast<StateChangeSubscription&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
cmdBuilder = make_unique<FBCommandBuilder>(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<StateChangeUnsubscription&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
cmdBuilder = make_unique<FBCommandBuilder>(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<StateChange&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
cmdBuilder = make_unique<FBCommandBuilder>(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<Properties&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
std::vector<flatbuffers::Offset<FBProperty>> 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<FBCommandBuilder>(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<PropertiesSet&>(*cmd);
auto deviceId = fbb.CreateString(_cmd.GetDeviceId());
cmdBuilder = make_unique<FBCommandBuilder>(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<int>(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<char*>(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<flatbuffers::Offset<FBCommand>>* cmds = nullptr;
flatbuffers::Parser parser;
if (type == Format::Binary) {
cmds = cmd::GetFBCommands(const_cast<char*>(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<CheckState>());
break;
case FBCmd_change_state:
fCmds.emplace_back(make<ChangeState>(GetMQTransition(cmdPtr.transition())));
break;
case FBCmd_dump_config:
fCmds.emplace_back(make<DumpConfig>());
break;
case FBCmd_subscribe_to_state_change:
fCmds.emplace_back(make<SubscribeToStateChange>(cmdPtr.interval()));
break;
case FBCmd_unsubscribe_from_state_change:
fCmds.emplace_back(make<UnsubscribeFromStateChange>());
break;
case FBCmd_state_change_exiting_received:
fCmds.emplace_back(make<StateChangeExitingReceived>());
break;
case FBCmd_get_properties:
fCmds.emplace_back(make<GetProperties>(cmdPtr.request_id(), cmdPtr.property_query()->str()));
break;
case FBCmd_set_properties: {
std::vector<std::pair<std::string, std::string>> 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<SetProperties>(cmdPtr.request_id(), properties));
} break;
case FBCmd_subscription_heartbeat:
fCmds.emplace_back(make<SubscriptionHeartbeat>(cmdPtr.interval()));
break;
case FBCmd_current_state:
fCmds.emplace_back(make<CurrentState>(cmdPtr.device_id()->str(), GetMQState(cmdPtr.current_state())));
break;
case FBCmd_transition_status:
fCmds.emplace_back(make<TransitionStatus>(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<Config>(cmdPtr.device_id()->str(), cmdPtr.config_string()->str()));
break;
case FBCmd_state_change_subscription:
fCmds.emplace_back(make<StateChangeSubscription>(cmdPtr.device_id()->str(), cmdPtr.task_id(), GetResult(cmdPtr.result())));
break;
case FBCmd_state_change_unsubscription:
fCmds.emplace_back(make<StateChangeUnsubscription>(cmdPtr.device_id()->str(), cmdPtr.task_id(), GetResult(cmdPtr.result())));
break;
case FBCmd_state_change:
fCmds.emplace_back(make<StateChange>(cmdPtr.device_id()->str(), cmdPtr.task_id(), GetMQState(cmdPtr.last_state()), GetMQState(cmdPtr.current_state())));
break;
case FBCmd_properties: {
std::vector<std::pair<std::string, std::string>> 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<Properties>(cmdPtr.device_id()->str(), cmdPtr.request_id(), GetResult(cmdPtr.result()), properties));
} break;
case FBCmd_properties_set:
fCmds.emplace_back(make<PropertiesSet>(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

View File

@ -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 <fairmq/States.h>
#include <vector>
#include <string>
#include <memory>
#include <type_traits>
#include <stdexcept>
#include <utility> // 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<std::pair<std::string, std::string>> 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<std::pair<std::string, std::string>> { return fProperties; }
auto SetProps(std::vector<std::pair<std::string, std::string>> properties) -> void { fProperties = std::move(properties); }
private:
std::size_t fRequestId;
std::vector<std::pair<std::string, std::string>> 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<std::pair<std::string, std::string>> 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<std::pair<std::string, std::string>> { return fProperties; }
auto SetProps(std::vector<std::pair<std::string, std::string>> properties) -> void { fProperties = std::move(properties); }
private:
std::string fDeviceId;
std::size_t fRequestId;
Result fResult;
std::vector<std::pair<std::string, std::string>> 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<typename C, typename... Args>
std::unique_ptr<Cmd> make(Args&&... args)
{
return std::make_unique<C>(std::forward<Args>(args)...);
}
struct Cmds
{
using container = std::vector<std::unique_ptr<Cmd>>;
struct CommandFormatError : std::runtime_error { using std::runtime_error::runtime_error; };
explicit Cmds() = default;
template<typename... Rest>
explicit Cmds(std::unique_ptr<Cmd>&& first, Rest&&... rest)
{
Unpack(std::forward<std::unique_ptr<Cmd>&&>(first), std::forward<Rest>(rest)...);
}
void Add(std::unique_ptr<Cmd>&& cmd) { fCmds.emplace_back(std::move(cmd)); }
template<typename C, typename... Args>
void Add(Args&&... args)
{
static_assert(std::is_base_of<Cmd, C>::value, "Only types derived from fair::mq::cmd::Cmd are allowed");
Add(make<C>(std::forward<Args>(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 <class... Rest>
void Unpack(std::unique_ptr<Cmd>&& first, Rest&&... rest)
{
fCmds.emplace_back(std::move(first));
Unpack(std::forward<Rest>(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 */

View File

@ -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;

View File

@ -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 <string>
namespace fair::mq::sdk
{
namespace cmd {
constexpr auto commandsFormatDefFbs = R"(@commands_format_def_fbs@)";
} // namespace cmd
} // namespace fair::mq::sdk

View File

@ -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 <fairmq/sdk/commands/Commands.h>
#include <fairmq/States.h>
#include <fairmq/SDK.h>
#include <fairmq/tools/Strings.h>
#include <boost/program_options.hpp>
#include <termios.h> // raw mode console input
#include <unistd.h>
#include <condition_variable>
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
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<std::error_code, fair::mq::sdk::TopologyState> 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<string>(&sessionID)->default_value(envSessionId), "DDS Session ID (overrides any value in env var $DDS_SESSION_ID)");
} else {
options.add_options()("session,s", bpo::value<string>(&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<string>(&topoFile)->default_value(envTopoFile), "DDS topology file path");
} else {
options.add_options()("topology-file,f", bpo::value<string>(&topoFile)->required(), "DDS topology file path");
}
options.add_options()
("command,c", bpo::value<string>(&command)->default_value(""), "Command character")
("path,p", bpo::value<string>(&path)->default_value(""), "DDS Topology path to send command to (empty - send to all tasks)")
("property-key", bpo::value<string>(&pKey)->default_value(""), "property key to be used with 'p' command")
("property-value", bpo::value<string>(&pVal)->default_value(""), "property value to be used with 'p' command")
("wait-for-state,w", bpo::value<string>(&targetState)->default_value(""), "Wait until targeted FairMQ devices reach the given state")
("timeout,t", bpo::value<unsigned int>(&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;
}

View File

@ -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()

View File

@ -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}")

View File

@ -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 <gtest/gtest.h>
#include <fairmq/sdk/commands/Commands.h>
#include <string>
namespace
{
using namespace fair::mq;
using namespace fair::mq::sdk::cmd;
TEST(Format, Construction)
{
auto const props(std::vector<std::pair<std::string, std::string>>({{"k1", "v1"}, {"k2", "v2"}}));
Cmds checkStateCmds(make<CheckState>());
Cmds changeStateCmds(make<ChangeState>(Transition::Stop));
Cmds dumpConfigCmds(make<DumpConfig>());
Cmds subscribeToStateChangeCmds(make<SubscribeToStateChange>(60000));
Cmds unsubscribeFromStateChangeCmds(make<UnsubscribeFromStateChange>());
Cmds stateChangeExitingReceivedCmds(make<StateChangeExitingReceived>());
Cmds getPropertiesCmds(make<GetProperties>(66, "k[12]"));
Cmds setPropertiesCmds(make<SetProperties>(42, props));
Cmds subscriptionHeartbeatCmds(make<SubscriptionHeartbeat>(60000));
Cmds currentStateCmds(make<CurrentState>("somedeviceid", State::Running));
Cmds transitionStatusCmds(make<TransitionStatus>("somedeviceid", 123456, Result::Ok, Transition::Stop, State::Running));
Cmds configCmds(make<Config>("somedeviceid", "someconfig"));
Cmds stateChangeSubscriptionCmds(make<StateChangeSubscription>("somedeviceid", 123456, Result::Ok));
Cmds stateChangeUnsubscriptionCmds(make<StateChangeUnsubscription>("somedeviceid", 123456, Result::Ok));
Cmds stateChangeCmds(make<StateChange>("somedeviceid", 123456, State::Running, State::Ready));
Cmds propertiesCmds(make<Properties>("somedeviceid", 66, Result::Ok, props));
Cmds propertiesSetCmds(make<PropertiesSet>("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<ChangeState&>(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<SubscribeToStateChange&>(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<GetProperties&>(getPropertiesCmds.At(0)).GetRequestId(), 66);
ASSERT_EQ(static_cast<GetProperties&>(getPropertiesCmds.At(0)).GetQuery(), "k[12]");
ASSERT_EQ(setPropertiesCmds.At(0).GetType(), Type::set_properties);
ASSERT_EQ(static_cast<SetProperties&>(setPropertiesCmds.At(0)).GetRequestId(), 42);
ASSERT_EQ(static_cast<SetProperties&>(setPropertiesCmds.At(0)).GetProps(), props);
ASSERT_EQ(subscriptionHeartbeatCmds.At(0).GetType(), Type::subscription_heartbeat);
ASSERT_EQ(static_cast<SubscriptionHeartbeat&>(subscriptionHeartbeatCmds.At(0)).GetInterval(), 60000);
ASSERT_EQ(currentStateCmds.At(0).GetType(), Type::current_state);
ASSERT_EQ(static_cast<CurrentState&>(currentStateCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<CurrentState&>(currentStateCmds.At(0)).GetCurrentState(), State::Running);
ASSERT_EQ(transitionStatusCmds.At(0).GetType(), Type::transition_status);
ASSERT_EQ(static_cast<TransitionStatus&>(transitionStatusCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<TransitionStatus&>(transitionStatusCmds.At(0)).GetTaskId(), 123456);
ASSERT_EQ(static_cast<TransitionStatus&>(transitionStatusCmds.At(0)).GetResult(), Result::Ok);
ASSERT_EQ(static_cast<TransitionStatus&>(transitionStatusCmds.At(0)).GetTransition(), Transition::Stop);
ASSERT_EQ(static_cast<TransitionStatus&>(transitionStatusCmds.At(0)).GetCurrentState(), State::Running);
ASSERT_EQ(configCmds.At(0).GetType(), Type::config);
ASSERT_EQ(static_cast<Config&>(configCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<Config&>(configCmds.At(0)).GetConfig(), "someconfig");
ASSERT_EQ(stateChangeSubscriptionCmds.At(0).GetType(), Type::state_change_subscription);
ASSERT_EQ(static_cast<StateChangeSubscription&>(stateChangeSubscriptionCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<StateChangeSubscription&>(stateChangeSubscriptionCmds.At(0)).GetTaskId(), 123456);
ASSERT_EQ(static_cast<StateChangeSubscription&>(stateChangeSubscriptionCmds.At(0)).GetResult(), Result::Ok);
ASSERT_EQ(stateChangeUnsubscriptionCmds.At(0).GetType(), Type::state_change_unsubscription);
ASSERT_EQ(static_cast<StateChangeUnsubscription&>(stateChangeUnsubscriptionCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<StateChangeUnsubscription&>(stateChangeUnsubscriptionCmds.At(0)).GetTaskId(), 123456);
ASSERT_EQ(static_cast<StateChangeUnsubscription&>(stateChangeUnsubscriptionCmds.At(0)).GetResult(), Result::Ok);
ASSERT_EQ(stateChangeCmds.At(0).GetType(), Type::state_change);
ASSERT_EQ(static_cast<StateChange&>(stateChangeCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<StateChange&>(stateChangeCmds.At(0)).GetTaskId(), 123456);
ASSERT_EQ(static_cast<StateChange&>(stateChangeCmds.At(0)).GetLastState(), State::Running);
ASSERT_EQ(static_cast<StateChange&>(stateChangeCmds.At(0)).GetCurrentState(), State::Ready);
ASSERT_EQ(propertiesCmds.At(0).GetType(), Type::properties);
ASSERT_EQ(static_cast<Properties&>(propertiesCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<Properties&>(propertiesCmds.At(0)).GetRequestId(), 66);
ASSERT_EQ(static_cast<Properties&>(propertiesCmds.At(0)).GetResult(), Result::Ok);
ASSERT_EQ(static_cast<Properties&>(propertiesCmds.At(0)).GetProps(), props);
ASSERT_EQ(propertiesSetCmds.At(0).GetType(), Type::properties_set);
ASSERT_EQ(static_cast<PropertiesSet&>(propertiesSetCmds.At(0)).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<PropertiesSet&>(propertiesSetCmds.At(0)).GetRequestId(), 42);
ASSERT_EQ(static_cast<PropertiesSet&>(propertiesSetCmds.At(0)).GetResult(), Result::Ok);
}
void fillCommands(Cmds& cmds)
{
auto const props(std::vector<std::pair<std::string, std::string>>({{"k1", "v1"}, {"k2", "v2"}}));
cmds.Add<CheckState>();
cmds.Add<ChangeState>(Transition::Stop);
cmds.Add<DumpConfig>();
cmds.Add<SubscribeToStateChange>(60000);
cmds.Add<UnsubscribeFromStateChange>();
cmds.Add<StateChangeExitingReceived>();
cmds.Add<GetProperties>(66, "k[12]");
cmds.Add<SetProperties>(42, props);
cmds.Add<SubscriptionHeartbeat>(60000);
cmds.Add<CurrentState>("somedeviceid", State::Running);
cmds.Add<TransitionStatus>("somedeviceid", 123456, Result::Ok, Transition::Stop, State::Running);
cmds.Add<Config>("somedeviceid", "someconfig");
cmds.Add<StateChangeSubscription>("somedeviceid", 123456, Result::Ok);
cmds.Add<StateChangeUnsubscription>("somedeviceid", 123456, Result::Ok);
cmds.Add<StateChange>("somedeviceid", 123456, State::Running, State::Ready);
cmds.Add<Properties>("somedeviceid", 66, Result::Ok, props);
cmds.Add<PropertiesSet>("somedeviceid", 42, Result::Ok);
}
void checkCommands(Cmds& cmds)
{
ASSERT_EQ(cmds.Size(), 17);
int count = 0;
auto const props(std::vector<std::pair<std::string, std::string>>({{"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<ChangeState&>(*cmd).GetTransition(), Transition::Stop);
break;
case Type::dump_config:
++count;
break;
case Type::subscribe_to_state_change:
++count;
ASSERT_EQ(static_cast<SubscribeToStateChange&>(*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<GetProperties&>(*cmd).GetRequestId(), 66);
ASSERT_EQ(static_cast<GetProperties&>(*cmd).GetQuery(), "k[12]");
break;
case Type::set_properties:
++count;
ASSERT_EQ(static_cast<SetProperties&>(*cmd).GetRequestId(), 42);
ASSERT_EQ(static_cast<SetProperties&>(*cmd).GetProps(), props);
break;
case Type::subscription_heartbeat:
++count;
ASSERT_EQ(static_cast<SubscriptionHeartbeat&>(*cmd).GetInterval(), 60000);
break;
case Type::current_state:
++count;
ASSERT_EQ(static_cast<CurrentState&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<CurrentState&>(*cmd).GetCurrentState(), State::Running);
break;
case Type::transition_status:
++count;
ASSERT_EQ(static_cast<TransitionStatus&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<TransitionStatus&>(*cmd).GetTaskId(), 123456);
ASSERT_EQ(static_cast<TransitionStatus&>(*cmd).GetResult(), Result::Ok);
ASSERT_EQ(static_cast<TransitionStatus&>(*cmd).GetTransition(), Transition::Stop);
ASSERT_EQ(static_cast<TransitionStatus&>(*cmd).GetCurrentState(), State::Running);
break;
case Type::config:
++count;
ASSERT_EQ(static_cast<Config&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<Config&>(*cmd).GetConfig(), "someconfig");
break;
case Type::state_change_subscription:
++count;
ASSERT_EQ(static_cast<StateChangeSubscription&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<StateChangeSubscription&>(*cmd).GetTaskId(), 123456);
ASSERT_EQ(static_cast<StateChangeSubscription&>(*cmd).GetResult(), Result::Ok);
break;
case Type::state_change_unsubscription:
++count;
ASSERT_EQ(static_cast<StateChangeUnsubscription&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<StateChangeUnsubscription&>(*cmd).GetTaskId(), 123456);
ASSERT_EQ(static_cast<StateChangeUnsubscription&>(*cmd).GetResult(), Result::Ok);
break;
case Type::state_change:
++count;
ASSERT_EQ(static_cast<StateChange&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<StateChange&>(*cmd).GetTaskId(), 123456);
ASSERT_EQ(static_cast<StateChange&>(*cmd).GetLastState(), State::Running);
ASSERT_EQ(static_cast<StateChange&>(*cmd).GetCurrentState(), State::Ready);
break;
case Type::properties:
++count;
ASSERT_EQ(static_cast<Properties&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<Properties&>(*cmd).GetRequestId(), 66);
ASSERT_EQ(static_cast<Properties&>(*cmd).GetResult(), Result::Ok);
ASSERT_EQ(static_cast<Properties&>(*cmd).GetProps(), props);
break;
case Type::properties_set:
++count;
ASSERT_EQ(static_cast<PropertiesSet&>(*cmd).GetDeviceId(), "somedeviceid");
ASSERT_EQ(static_cast<PropertiesSet&>(*cmd).GetRequestId(), 42);
ASSERT_EQ(static_cast<PropertiesSet&>(*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

View File

@ -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 <fairmq/SDK.h>
#include <fairmq/tools/Strings.h>
#include <fairlogger/Logger.h>
#include <asio/io_context.hpp>
#include <gtest/gtest.h>
#include <algorithm> // for_each
#include <array>
#include <chrono>
#include <cstdlib>
#include <thread>
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<sdk::DDSAgent> agents = mDDSSession.RequestAgentInfo();
LOG(debug) << "##### AgentInfo:";
LOG(debug) << "size: " << agents.size();
for (const auto& a : agents) {
LOG(debug) << a;
}
std::vector<sdk::DDSTask> tasks = mDDSSession.RequestTaskInfo();
LOG(debug) << "##### TaskInfo:";
LOG(debug) << "size: " << tasks.size();
for (const auto& t : tasks) {
LOG(debug) << t;
}
std::vector<sdk::DDSCollection> 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<sdk::DDSAgent> agents = mDDSSessions[i].RequestAgentInfo();
LOG(info) << "##### AgentInfo:";
LOG(info) << "size: " << agents.size();
for (const auto& a : agents) {
LOG(info) << a;
}
std::vector<sdk::DDSTask> tasks = mDDSSessions[i].RequestTaskInfo();
LOG(info) << "##### TaskInfo:";
LOG(info) << "size: " << tasks.size();
for (const auto& t : tasks) {
LOG(info) << t;
}
std::vector<sdk::DDSCollection> 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<sdk::DDSSession, mNumSessions> mDDSSessions;
std::array<sdk::DDSTopology, mNumSessions> 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 */

View File

@ -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 <fairmq/sdk/AsioBase.h>
#include <fairmq/sdk/AsioAsyncOp.h>
#include <asio/steady_timer.hpp>
#include <iostream>
#include <thread>
namespace {
using AsyncOp = fair::mq::test::AsyncOpFixture;
// template <typename Executor, typename Allocator>
// class : public AsioBase<Executor, Allocator>
TEST_F(AsyncOp, DefaultConstruction)
{
using namespace fair::mq::sdk;
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code, int)> op;
EXPECT_TRUE(op.IsCompleted());
}
TEST_F(AsyncOp, ConstructionWithHandler)
{
using namespace fair::mq::sdk;
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code, int)> op(
[](std::error_code, int) {});
EXPECT_FALSE(op.IsCompleted());
}
TEST_F(AsyncOp, Complete)
{
using namespace fair::mq::sdk;
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code, int)> 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<sdk::DefaultExecutor, sdk::DefaultAllocator, void(std::error_code)> 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<sdk::DefaultExecutor, sdk::DefaultAllocator, void(std::error_code)> 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<DefaultExecutor, DefaultAllocator, void(std::error_code)> 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

View File

@ -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 <dds/dds.h>
#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<dds::tools_api::CSession>());
nativeSession->create();
fair::mq::sdk::DDSSession session(nativeSession, env);
session.StopOnDestruction();
LOG(debug) << session;
session.RequestCommanderInfo();
}
} // namespace

View File

@ -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 <asio.hpp>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <dds/dds.h>
#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <fairmq/sdk/Topology.h>
#include <fairmq/tools/Strings.h>
#include <fairmq/tools/Semaphore.h>
#include <thread>
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<dds::tools_api::CSession>());
nativeSession->create();
EXPECT_THROW(sdk::MakeTopology(nativeTopo, nativeSession, env), sdk::RuntimeError);
nativeSession->shutdown();
}
TEST_F(MultipleTopologies, Construction)
{
using namespace fair::mq;
std::array<sdk::Topology, mNumSessions> topos{
sdk::Topology(mDDSTopologies[0], mDDSSessions[0]),
sdk::Topology(mDDSTopologies[1], mDDSSessions[1])
};
}
TEST_F(MultipleTopologies, ChangeStateFullDeviceLifecycle)
{
using namespace fair::mq;
std::array<sdk::Topology, mNumSessions> 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<sdk::Topology, mNumSessions> 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<void> {
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<sdk::AggregatedTopologyState>(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

View File

@ -1,42 +0,0 @@
<topology name="sdk-test-topo">
<property name="fmqchan_data" />
<declrequirement name="SamplerWorker" type="wnname" value="sampler"/>
<declrequirement name="SinkWorker" type="wnname" value="sink"/>
<decltask name="Sampler">
<exe reachable="true">fairmq-bsampler --color false --channel-config name=data,type=push,method=bind -P dds --msg-rate 10 --severity trace --verbosity veryhigh</exe>
<requirements>
<name>SamplerWorker</name>
</requirements>
<properties>
<name access="write">fmqchan_data</name>
</properties>
</decltask>
<decltask name="Sink">
<exe reachable="true">fairmq-sink --color false --channel-config name=data,type=pull,method=connect -P dds --severity trace --verbosity veryhigh</exe>
<requirements>
<name>SinkWorker</name>
</requirements>
<properties>
<name access="read">fmqchan_data</name>
</properties>
</decltask>
<declcollection name="Sinks">
<tasks>
<name>Sink</name>
</tasks>
</declcollection>
<main name="main">
<task>Sampler</task>
<group name="SinkGroup" n="5">
<collection>Sinks</collection>
</group>
</main>
</topology>