From 4de53531d74749202b08a4d6ce4bcdc210c830f9 Mon Sep 17 00:00:00 2001 From: Javier Gil Aviles Date: Thu, 16 Jan 2025 09:47:34 +0100 Subject: [PATCH] Uncrust test Signed-off-by: Javier Gil Aviles --- .../include/sustainml_cpp/core/Node.hpp | 10 + .../core/RequestReplyListener.hpp | 68 +++++ .../nodes/AppRequirementsNode.hpp | 9 + .../nodes/CarbonFootprintNode.hpp | 9 + .../nodes/HardwareConstraintsNode.hpp | 9 + .../nodes/HardwareResourcesNode.hpp | 9 + .../nodes/MLModelMetadataNode.hpp | 9 + .../sustainml_cpp/nodes/MLModelNode.hpp | 9 + sustainml_cpp/src/cpp/core/Node.cpp | 150 ++++++----- sustainml_cpp/src/cpp/core/NodeImpl.cpp | 39 +++ sustainml_cpp/src/cpp/core/NodeImpl.hpp | 15 ++ .../src/cpp/nodes/AppRequirementsNode.cpp | 31 +++ .../src/cpp/nodes/CarbonFootprintNode.cpp | 31 +++ .../src/cpp/nodes/HardwareConstraintsNode.cpp | 30 +++ .../src/cpp/nodes/HardwareResourcesNode.cpp | 31 +++ .../src/cpp/nodes/MLModelMetadataNode.cpp | 30 +++ sustainml_cpp/src/cpp/nodes/MLModelNode.cpp | 31 +++ .../test/blackbox/api/ManagedNodeService.hpp | 177 +++++++++++++ .../test/blackbox/api/TaskInjector.hpp | 13 +- .../test/blackbox/api/TaskReceiver.hpp | 243 ++++++++++++++++++ .../test/blackbox/common/BlackboxTests.hpp | 32 +++ .../common/BlackboxTestsResponseNodes.cpp | 213 +++++++++++++++ 22 files changed, 1131 insertions(+), 67 deletions(-) create mode 100644 sustainml_cpp/include/sustainml_cpp/core/RequestReplyListener.hpp create mode 100644 sustainml_cpp/test/blackbox/api/ManagedNodeService.hpp create mode 100644 sustainml_cpp/test/blackbox/api/TaskReceiver.hpp create mode 100644 sustainml_cpp/test/blackbox/common/BlackboxTestsResponseNodes.cpp diff --git a/sustainml_cpp/include/sustainml_cpp/core/Node.hpp b/sustainml_cpp/include/sustainml_cpp/core/Node.hpp index 08efe6f..f768dc3 100644 --- a/sustainml_cpp/include/sustainml_cpp/core/Node.hpp +++ b/sustainml_cpp/include/sustainml_cpp/core/Node.hpp @@ -50,6 +50,7 @@ namespace core { class NodeImpl; class NodeControlListener; class Dispatcher; +class RequestReplyListener; struct Options; /** @@ -76,6 +77,15 @@ class Node const std::string& name, const Options& opts); + SUSTAINML_CPP_DLL_API Node( + const std::string& name, + RequestReplyListener& req_res_listener); + + SUSTAINML_CPP_DLL_API Node( + const std::string& name, + const Options& opts, + RequestReplyListener& req_res_listener); + SUSTAINML_CPP_DLL_API virtual ~Node(); /** diff --git a/sustainml_cpp/include/sustainml_cpp/core/RequestReplyListener.hpp b/sustainml_cpp/include/sustainml_cpp/core/RequestReplyListener.hpp new file mode 100644 index 0000000..dfbe12a --- /dev/null +++ b/sustainml_cpp/include/sustainml_cpp/core/RequestReplyListener.hpp @@ -0,0 +1,68 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file RequestReplyListener.hpp + */ + +#ifndef SUSTAINMLCPP_CORE_REQUESTREPLEIR_HPP +#define SUSTAINMLCPP_CORE_REQUESTREPLEIR_HPP + +#include + +namespace sustainml { +namespace core { + +/** + * @brief This class implement a virtual function for the configuration of each node. + * Each node will need to inherit it and implement their own. + * End user to implement specific callbacks to certain actions. + * + */ +class RequestReplyListener{ + +public: + + /** + * @brief Constructor + */ + RequestReplyListener() + { + } + + /** + * @brief Destructor + */ + ~RequestReplyListener() + { + } + + /** + * Virtual method to be called when a configuration request is received. + * + * @param req The request message + * @param res The response message to send + */ + virtual void on_configuration_request( + types::RequestType& req, + types::ResponseType& res) + { + } + +}; + +} // namespace core +} // namespace sustainml + +#endif // SUSTAINMLCPP_CORE_REQUESTREPLEIR_HPP diff --git a/sustainml_cpp/include/sustainml_cpp/nodes/AppRequirementsNode.hpp b/sustainml_cpp/include/sustainml_cpp/nodes/AppRequirementsNode.hpp index a7ba284..bdc120c 100644 --- a/sustainml_cpp/include/sustainml_cpp/nodes/AppRequirementsNode.hpp +++ b/sustainml_cpp/include/sustainml_cpp/nodes/AppRequirementsNode.hpp @@ -84,10 +84,19 @@ class AppRequirementsNode : public ::sustainml::core::Node SUSTAINML_CPP_DLL_API AppRequirementsNode( AppRequirementsTaskListener& user_listener); + SUSTAINML_CPP_DLL_API AppRequirementsNode( + AppRequirementsTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener); + #ifndef SWIG_WRAPPER SUSTAINML_CPP_DLL_API AppRequirementsNode( AppRequirementsTaskListener& user_listener, sustainml::core::Options opts); + + SUSTAINML_CPP_DLL_API AppRequirementsNode( + AppRequirementsTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener); #endif // SWIG_WRAPPER SUSTAINML_CPP_DLL_API virtual ~AppRequirementsNode(); diff --git a/sustainml_cpp/include/sustainml_cpp/nodes/CarbonFootprintNode.hpp b/sustainml_cpp/include/sustainml_cpp/nodes/CarbonFootprintNode.hpp index 449f610..10762aa 100644 --- a/sustainml_cpp/include/sustainml_cpp/nodes/CarbonFootprintNode.hpp +++ b/sustainml_cpp/include/sustainml_cpp/nodes/CarbonFootprintNode.hpp @@ -91,10 +91,19 @@ class CarbonFootprintNode : public ::sustainml::core::Node SUSTAINML_CPP_DLL_API CarbonFootprintNode( CarbonFootprintTaskListener& listener); + SUSTAINML_CPP_DLL_API CarbonFootprintNode( + CarbonFootprintTaskListener& listener, + sustainml::core::RequestReplyListener& req_res_listener); + #ifndef SWIG_WRAPPER SUSTAINML_CPP_DLL_API CarbonFootprintNode( CarbonFootprintTaskListener& listener, sustainml::core::Options opts); + + SUSTAINML_CPP_DLL_API CarbonFootprintNode( + CarbonFootprintTaskListener& listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener); #endif // SWIG_WRAPPER SUSTAINML_CPP_DLL_API virtual ~CarbonFootprintNode(); diff --git a/sustainml_cpp/include/sustainml_cpp/nodes/HardwareConstraintsNode.hpp b/sustainml_cpp/include/sustainml_cpp/nodes/HardwareConstraintsNode.hpp index 1770c8b..0666f68 100644 --- a/sustainml_cpp/include/sustainml_cpp/nodes/HardwareConstraintsNode.hpp +++ b/sustainml_cpp/include/sustainml_cpp/nodes/HardwareConstraintsNode.hpp @@ -84,10 +84,19 @@ class HardwareConstraintsNode : public ::sustainml::core::Node SUSTAINML_CPP_DLL_API HardwareConstraintsNode( HardwareConstraintsTaskListener& user_listener); + SUSTAINML_CPP_DLL_API HardwareConstraintsNode( + HardwareConstraintsTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener); + #ifndef SWIG_WRAPPER SUSTAINML_CPP_DLL_API HardwareConstraintsNode( HardwareConstraintsTaskListener& user_listener, sustainml::core::Options opts); + + SUSTAINML_CPP_DLL_API HardwareConstraintsNode( + HardwareConstraintsTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener); #endif // SWIG_WRAPPER SUSTAINML_CPP_DLL_API virtual ~HardwareConstraintsNode(); diff --git a/sustainml_cpp/include/sustainml_cpp/nodes/HardwareResourcesNode.hpp b/sustainml_cpp/include/sustainml_cpp/nodes/HardwareResourcesNode.hpp index 4dbafba..f0e6983 100644 --- a/sustainml_cpp/include/sustainml_cpp/nodes/HardwareResourcesNode.hpp +++ b/sustainml_cpp/include/sustainml_cpp/nodes/HardwareResourcesNode.hpp @@ -88,10 +88,19 @@ class HardwareResourcesNode : public ::sustainml::core::Node SUSTAINML_CPP_DLL_API HardwareResourcesNode( HardwareResourcesTaskListener& user_listener); + SUSTAINML_CPP_DLL_API HardwareResourcesNode( + HardwareResourcesTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener); + #ifndef SWIG_WRAPPER SUSTAINML_CPP_DLL_API HardwareResourcesNode( HardwareResourcesTaskListener& user_listener, sustainml::core::Options opts); + + SUSTAINML_CPP_DLL_API HardwareResourcesNode( + HardwareResourcesTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener); #endif // SWIG_WRAPPER SUSTAINML_CPP_DLL_API virtual ~HardwareResourcesNode(); diff --git a/sustainml_cpp/include/sustainml_cpp/nodes/MLModelMetadataNode.hpp b/sustainml_cpp/include/sustainml_cpp/nodes/MLModelMetadataNode.hpp index 0357054..16a31c6 100644 --- a/sustainml_cpp/include/sustainml_cpp/nodes/MLModelMetadataNode.hpp +++ b/sustainml_cpp/include/sustainml_cpp/nodes/MLModelMetadataNode.hpp @@ -84,10 +84,19 @@ class MLModelMetadataNode : public ::sustainml::core::Node SUSTAINML_CPP_DLL_API MLModelMetadataNode( MLModelMetadataTaskListener& user_listener); + SUSTAINML_CPP_DLL_API MLModelMetadataNode( + MLModelMetadataTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener); + #ifndef SWIG_WRAPPER SUSTAINML_CPP_DLL_API MLModelMetadataNode( MLModelMetadataTaskListener& user_listener, sustainml::core::Options opts); + + SUSTAINML_CPP_DLL_API MLModelMetadataNode( + MLModelMetadataTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener); #endif // SWIG_WRAPPER SUSTAINML_CPP_DLL_API virtual ~MLModelMetadataNode(); diff --git a/sustainml_cpp/include/sustainml_cpp/nodes/MLModelNode.hpp b/sustainml_cpp/include/sustainml_cpp/nodes/MLModelNode.hpp index 04baf0e..3c87ce1 100644 --- a/sustainml_cpp/include/sustainml_cpp/nodes/MLModelNode.hpp +++ b/sustainml_cpp/include/sustainml_cpp/nodes/MLModelNode.hpp @@ -94,10 +94,19 @@ class MLModelNode : public ::sustainml::core::Node SUSTAINML_CPP_DLL_API MLModelNode( MLModelTaskListener& user_listener); + SUSTAINML_CPP_DLL_API MLModelNode( + MLModelTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener); + #ifndef SWIG_WRAPPER SUSTAINML_CPP_DLL_API MLModelNode( MLModelTaskListener& user_listener, sustainml::core::Options opts); + + SUSTAINML_CPP_DLL_API MLModelNode( + MLModelTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener); #endif // SWIG_WRAPPER SUSTAINML_CPP_DLL_API virtual ~MLModelNode(); diff --git a/sustainml_cpp/src/cpp/core/Node.cpp b/sustainml_cpp/src/cpp/core/Node.cpp index 1f821a5..c44ca83 100644 --- a/sustainml_cpp/src/cpp/core/Node.cpp +++ b/sustainml_cpp/src/cpp/core/Node.cpp @@ -26,78 +26,96 @@ using namespace eprosima::fastdds::dds; namespace sustainml { namespace core { - Node::Node(const std::string &name) - { - impl_ = new NodeImpl(this, name); - } - - Node::Node(const std::string &name, - const Options& opts) - { - impl_ = new NodeImpl(this, name, opts); - } - - Node::~Node() - { - delete impl_; - } - - void Node::spin() - { - impl_->spin(); - } - - bool Node::initialize_subscription( +Node::Node( + const std::string& name) +{ + impl_ = new NodeImpl(this, name); +} + +Node::Node( + const std::string& name, + const Options& opts) +{ + impl_ = new NodeImpl(this, name, opts); +} + +Node::Node( + const std::string& name, + RequestReplyListener& req_res_listener) +{ + impl_ = new NodeImpl(this, name, req_res_listener); +} + +Node::Node( + const std::string& name, + const Options& opts, + RequestReplyListener& req_res_listener) +{ + impl_ = new NodeImpl(this, name, opts, req_res_listener); +} + +Node::~Node() +{ + delete impl_; +} + +void Node::spin() +{ + impl_->spin(); +} + +bool Node::initialize_subscription( const char* topic_name, const char* type_name, eprosima::fastdds::dds::DataReaderListener* listener, - const Options &opts) - { - return impl_->initialize_subscription(topic_name, type_name, listener, opts); - } + const Options& opts) +{ + return impl_->initialize_subscription(topic_name, type_name, listener, opts); +} - bool Node::initialize_publication( +bool Node::initialize_publication( const char* topic_name, const char* type_name, - const Options &opts) - { - return impl_->initialize_publication(topic_name, type_name, opts); - } - - void Node::publish_node_status() - { - impl_->publish_node_status(); - } - - void Node::terminate() - { - NodeImpl::terminate(); - } - - const std::string& Node::name() - { - return impl_->node_status_.node_name(); - } - - const Status& Node::status() - { - return impl_->node_status_.node_status(); - } - - void Node::status(const Status &status) - { - impl_->node_status_.node_status(status); - } - - std::weak_ptr Node::get_dispatcher() - { - return impl_->get_dispatcher(); - } - - const std::vector& Node::writers() - { - return impl_->writers_; - } + const Options& opts) +{ + return impl_->initialize_publication(topic_name, type_name, opts); +} + +void Node::publish_node_status() +{ + impl_->publish_node_status(); +} + +void Node::terminate() +{ + NodeImpl::terminate(); +} + +const std::string& Node::name() +{ + return impl_->node_status_.node_name(); +} + +const Status& Node::status() +{ + return impl_->node_status_.node_status(); +} + +void Node::status( + const Status& status) +{ + impl_->node_status_.node_status(status); +} + +std::weak_ptr Node::get_dispatcher() +{ + return impl_->get_dispatcher(); +} + +const std::vector& Node::writers() +{ + return impl_->writers_; +} } // namespace core } // namespace sustainml diff --git a/sustainml_cpp/src/cpp/core/NodeImpl.cpp b/sustainml_cpp/src/cpp/core/NodeImpl.cpp index bd3bac0..eeceda8 100644 --- a/sustainml_cpp/src/cpp/core/NodeImpl.cpp +++ b/sustainml_cpp/src/cpp/core/NodeImpl.cpp @@ -45,6 +45,7 @@ NodeImpl::NodeImpl( , publisher_(nullptr) , subscriber_(nullptr) , control_listener_(this) + , req_res_listener_(*new RequestReplyListener()) { if (!init(name)) { @@ -62,6 +63,44 @@ NodeImpl::NodeImpl( , publisher_(nullptr) , subscriber_(nullptr) , control_listener_(this) + , req_res_listener_(*new RequestReplyListener()) +{ + if (!init(name, opts)) + { + EPROSIMA_LOG_ERROR(NODE, "Initialization Failed with the provided Options"); + } +} + +NodeImpl::NodeImpl( + Node* node, + const std::string& name, + RequestReplyListener& req_res_listener) + : node_(node) + , dispatcher_(new Dispatcher(node_)) + , participant_(nullptr) + , publisher_(nullptr) + , subscriber_(nullptr) + , control_listener_(this) + , req_res_listener_(req_res_listener) +{ + if (!init(name)) + { + EPROSIMA_LOG_ERROR(NODE, "Initialization Failed with the provided Options"); + } +} + +NodeImpl::NodeImpl( + Node* node, + const std::string& name, + const Options& opts, + RequestReplyListener& req_res_listener) + : node_(node) + , dispatcher_(new Dispatcher(node_)) + , participant_(nullptr) + , publisher_(nullptr) + , subscriber_(nullptr) + , control_listener_(this) + , req_res_listener_(req_res_listener) { if (!init(name, opts)) { diff --git a/sustainml_cpp/src/cpp/core/NodeImpl.hpp b/sustainml_cpp/src/cpp/core/NodeImpl.hpp index 6f3b40e..3ece340 100644 --- a/sustainml_cpp/src/cpp/core/NodeImpl.hpp +++ b/sustainml_cpp/src/cpp/core/NodeImpl.hpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -52,6 +53,7 @@ namespace core { class Dispatcher; class Node; +class RequestReplyListener; struct Options; /** @@ -81,6 +83,17 @@ class NodeImpl const std::string& name, const Options& opts); + NodeImpl( + Node* node, + const std::string& name, + RequestReplyListener& req_res_listener); + + NodeImpl( + Node* node, + const std::string& name, + const Options& opts, + RequestReplyListener& req_res_listener); + ~NodeImpl(); /** @@ -153,6 +166,8 @@ class NodeImpl NodeStatusImpl node_status_; + RequestReplyListener& req_res_listener_; + private: /** diff --git a/sustainml_cpp/src/cpp/nodes/AppRequirementsNode.cpp b/sustainml_cpp/src/cpp/nodes/AppRequirementsNode.cpp index b815ece..edb1dac 100644 --- a/sustainml_cpp/src/cpp/nodes/AppRequirementsNode.cpp +++ b/sustainml_cpp/src/cpp/nodes/AppRequirementsNode.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include using namespace types; @@ -49,6 +50,26 @@ AppRequirementsNode::AppRequirementsNode( init(opts); } +AppRequirementsNode::AppRequirementsNode( + AppRequirementsTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::APP_REQUIREMENTS_NODE, req_res_listener) + , user_listener_(user_listener) +{ + sustainml::core::Options opts; + opts.rqos.resource_limits().max_instances = 500; + opts.rqos.resource_limits().max_samples_per_instance = 1; + opts.rqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + opts.rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + opts.rqos.history().kind = eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS; + opts.rqos.history().depth = 1; + + opts.wqos.resource_limits().max_instances = 500; + opts.wqos.resource_limits().max_samples_per_instance = 1; + opts.wqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + init(opts); +} + AppRequirementsNode::AppRequirementsNode( AppRequirementsTaskListener& user_listener, sustainml::core::Options opts) @@ -58,6 +79,16 @@ AppRequirementsNode::AppRequirementsNode( init(opts); } +AppRequirementsNode::AppRequirementsNode( + AppRequirementsTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::APP_REQUIREMENTS_NODE, opts, req_res_listener) + , user_listener_(user_listener) +{ + init(opts); +} + AppRequirementsNode::~AppRequirementsNode() { diff --git a/sustainml_cpp/src/cpp/nodes/CarbonFootprintNode.cpp b/sustainml_cpp/src/cpp/nodes/CarbonFootprintNode.cpp index 72c592d..32ef887 100644 --- a/sustainml_cpp/src/cpp/nodes/CarbonFootprintNode.cpp +++ b/sustainml_cpp/src/cpp/nodes/CarbonFootprintNode.cpp @@ -50,6 +50,27 @@ CarbonFootprintNode::CarbonFootprintNode( init(opts); } +CarbonFootprintNode::CarbonFootprintNode( + CarbonFootprintTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::CARBON_FOOTPRINT_NODE, req_res_listener) + , user_listener_(user_listener) +{ + sustainml::core::Options opts; + opts.rqos.resource_limits().max_instances = 500; + opts.rqos.resource_limits().max_samples_per_instance = 1; + opts.rqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + opts.rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + opts.rqos.history().kind = eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS; + opts.rqos.history().depth = 1; + + opts.wqos.resource_limits().max_instances = 500; + opts.wqos.resource_limits().max_samples_per_instance = 1; + opts.wqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + + init(opts); +} + CarbonFootprintNode::CarbonFootprintNode( CarbonFootprintTaskListener& user_listener, sustainml::core::Options opts) @@ -59,6 +80,16 @@ CarbonFootprintNode::CarbonFootprintNode( init(opts); } +CarbonFootprintNode::CarbonFootprintNode( + CarbonFootprintTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::CARBON_FOOTPRINT_NODE, opts, req_res_listener) + , user_listener_(user_listener) +{ + init(opts); +} + CarbonFootprintNode::~CarbonFootprintNode() { diff --git a/sustainml_cpp/src/cpp/nodes/HardwareConstraintsNode.cpp b/sustainml_cpp/src/cpp/nodes/HardwareConstraintsNode.cpp index c74157f..08a2b42 100644 --- a/sustainml_cpp/src/cpp/nodes/HardwareConstraintsNode.cpp +++ b/sustainml_cpp/src/cpp/nodes/HardwareConstraintsNode.cpp @@ -49,6 +49,26 @@ HardwareConstraintsNode::HardwareConstraintsNode( init(opts); } +HardwareConstraintsNode::HardwareConstraintsNode( + HardwareConstraintsTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::HW_CONSTRAINTS_NODE, req_res_listener) + , user_listener_(user_listener) +{ + sustainml::core::Options opts; + opts.rqos.resource_limits().max_instances = 500; + opts.rqos.resource_limits().max_samples_per_instance = 1; + opts.rqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + opts.rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + opts.rqos.history().kind = eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS; + opts.rqos.history().depth = 1; + + opts.wqos.resource_limits().max_instances = 500; + opts.wqos.resource_limits().max_samples_per_instance = 1; + opts.wqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + init(opts); +} + HardwareConstraintsNode::HardwareConstraintsNode( HardwareConstraintsTaskListener& user_listener, sustainml::core::Options opts) @@ -58,6 +78,16 @@ HardwareConstraintsNode::HardwareConstraintsNode( init(opts); } +HardwareConstraintsNode::HardwareConstraintsNode( + HardwareConstraintsTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::HW_CONSTRAINTS_NODE, opts, req_res_listener) + , user_listener_(user_listener) +{ + init(opts); +} + HardwareConstraintsNode::~HardwareConstraintsNode() { diff --git a/sustainml_cpp/src/cpp/nodes/HardwareResourcesNode.cpp b/sustainml_cpp/src/cpp/nodes/HardwareResourcesNode.cpp index 78096e3..b6ef1fb 100644 --- a/sustainml_cpp/src/cpp/nodes/HardwareResourcesNode.cpp +++ b/sustainml_cpp/src/cpp/nodes/HardwareResourcesNode.cpp @@ -50,6 +50,27 @@ HardwareResourcesNode::HardwareResourcesNode( init(opts); } +HardwareResourcesNode::HardwareResourcesNode( + HardwareResourcesTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::HW_RESOURCES_NODE, req_res_listener) + , user_listener_(user_listener) +{ + sustainml::core::Options opts; + opts.rqos.resource_limits().max_instances = 500; + opts.rqos.resource_limits().max_samples_per_instance = 1; + opts.rqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + opts.rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + opts.rqos.history().kind = eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS; + opts.rqos.history().depth = 1; + + opts.wqos.resource_limits().max_instances = 500; + opts.wqos.resource_limits().max_samples_per_instance = 1; + opts.wqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + + init(opts); +} + HardwareResourcesNode::HardwareResourcesNode( HardwareResourcesTaskListener& user_listener, sustainml::core::Options opts) @@ -59,6 +80,16 @@ HardwareResourcesNode::HardwareResourcesNode( init(opts); } +HardwareResourcesNode::HardwareResourcesNode( + HardwareResourcesTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::HW_RESOURCES_NODE, opts, req_res_listener) + , user_listener_(user_listener) +{ + init(opts); +} + HardwareResourcesNode::~HardwareResourcesNode() { diff --git a/sustainml_cpp/src/cpp/nodes/MLModelMetadataNode.cpp b/sustainml_cpp/src/cpp/nodes/MLModelMetadataNode.cpp index 2f591c0..9ac69ca 100644 --- a/sustainml_cpp/src/cpp/nodes/MLModelMetadataNode.cpp +++ b/sustainml_cpp/src/cpp/nodes/MLModelMetadataNode.cpp @@ -49,6 +49,26 @@ MLModelMetadataNode::MLModelMetadataNode( init(opts); } +MLModelMetadataNode::MLModelMetadataNode( + MLModelMetadataTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::ML_MODEL_METADATA_NODE, req_res_listener) + , user_listener_(user_listener) +{ + sustainml::core::Options opts; + opts.rqos.resource_limits().max_instances = 500; + opts.rqos.resource_limits().max_samples_per_instance = 1; + opts.rqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + opts.rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + opts.rqos.history().kind = eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS; + opts.rqos.history().depth = 1; + + opts.wqos.resource_limits().max_instances = 500; + opts.wqos.resource_limits().max_samples_per_instance = 1; + opts.wqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + init(opts); +} + MLModelMetadataNode::MLModelMetadataNode( MLModelMetadataTaskListener& user_listener, sustainml::core::Options opts) @@ -58,6 +78,16 @@ MLModelMetadataNode::MLModelMetadataNode( init(opts); } +MLModelMetadataNode::MLModelMetadataNode( + MLModelMetadataTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::ML_MODEL_METADATA_NODE, opts, req_res_listener) + , user_listener_(user_listener) +{ + init(opts); +} + MLModelMetadataNode::~MLModelMetadataNode() { diff --git a/sustainml_cpp/src/cpp/nodes/MLModelNode.cpp b/sustainml_cpp/src/cpp/nodes/MLModelNode.cpp index 8fb773e..22d893f 100644 --- a/sustainml_cpp/src/cpp/nodes/MLModelNode.cpp +++ b/sustainml_cpp/src/cpp/nodes/MLModelNode.cpp @@ -50,6 +50,27 @@ MLModelNode::MLModelNode( init(opts); } +MLModelNode::MLModelNode( + MLModelTaskListener& user_listener, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::ML_MODEL_NODE, req_res_listener) + , user_listener_(user_listener) +{ + sustainml::core::Options opts; + opts.rqos.resource_limits().max_instances = 500; + opts.rqos.resource_limits().max_samples_per_instance = 1; + opts.rqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + opts.rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + opts.rqos.history().kind = eprosima::fastdds::dds::KEEP_LAST_HISTORY_QOS; + opts.rqos.history().depth = 1; + + opts.wqos.resource_limits().max_instances = 500; + opts.wqos.resource_limits().max_samples_per_instance = 1; + opts.wqos.durability().kind = eprosima::fastdds::dds::TRANSIENT_LOCAL_DURABILITY_QOS; + + init(opts); +} + MLModelNode::MLModelNode( MLModelTaskListener& user_listener, sustainml::core::Options opts) @@ -59,6 +80,16 @@ MLModelNode::MLModelNode( init(opts); } +MLModelNode::MLModelNode( + MLModelTaskListener& user_listener, + sustainml::core::Options opts, + sustainml::core::RequestReplyListener& req_res_listener) + : Node(common::ML_MODEL_NODE, opts, req_res_listener) + , user_listener_(user_listener) +{ + init(opts); +} + MLModelNode::~MLModelNode() { diff --git a/sustainml_cpp/test/blackbox/api/ManagedNodeService.hpp b/sustainml_cpp/test/blackbox/api/ManagedNodeService.hpp new file mode 100644 index 0000000..4af007a --- /dev/null +++ b/sustainml_cpp/test/blackbox/api/ManagedNodeService.hpp @@ -0,0 +1,177 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file ManagedNodeService.hpp + * + */ + +#ifndef _TEST_BLACKBOX_MANAGEDNODESERVICE_HPP_ +#define _TEST_BLACKBOX_MANAGEDNODESERVICE_HPP_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +template +class ManagedNodeService +{ + using functor_t = std::function; + friend class ManagedNodeServiceListener; + friend class ManagedRequestReplyListener; + + struct ManagedNodeServiceListener : public _LISTENER_TYPE + { + ManagedNodeServiceListener( + ManagedNodeService* parent, + const functor_t& functor = nullptr) + : functor_(functor) + , managed_node_(parent) + { + + } + + virtual void on_new_task_available( + Args&... args) override + { + std::cout << "New task available in " << managed_node_->node_.name() << std::endl; + managed_node_->received_samples_.fetch_add(1); + + if (functor_) + { + functor_(std::forward(args)...); + } + + managed_node_->cv_.notify_all(); + } + + std::function functor_; + ManagedNodeService* managed_node_; + }; + + struct ManagedRequestReplyListener : public sustainml::core::RequestReplyListener + { + ManagedRequestReplyListener( + ManagedNodeService* parent) + : managed_node_(parent) + { + + } + + virtual void on_configuration_request( + types::RequestType& req, + types::ResponseType& res) override + { + std::cout << "New request available in " << req.node_id() << std::endl; + + res.node_id(req.node_id()); + res.transaction_id(req.transaction_id()); + res.success(true); + res.configuration(req.configuration()); + + managed_node_->cv_.notify_all(); + std::cout << "Sending back response" << req.node_id() << std::endl; + } + + ManagedNodeService* managed_node_; + }; + +public: + + ManagedNodeService( + const functor_t& callback = nullptr) + : listener_(this, callback) + , req_listener_(this) + , node_(listener_, req_listener_) + , received_samples_(0) + , expected_samples_(0) + { + + } + + ~ManagedNodeService() + { + stop(); + th_->join(); + } + + void start() + { + std::packaged_task task ([&]() + { + node_.spin(); + }); + + th_.reset(new std::thread(std::move(task))); + } + + void stop() + { + _NODE_TYPE::terminate(); + } + + void prepare_expected_samples( + const size_t& expected_samples) + { + expected_samples_.store(expected_samples); + } + + bool block_for_all() + { + std::unique_lock lock(mtx_); + cv_.wait(lock, [this]() -> bool + { + return expected_samples_ == received_samples_; + }); + + return expected_samples_ == received_samples_; + } + + template + size_t block_for_all( + const std::chrono::duration<_Rep, _Period>& max_wait) + { + std::unique_lock lock(mtx_); + cv_.wait_for(lock, max_wait, [this]() -> bool + { + return expected_samples_ == received_samples_; + }); + + return expected_samples_ == received_samples_; + } + +private: + + ManagedNodeServiceListener listener_; + ManagedRequestReplyListener req_listener_; + _NODE_TYPE node_; + + std::mutex mtx_; + std::condition_variable cv_; + std::unique_ptr th_; + + std::atomic received_samples_; + std::atomic expected_samples_; +}; + +#endif // _TEST_BLACKBOX_MANAGEDNODESERVICE_HPP_ + diff --git a/sustainml_cpp/test/blackbox/api/TaskInjector.hpp b/sustainml_cpp/test/blackbox/api/TaskInjector.hpp index ea2735b..9469e85 100644 --- a/sustainml_cpp/test/blackbox/api/TaskInjector.hpp +++ b/sustainml_cpp/test/blackbox/api/TaskInjector.hpp @@ -164,7 +164,7 @@ class TaskInjector { std::unique_lock lock(mtx_); - std::cout << "TrajInjector is waiting discovery..." << std::endl; + std::cout << "TaskInjector is waiting discovery..." << std::endl; if (timeout == std::chrono::seconds::zero()) { @@ -209,6 +209,17 @@ class TaskInjector } } + void inject_request( + typename T::type& req) + { + if (eprosima::fastdds::dds::RETCODE_OK == + datawriter_->write((void*)&(req))) + { + std::cout << "Injecting data for service with node id " << req.node_id() << + " ,with transaction " << req.transaction_id() << "." << std::endl; + } + } + private: eprosima::fastdds::dds::DomainParticipant* participant_; diff --git a/sustainml_cpp/test/blackbox/api/TaskReceiver.hpp b/sustainml_cpp/test/blackbox/api/TaskReceiver.hpp new file mode 100644 index 0000000..0a73d8f --- /dev/null +++ b/sustainml_cpp/test/blackbox/api/TaskReceiver.hpp @@ -0,0 +1,243 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file TaskReceiver.hpp + * + */ + +#ifndef _TEST_BLACKBOX_TASKRECEIVER_HPP_ +#define _TEST_BLACKBOX_TASKRECEIVER_HPP_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +class TaskReceiver +{ + + struct TaskReceiverListener : public eprosima::fastdds::dds::DataReaderListener + { + TaskReceiverListener( + TaskReceiver* task_receiver) + : task_inj_(task_receiver) + { + + } + + virtual void on_data_available( + eprosima::fastdds::dds::DataReader* reader) + { + typename T::type data; + eprosima::fastdds::dds::SampleInfo info; + + if (eprosima::fastdds::dds::RETCODE_OK == + reader->take_next_sample((void*)&data, &info)) + { + std::cout << "TaskReceiver receive data for service from node id " << data.node_id() << + " ,with transaction " << data.transaction_id() << "." << std::endl; + } + } + + void on_subscription_matched( + eprosima::fastdds::dds::DataReader* reader, + const eprosima::fastdds::dds::SubscriptionMatchedStatus& status) + { + if (status.current_count_change == 1) + { + task_inj_->matched_.fetch_add(1); + std::cout << "TaskReceiver matched" << std::endl; + } + else if (status.current_count_change == -1) + { + task_inj_->matched_.fetch_sub(1); + } + } + + TaskReceiver* task_inj_; + }; + +public: + + TaskReceiver( + const std::string& topic_name + ) + : participant_(nullptr) + , subscriber_(nullptr) + , topic_(nullptr) + , datareader_(nullptr) + , type_(new T()) + , listener_(this) + , matched_(0) + { + eprosima::fastdds::dds::DomainParticipantQos pqos = eprosima::fastdds::dds::PARTICIPANT_QOS_DEFAULT; + pqos.name("TaskReceiver_Participant"); + auto factory = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance(); + + participant_ = factory->create_participant(0, pqos); + + if (participant_ == nullptr) + { + return; + } + + //REGISTER THE TYPE + type_.register_type(participant_); + + //CREATE THE SUBSCRIBER + eprosima::fastdds::dds::SubscriberQos pubqos = + eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT; + + subscriber_ = participant_->create_subscriber( + pubqos, + nullptr); + + if (subscriber_ == nullptr) + { + return; + } + + //CREATE THE TOPIC + eprosima::fastdds::dds::TopicQos tqos = + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT; + + topic_ = participant_->create_topic( + topic_name, + type_.get_type_name(), + tqos); + + if (topic_ == nullptr) + { + return; + } + + // CREATE THE READER + eprosima::fastdds::dds::DataReaderQos wqos = + eprosima::fastdds::dds::DATAREADER_QOS_DEFAULT; + + wqos.resource_limits().max_instances = 500; + wqos.resource_limits().max_samples_per_instance = 1; + + datareader_ = subscriber_->create_datareader( + topic_, + wqos, + &listener_); + + if (datareader_ == nullptr) + { + return; + } + } + + ~TaskReceiver() + { + if (datareader_ != nullptr) + { + subscriber_->delete_datareader(datareader_); + } + if (subscriber_ != nullptr) + { + participant_->delete_subscriber(subscriber_); + } + if (topic_ != nullptr) + { + participant_->delete_topic(topic_); + } + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()-> + delete_participant(participant_); + } + + bool wait_discovery( + const size_t& expected_matches, + std::chrono::seconds timeout = std::chrono::seconds::zero()) + { + std::unique_lock lock(mtx_); + + std::cout << "TaskReceiver is waiting discovery..." << std::endl; + + if (timeout == std::chrono::seconds::zero()) + { + cv_.wait(lock, [&]() + { + return matched_ == expected_matches; + }); + } + else + { + cv_.wait_for(lock, timeout, [&]() + { + return matched_ == expected_matches; + }); + } + + std::cout << "Reader discovery finished..." << std::endl; + + return matched_ == expected_matches; + } + + bool get_data( + typename T::type& data, + std::chrono::milliseconds timeout) + { + eprosima::fastdds::dds::SampleInfo info; + auto start = std::chrono::steady_clock::now(); + + while (std::chrono::steady_clock::now() - start < timeout) + { + if (eprosima::fastdds::dds::RETCODE_OK == + datareader_->take_next_sample((void*)&data, &info)) + { + return true; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + return false; + } + +private: + + eprosima::fastdds::dds::DomainParticipant* participant_; + + eprosima::fastdds::dds::Subscriber* subscriber_; + + eprosima::fastdds::dds::Topic* topic_; + + eprosima::fastdds::dds::DataReader* datareader_; + + eprosima::fastdds::dds::TypeSupport type_; + + TaskReceiverListener listener_; + + std::atomic matched_; + + std::condition_variable cv_; + + std::mutex mtx_; + +}; + +#endif // _TEST_BLACKBOX_TASKRECEIVER_HPP_ diff --git a/sustainml_cpp/test/blackbox/common/BlackboxTests.hpp b/sustainml_cpp/test/blackbox/common/BlackboxTests.hpp index 4e282b1..da72f10 100644 --- a/sustainml_cpp/test/blackbox/common/BlackboxTests.hpp +++ b/sustainml_cpp/test/blackbox/common/BlackboxTests.hpp @@ -34,7 +34,9 @@ #include "common/Common.hpp" #include "../api/ManagedNode.hpp" +#include "../api/ManagedNodeService.hpp" #include "../api/TaskInjector.hpp" +#include "../api/TaskReceiver.hpp" #include #include @@ -102,6 +104,36 @@ using MLModelManagedNode = ManagedNode; +/******* Auxiliary Managed Nodes with Service aliases *****/ + +using AppRequirementsManagedNodeService = ManagedNodeService; + +using CarbonFootprintManagedNodeService = ManagedNodeService; + +using HWConstraintsManagedNodeService = ManagedNodeService; + +using HWResourcesManagedNodeService = ManagedNodeService; + +using MLModelMetadataManagedNodeService = ManagedNodeService; + +using MLModelManagedNodeService = ManagedNodeService; + /******* Auxiliary Signature aliases *****/ using AppRequirementsCallbackSignature = std::function request("sustainml/request"); + TaskReceiver response("sustainml/response"); + + node.start(); + + request.wait_discovery(1); + response.wait_discovery(1); + + RequestTypeImpl req; + req.node_id(static_cast(NodeID::ID_ML_MODEL_METADATA)); + req.transaction_id(1); + req.configuration("Test"); + request.inject_request(req); + + ResponseTypeImpl res; + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_TRUE(res.configuration() == req.configuration()); + + req.node_id(static_cast(NodeID::ID_CARBON_FOOTPRINT)); + req.transaction_id(1); + req.configuration("Test2"); + request.inject_request(req); + + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_FALSE(res.configuration() == req.configuration()); +} + +TEST(BlackboxTestsResponseNodes, AppRequirementsNode) +{ + AppRequirementsManagedNodeService node; + + TaskInjector request("sustainml/request"); + TaskReceiver response("sustainml/response"); + + node.start(); + + request.wait_discovery(1); + response.wait_discovery(1); + + RequestTypeImpl req; + req.node_id(static_cast(NodeID::ID_APP_REQUIREMENTS)); + req.transaction_id(1); + req.configuration("Test"); + request.inject_request(req); + + ResponseTypeImpl res; + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_TRUE(res.configuration() == req.configuration()); + + req.node_id(static_cast(NodeID::ID_CARBON_FOOTPRINT)); + req.transaction_id(1); + req.configuration("Test2"); + request.inject_request(req); + + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_FALSE(res.configuration() == req.configuration()); +} + +TEST(BlackboxTestsResponseNodes, CarbonFootprintNode) +{ + CarbonFootprintManagedNodeService node; + + TaskInjector request("sustainml/request"); + TaskReceiver response("sustainml/response"); + + node.start(); + + request.wait_discovery(1); + response.wait_discovery(1); + + RequestTypeImpl req; + req.node_id(static_cast(NodeID::ID_CARBON_FOOTPRINT)); + req.transaction_id(1); + req.configuration("Test"); + request.inject_request(req); + + ResponseTypeImpl res; + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_TRUE(res.configuration() == req.configuration()); + + req.node_id(static_cast(NodeID::ID_APP_REQUIREMENTS)); + req.transaction_id(1); + req.configuration("Test2"); + request.inject_request(req); + + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_FALSE(res.configuration() == req.configuration()); +} + +TEST(BlackboxTestsResponseNodes, HWConstraintsNode) +{ + HWConstraintsManagedNodeService node; + + TaskInjector request("sustainml/request"); + TaskReceiver response("sustainml/response"); + + node.start(); + + request.wait_discovery(1); + response.wait_discovery(1); + + RequestTypeImpl req; + req.node_id(static_cast(NodeID::ID_HW_CONSTRAINTS)); + req.transaction_id(1); + req.configuration("Test"); + request.inject_request(req); + + ResponseTypeImpl res; + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_TRUE(res.configuration() == req.configuration()); + + req.node_id(static_cast(NodeID::ID_APP_REQUIREMENTS)); + req.transaction_id(1); + req.configuration("Test2"); + request.inject_request(req); + + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_FALSE(res.configuration() == req.configuration()); +} + +TEST(BlackboxTestsResponseNodes, HWResourcesNode) +{ + HWResourcesManagedNodeService node; + + TaskInjector request("sustainml/request"); + TaskReceiver response("sustainml/response"); + + node.start(); + + request.wait_discovery(1); + response.wait_discovery(1); + + RequestTypeImpl req; + req.node_id(static_cast(NodeID::ID_HW_RESOURCES)); + req.transaction_id(1); + req.configuration("Test"); + request.inject_request(req); + + ResponseTypeImpl res; + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_TRUE(res.configuration() == req.configuration()); + + req.node_id(static_cast(NodeID::ID_APP_REQUIREMENTS)); + req.transaction_id(1); + req.configuration("Test2"); + request.inject_request(req); + + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_FALSE(res.configuration() == req.configuration()); +} + +TEST(BlackboxTestsResponseNodes, MLModelNodeService) +{ + MLModelManagedNodeService node; + + TaskInjector request("sustainml/request"); + TaskReceiver response("sustainml/response"); + + node.start(); + + request.wait_discovery(1); + response.wait_discovery(1); + + RequestTypeImpl req; + req.node_id(static_cast(NodeID::ID_ML_MODEL)); + req.transaction_id(1); + req.configuration("Test"); + request.inject_request(req); + + ResponseTypeImpl res; + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_TRUE(res.configuration() == req.configuration()); + + req.node_id(static_cast(NodeID::ID_APP_REQUIREMENTS)); + req.transaction_id(1); + req.configuration("Test2"); + request.inject_request(req); + + response.get_data(res, std::chrono::milliseconds(1000)); + + ASSERT_FALSE(res.configuration() == req.configuration()); +}