diff --git a/cs4home_core/CMakeLists.txt b/cs4home_core/CMakeLists.txt index 774d5a3..62ed4f9 100644 --- a/cs4home_core/CMakeLists.txt +++ b/cs4home_core/CMakeLists.txt @@ -11,11 +11,13 @@ endif() find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(rclcpp_lifecycle REQUIRED) +find_package(rclcpp_cascade_lifecycle REQUIRED) find_package(rclcpp_components REQUIRED) set(dependencies rclcpp rclcpp_lifecycle + rclcpp_cascade_lifecycle rclcpp_components ) diff --git a/cs4home_core/include/cs4home_core/Afferent.hpp b/cs4home_core/include/cs4home_core/Afferent.hpp index 30750cc..1027784 100644 --- a/cs4home_core/include/cs4home_core/Afferent.hpp +++ b/cs4home_core/include/cs4home_core/Afferent.hpp @@ -16,11 +16,12 @@ #ifndef CS4HOME_CORE__AFFERENT_HPP_ #define CS4HOME_CORE__AFFERENT_HPP_ +#include #include #include #include -#include #include +#include #include "rclcpp_lifecycle/lifecycle_node.hpp" #include "rclcpp/rclcpp.hpp" @@ -51,24 +52,37 @@ class Afferent * @brief Constructor for the Afferent class. * @param parent Shared pointer to the lifecycle node managing this instance. */ - explicit Afferent(rclcpp_lifecycle::LifecycleNode::SharedPtr parent); + explicit Afferent(const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent); /** * @brief Configures the afferent component; intended for subclass implementation. * @return True if configuration is successful. */ - virtual bool configure() = 0; + virtual bool configure(); /** * @brief Sets the processing mode and an optional callback function. * + * @param topic The topic to subscribe * @param mode Processing mode for handling messages. * @param cb Optional callback function for handling serialized messages in CALLBACK mode. */ void set_mode( + const std::string & topic, EfferentProcessMode mode, std::function)> cb = nullptr); + /** + * @brief Sets the processing mode and an optional callback function. + * + * @param topic_idx The index of the topic input. + * @param mode Processing mode for handling messages. + * @param cb Optional callback function for handling serialized messages in CALLBACK mode. + */ + void set_mode( + size_t topic_idx, + EfferentProcessMode mode, + std::function)> cb = nullptr); /** * @brief Gets the current processing mode. @@ -107,16 +121,43 @@ class Afferent /** * @brief Retrieves the next message from the queue, if available. * @tparam MessageT Type of message to retrieve. - * @return A unique pointer to the next message, or nullptr if the queue is empty. + * @tparam topic The topic of the associated queue to retrieve the message. + * @return A unique pointer to the next message, or nullptr if the queue is + * empty or does not exists. + */ + template std::unique_ptr get_msg(const std::string & topic) + { + if (msg_queues_.find(topic) == msg_queues_.end() || msg_queues_[topic].empty()) { + return nullptr; + } + + std::unique_ptr msg = std::move(msg_queues_[topic].front()); + msg_queues_[topic].pop(); + + return get_msg(std::move(msg)); + } + + /** + * @brief Retrieves the next message from the queue, if available. + * @tparam MessageT Type of message to retrieve. + * @tparam topic_idx The index fn the topic input associated queue to retrieve the message. + * @return A unique pointer to the next message, or nullptr if the queue is + * empty or does not exists. */ - template std::unique_ptr get_msg() + template std::unique_ptr get_msg(size_t topic_idx) { - if (msg_queue_.empty()) { - return {}; + if (topic_idx >= msg_queues_.size()) { + return nullptr; } - std::unique_ptr msg = std::move(msg_queue_.front()); - msg_queue_.pop(); + const std::string & topic = input_topic_names_[topic_idx]; + + if (msg_queues_.find(topic) == msg_queues_.end() || msg_queues_[topic].empty()) { + return nullptr; + } + + std::unique_ptr msg = std::move(msg_queues_[topic].front()); + msg_queues_[topic].pop(); return get_msg(std::move(msg)); } @@ -124,8 +165,7 @@ class Afferent protected: /** Shared pointer to the parent node. */ rclcpp_lifecycle::LifecycleNode::SharedPtr parent_; - /** List of subscriptions. */ - std::vector> subs_; + std::string name_; EfferentProcessMode mode_ {ONDEMAND}; /**< Current processing mode. */ @@ -133,11 +173,18 @@ class Afferent const size_t MAX_DEFAULT_QUEUE_SIZE = 100; /** Maximum queue size. */ size_t max_queue_size_ {MAX_DEFAULT_QUEUE_SIZE}; + + /** List of subscriptions. */ + std::vector> subs_; /** Queue for serialized messages. */ - std::queue> msg_queue_; + std::map>> msg_queues_; + /**< List of input topics to subscribe to for images. */ + std::vector input_topic_names_; + /**< List of input topics types. */ + std::vector input_topic_types_; /** Callback for serialized messages. */ - std::function)> callback_; + std::map)>> callbacks_; /** diff --git a/cs4home_core/include/cs4home_core/CognitiveModule.hpp b/cs4home_core/include/cs4home_core/CognitiveModule.hpp index 57885cd..f4b1724 100644 --- a/cs4home_core/include/cs4home_core/CognitiveModule.hpp +++ b/cs4home_core/include/cs4home_core/CognitiveModule.hpp @@ -28,16 +28,17 @@ #include "rclcpp/macros.hpp" #include "rclcpp/rclcpp.hpp" #include "rclcpp_lifecycle/lifecycle_node.hpp" +#include "rclcpp_cascade_lifecycle/rclcpp_cascade_lifecycle.hpp" namespace cs4home_core { +class CognitiveModule : public rclcpp_cascade_lifecycle::CascadeLifecycleNode /** * @class CognitiveModule - * @brief Extends the LifecycleNode to manage cognitive processing components in a ROS 2 lifecycle, + * @brief Extends the Cascade LifecycleNode to manage cognitive processing components in a ROS 2 lifecycle, * including afferent, efferent, core, meta, and coupling components. */ -class CognitiveModule : public rclcpp_lifecycle::LifecycleNode { public: RCLCPP_SMART_PTR_DEFINITIONS(CognitiveModule) diff --git a/cs4home_core/include/cs4home_core/Core.hpp b/cs4home_core/include/cs4home_core/Core.hpp index be7e701..a9b0630 100644 --- a/cs4home_core/include/cs4home_core/Core.hpp +++ b/cs4home_core/include/cs4home_core/Core.hpp @@ -16,6 +16,7 @@ #define CS4HOME_CORE__CORE_HPP_ #include +#include #include "cs4home_core/Afferent.hpp" #include "cs4home_core/Efferent.hpp" @@ -40,7 +41,8 @@ class Core * @brief Constructs a Core object associated with a parent lifecycle node. * @param parent Shared pointer to the lifecycle node managing this Core instance. */ - explicit Core(rclcpp_lifecycle::LifecycleNode::SharedPtr parent); + explicit Core( + const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent); /** * @brief Configures the Core component. @@ -75,6 +77,8 @@ class Core protected: /** Shared pointer to the parent lifecycle node. */ rclcpp_lifecycle::LifecycleNode::SharedPtr parent_; + + std::string name_; /** Shared pointer to the Afferent component. */ cs4home_core::Afferent::SharedPtr afferent_; /** Shared pointer to the Efferent component. */ diff --git a/cs4home_core/include/cs4home_core/Coupling.hpp b/cs4home_core/include/cs4home_core/Coupling.hpp index ffaa10d..96afcaa 100644 --- a/cs4home_core/include/cs4home_core/Coupling.hpp +++ b/cs4home_core/include/cs4home_core/Coupling.hpp @@ -15,6 +15,8 @@ #ifndef CS4HOME_CORE__COUPLING_HPP_ #define CS4HOME_CORE__COUPLING_HPP_ +#include + #include "rclcpp_lifecycle/lifecycle_node.hpp" #include "rclcpp/macros.hpp" @@ -35,7 +37,8 @@ class Coupling * @brief Constructs a Coupling object associated with a parent lifecycle node. * @param parent Shared pointer to the lifecycle node managing this Coupling instance. */ - explicit Coupling(rclcpp_lifecycle::LifecycleNode::SharedPtr parent); + explicit Coupling( + const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent); /** * @brief Configures the Coupling component. @@ -46,6 +49,7 @@ class Coupling protected: /**< Shared pointer to the parent lifecycle node. */ rclcpp_lifecycle::LifecycleNode::SharedPtr parent_; + std::string name_; }; } // namespace cs4home_core diff --git a/cs4home_core/include/cs4home_core/Efferent.hpp b/cs4home_core/include/cs4home_core/Efferent.hpp index 2dea9e3..b65d18e 100644 --- a/cs4home_core/include/cs4home_core/Efferent.hpp +++ b/cs4home_core/include/cs4home_core/Efferent.hpp @@ -38,15 +38,16 @@ class Efferent /** * @brief Constructs an Efferent object associated with a parent lifecycle node. + * @param name name of the component. * @param parent Shared pointer to the lifecycle node managing this Efferent instance. */ - explicit Efferent(rclcpp_lifecycle::LifecycleNode::SharedPtr parent); + explicit Efferent(const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent); /** * @brief Configures the Efferent component. * @return True if configuration is successful. */ - virtual bool configure() = 0; + virtual bool configure(); /** * @brief Publishes a serialized message to all configured publishers. @@ -58,24 +59,35 @@ class Efferent * @param msg Unique pointer to the message to broadcast. */ template - void publish(std::unique_ptr msg) + void publish(size_t topic_index, std::unique_ptr msg) { + if (topic_index >= pubs_.size()) { + RCLCPP_WARN( + parent_->get_logger(), "[Efferent] Error publishing: topic index not valid"); + return; + } + rclcpp::Serialization serializer; auto untyped_msg = rclcpp::SerializedMessage(); serializer.serialize_message(msg.get(), &untyped_msg); - for (auto & pub : pubs_) { - pub->publish(untyped_msg); - } + pubs_[topic_index]->publish(untyped_msg); } protected: /**< Shared pointer to the parent lifecycle node. */ rclcpp_lifecycle::LifecycleNode::SharedPtr parent_; + std::string name_; + /**< List of generic publishers. */ std::vector> pubs_; + /**< List of output topics to publish images. */ + std::vector output_topic_names_; + /**< List of output topics types. */ + std::vector output_topic_types_; + /** * @brief Creates a publisher for a specified topic and message type. * @param topic The topic name to publish messages to. diff --git a/cs4home_core/include/cs4home_core/Flow.hpp b/cs4home_core/include/cs4home_core/Flow.hpp index 2fdc463..31771d2 100644 --- a/cs4home_core/include/cs4home_core/Flow.hpp +++ b/cs4home_core/include/cs4home_core/Flow.hpp @@ -22,6 +22,8 @@ #include "rclcpp/rclcpp.hpp" #include "rclcpp/macros.hpp" +#include "rclcpp_lifecycle/lifecycle_node.hpp" +#include "rclcpp_cascade_lifecycle/rclcpp_cascade_lifecycle.hpp" namespace cs4home_core { @@ -31,7 +33,7 @@ namespace cs4home_core * @brief Represents a sequence of nodes within a robotic system, with utilities * to manage and print the node sequence. */ -class Flow +class Flow : public rclcpp_cascade_lifecycle::CascadeLifecycleNode { public: RCLCPP_SMART_PTR_DEFINITIONS(Flow) @@ -40,18 +42,22 @@ class Flow * @brief Constructs a Flow object with a specified sequence of nodes. * @param nodes A vector of node names to initialize the flow sequence. */ - explicit Flow(const std::vector & nodes); + explicit Flow(const std::string & name); + Flow(const std::string & name, const std::vector & nodes); /** * @brief Prints the sequence of nodes in the flow to the standard output. */ void print() const; - /** * @brief Retrieves the sequence of nodes in the flow. * @return A constant reference to the vector of node names. */ - const std::vector & get_nodes() const {return nodes_;} + const std::vector & get_flow() const {return nodes_;} + void set_flow(const std::vector & nodes); + + void activate(); + void deactivate(); private: std::vector nodes_; /**< Sequence of nodes in the flow. */ diff --git a/cs4home_core/include/cs4home_core/Master.hpp b/cs4home_core/include/cs4home_core/Master.hpp index c67cfd3..c249025 100644 --- a/cs4home_core/include/cs4home_core/Master.hpp +++ b/cs4home_core/include/cs4home_core/Master.hpp @@ -17,8 +17,10 @@ #include #include +#include #include "cs4home_core/CognitiveModule.hpp" +#include "cs4home_core/Flow.hpp" #include "rclcpp_lifecycle/lifecycle_node.hpp" #include "rclcpp/rclcpp.hpp" @@ -39,10 +41,12 @@ class Master : public rclcpp_lifecycle::LifecycleNode using CallbackReturnT = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn; /** - * @brief Constructs a Master lifecycle node with the specified options. + * @brief Constructs a Master cascade lifecycle node with the specified options. * @param options Node options to configure the Master node. */ - explicit Master(const rclcpp::NodeOptions & options = rclcpp::NodeOptions()); + explicit Master( + const std::string & name, + const rclcpp::NodeOptions & options = rclcpp::NodeOptions()); /** * @brief Configures the Master node. @@ -86,9 +90,10 @@ class Master : public rclcpp_lifecycle::LifecycleNode */ CallbackReturnT on_error(const rclcpp_lifecycle::State & state); + const std::map & get_flows() const {return flows_;} + protected: - /** Map of cognitive modules managed by the Master node. */ - std::map cog_modules_; + std::map flows_; }; } // namespace cs4home_core diff --git a/cs4home_core/include/cs4home_core/Meta.hpp b/cs4home_core/include/cs4home_core/Meta.hpp index 8431ad2..fc2e6ae 100644 --- a/cs4home_core/include/cs4home_core/Meta.hpp +++ b/cs4home_core/include/cs4home_core/Meta.hpp @@ -15,6 +15,8 @@ #ifndef CS4HOME_CORE__META_HPP_ #define CS4HOME_CORE__META_HPP_ +#include + #include "rclcpp_lifecycle/lifecycle_node.hpp" #include "rclcpp/macros.hpp" @@ -35,7 +37,7 @@ class Meta * @brief Constructs a Meta object associated with a parent lifecycle node. * @param parent Shared pointer to the lifecycle node managing this Meta instance. */ - explicit Meta(rclcpp_lifecycle::LifecycleNode::SharedPtr parent); + explicit Meta(const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent); /** * @brief Configures the Meta component. @@ -46,6 +48,7 @@ class Meta protected: /// < Shared pointer to the parent lifecycle node. rclcpp_lifecycle::LifecycleNode::SharedPtr parent_; + std::string name_; }; } // namespace cs4home_core diff --git a/cs4home_core/package.xml b/cs4home_core/package.xml index bc5d309..8803647 100644 --- a/cs4home_core/package.xml +++ b/cs4home_core/package.xml @@ -10,6 +10,7 @@ rclcpp rclcpp_lifecycle + rclcpp_cascade_lifecycle rclcpp_components ament_lint_auto diff --git a/cs4home_core/src/cs4home_core/Afferent.cpp b/cs4home_core/src/cs4home_core/Afferent.cpp index 0ab5ab6..72bc3da 100644 --- a/cs4home_core/src/cs4home_core/Afferent.cpp +++ b/cs4home_core/src/cs4home_core/Afferent.cpp @@ -24,9 +24,13 @@ namespace cs4home_core * @brief Constructor for the Afferent class. * @param parent Shared pointer to the lifecycle node that owns this Afferent instance. */ -Afferent::Afferent(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) -: parent_(parent) +Afferent::Afferent(const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent) +: parent_(parent), + name_(name) { + // Declares the parameter for input topics. and types + parent_->declare_parameter(name_ + ".topics", input_topic_names_); + parent_->declare_parameter(name_ + ".types", input_topic_types_); } /** @@ -35,17 +39,55 @@ Afferent::Afferent(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) * This function allows configuring the Afferent object with a specific * processing mode and an optional callback to handle serialized messages. * + * @param topic The topic to subscribe * @param mode The processing mode for the Afferent object. * @param cb A callback function to process serialized messages, used if the mode is CALLBACK. */ void Afferent::set_mode( + const std::string & topic, EfferentProcessMode mode, std::function)> cb) { if (mode == CALLBACK) { if (cb) { - callback_ = cb; + callbacks_[topic] = cb; + } else { + RCLCPP_WARN( + parent_->get_logger(), "[Afferent] Error setting callback: not function specified"); + return; + } + } + mode_ = mode; +} + +/** + * @brief Sets the operation mode and an optional callback function. + * + * This function allows configuring the Afferent object with a specific + * processing mode and an optional callback to handle serialized messages. + * + * @param topic_idx The index of the topic input. + * @param mode The processing mode for the Afferent object. + * @param cb A callback function to process serialized messages, used if the mode is CALLBACK. + */ +void +Afferent::set_mode( + size_t topic_idx, + EfferentProcessMode mode, + std::function)> cb) +{ + if (topic_idx >= input_topic_names_.size()) { + RCLCPP_WARN( + parent_->get_logger(), "[Afferent] Error setting callback: topic index not valid"); + return; + } + + const std::string & topic = input_topic_names_[topic_idx]; + + if (mode == CALLBACK) { + if (cb) { + callbacks_[topic] = cb; } else { RCLCPP_WARN( parent_->get_logger(), "[Afferent] Error setting callback: not function specified"); @@ -75,21 +117,26 @@ Afferent::create_subscriber(const std::string & topic, const std::string & type) "[Afferent] Creating subscription [%s, %s]", topic.c_str(), type.c_str()); + + if (msg_queues_.find(topic) == msg_queues_.end()) { + msg_queues_[topic] = std::queue>(); + } + auto sub = rclcpp::create_generic_subscription( parent_->get_node_topics_interface(), topic, type, 100, [&](std::unique_ptr msg) { if (mode_ == CALLBACK) { - if (callback_) { - callback_(std::move(msg)); + if (callbacks_[topic]) { + callbacks_[topic](std::move(msg)); } else { RCLCPP_WARN( parent_->get_logger(), "[Afferent] Error calling callback: not function specified"); } } else { - msg_queue_.push(std::move(msg)); - if (msg_queue_.size() > max_queue_size_) { - msg_queue_.pop(); + msg_queues_[topic].push(std::move(msg)); + if (msg_queues_[topic].size() > max_queue_size_) { + msg_queues_[topic].pop(); } } }); @@ -100,4 +147,53 @@ Afferent::create_subscriber(const std::string & topic, const std::string & type) return true; } + +/** + * @brief Configures the Afferent by creating subscribers for each specified topic. + * + * This method retrieves the topic names from the parameter server and attempts to create + * a subscription for each topic to receive messages. + * + * @return True if all subscriptions are created successfully. + */ +bool +Afferent::configure() +{ + parent_->get_parameter(name_ + ".topics", input_topic_names_); + parent_->get_parameter(name_ + ".types", input_topic_types_); + + if (input_topic_names_.size() != input_topic_types_.size()) { + RCLCPP_ERROR( + parent_->get_logger(), + "Parameter sizes are not correct (%zu != %zu)", + input_topic_names_.size(), input_topic_types_.size()); + return false; + } + + RCLCPP_DEBUG( + parent_->get_logger(), + "Creating %zu subscriptions: ", input_topic_names_.size()); + + for (size_t i = 0; i < input_topic_names_.size(); i++) { + RCLCPP_DEBUG( + parent_->get_logger(), + "\tCreating subscription for topic [%s] (%s)", + input_topic_names_[i].c_str(), input_topic_types_[i].c_str()); + + if (create_subscriber(input_topic_names_[i], input_topic_types_[i])) { + RCLCPP_DEBUG( + parent_->get_logger(), + "[SimpleImageInput] created subscription to [%s, %s]", + input_topic_names_[i].c_str(), input_topic_types_[i].c_str()); + } else { + RCLCPP_WARN( + parent_->get_logger(), + "[SimpleImageInput] Couldn't create subscription to [%s, %s]", + input_topic_names_[i].c_str(), input_topic_types_[i].c_str()); + } + } + + return true; +} + } // namespace cs4home_core diff --git a/cs4home_core/src/cs4home_core/CognitiveModule.cpp b/cs4home_core/src/cs4home_core/CognitiveModule.cpp index 2c5bc01..07be2dc 100644 --- a/cs4home_core/src/cs4home_core/CognitiveModule.cpp +++ b/cs4home_core/src/cs4home_core/CognitiveModule.cpp @@ -25,7 +25,7 @@ namespace cs4home_core CognitiveModule::CognitiveModule( const std::string & name, const rclcpp::NodeOptions & options) -: LifecycleNode(name, options) +: CascadeLifecycleNode(name, options) { declare_parameter("core", core_name_); declare_parameter("afferent", afferent_name_); @@ -78,11 +78,9 @@ CallbackReturnT CognitiveModule::on_configure(const rclcpp_lifecycle::State & st return CallbackReturnT::FAILURE; } - core_->set_afferent(afferent_); core_->set_efferent(efferent_); - get_parameter("meta", meta_name_); std::string error_meta; std::tie(meta_, error_meta) = load_component(meta_name_, shared_from_this()); diff --git a/cs4home_core/src/cs4home_core/Core.cpp b/cs4home_core/src/cs4home_core/Core.cpp index 4d4f591..2db2e75 100644 --- a/cs4home_core/src/cs4home_core/Core.cpp +++ b/cs4home_core/src/cs4home_core/Core.cpp @@ -21,8 +21,10 @@ namespace cs4home_core * @brief Constructs a Core object associated with a given lifecycle node. * @param parent Shared pointer to the lifecycle node managing this Core instance. */ -Core::Core(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) -: parent_(parent) +Core::Core( + const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent) +: parent_(parent), + name_(name) { } diff --git a/cs4home_core/src/cs4home_core/Coupling.cpp b/cs4home_core/src/cs4home_core/Coupling.cpp index 7efe8ab..2019370 100644 --- a/cs4home_core/src/cs4home_core/Coupling.cpp +++ b/cs4home_core/src/cs4home_core/Coupling.cpp @@ -21,8 +21,9 @@ namespace cs4home_core * @brief Constructs a Coupling object and assigns the parent lifecycle node. * @param parent Shared pointer to the lifecycle node managing this Coupling instance. */ -Coupling::Coupling(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) -: parent_(parent) +Coupling::Coupling(const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent) +: parent_(parent), + name_(name) { } diff --git a/cs4home_core/src/cs4home_core/Efferent.cpp b/cs4home_core/src/cs4home_core/Efferent.cpp index feebdaa..d16f147 100644 --- a/cs4home_core/src/cs4home_core/Efferent.cpp +++ b/cs4home_core/src/cs4home_core/Efferent.cpp @@ -21,9 +21,44 @@ namespace cs4home_core * @brief Constructs an Efferent object and assigns the parent lifecycle node. * @param parent Shared pointer to the lifecycle node managing this Efferent instance. */ -Efferent::Efferent(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) -: parent_(parent) +Efferent::Efferent(const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent) +: parent_(parent), + name_(name) { + // Declares the parameter for output topics. + parent_->declare_parameter(name_ + ".topics", output_topic_names_); + parent_->declare_parameter(name_ + ".types", output_topic_names_); +} + +/** + * @brief Configures the Efferent by creating publishers for each specified topic. + * + * This method retrieves the topic names from the parameter server and attempts to create + * publishers for each topic to send messages. + * + * @return True if all publishers are created successfully. + */ +bool +Efferent::configure() +{ + parent_->get_parameter(name_ + ".topics", output_topic_names_); + parent_->get_parameter(name_ + ".types", output_topic_types_); + + + for (size_t i = 0; i < output_topic_names_.size(); i++) { + if (create_publisher(output_topic_names_[i], output_topic_types_[i])) { + RCLCPP_DEBUG( + parent_->get_logger(), + "[SimpleImageOutput] created publisher to [%s, %s]", + output_topic_names_[i].c_str(), output_topic_types_[i].c_str()); + } else { + RCLCPP_WARN( + parent_->get_logger(), + "[SimpleImageOutput] Couldn't create publisher to [%s, %s]", + output_topic_names_[i].c_str(), output_topic_types_[i].c_str()); + } + } + return true; } /** diff --git a/cs4home_core/src/cs4home_core/Flow.cpp b/cs4home_core/src/cs4home_core/Flow.cpp index 817317c..a6a502e 100644 --- a/cs4home_core/src/cs4home_core/Flow.cpp +++ b/cs4home_core/src/cs4home_core/Flow.cpp @@ -29,9 +29,40 @@ namespace cs4home_core * @brief Constructs a Flow object with a list of nodes. * @param nodes A vector of node names to initialize the flow sequence. */ -Flow::Flow(const std::vector & nodes) -: nodes_(nodes) +Flow::Flow(const std::string & name) +: CascadeLifecycleNode(name) { + trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE); +} + +Flow::Flow(const std::string & name, const std::vector & nodes) +: Flow(name) +{ + set_flow(nodes); +} + +void +Flow::set_flow(const std::vector & nodes) +{ + clear_activation(); + + nodes_ = nodes; + + for (const auto & node : nodes) { + add_activation(node); + } +} + +void +Flow::activate() +{ + trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE); +} + +void +Flow::deactivate() +{ + trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE); } /** @@ -49,4 +80,5 @@ Flow::print() const std::cout << std::endl; } + } // namespace cs4home_core diff --git a/cs4home_core/src/cs4home_core/Master.cpp b/cs4home_core/src/cs4home_core/Master.cpp index 054f94c..44606ee 100644 --- a/cs4home_core/src/cs4home_core/Master.cpp +++ b/cs4home_core/src/cs4home_core/Master.cpp @@ -21,9 +21,11 @@ namespace cs4home_core * @brief Constructs a Master lifecycle node with the specified options. * @param options Node options to configure the Master node. */ -Master::Master(const rclcpp::NodeOptions & options) -: LifecycleNode("master", options) +Master::Master(const std::string & name, const rclcpp::NodeOptions & options) +: LifecycleNode(name, options) { + std::vector flows; + declare_parameter("flows", std::vector{}); } using CallbackReturnT = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn; @@ -38,6 +40,17 @@ Master::on_configure(const rclcpp_lifecycle::State & state) { (void)state; + std::vector flows; + get_parameter("flows", flows); + + for (const auto & flow : flows) { + std::vector flow_cms; + declare_parameter(flow, flow_cms); + get_parameter(flow, flow_cms); + + flows_[flow] = Flow::make_shared(flow, flow_cms); + } + return CallbackReturnT::SUCCESS; } diff --git a/cs4home_core/src/cs4home_core/Meta.cpp b/cs4home_core/src/cs4home_core/Meta.cpp index cb98f4e..f685264 100644 --- a/cs4home_core/src/cs4home_core/Meta.cpp +++ b/cs4home_core/src/cs4home_core/Meta.cpp @@ -21,8 +21,9 @@ namespace cs4home_core * @brief Constructs a Meta object and assigns the parent lifecycle node. * @param parent Shared pointer to the lifecycle node managing this Meta instance. */ -Meta::Meta(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) -: parent_(parent) +Meta::Meta(const std::string & name, rclcpp_lifecycle::LifecycleNode::SharedPtr parent) +: parent_(parent), + name_(name) { } diff --git a/cs4home_core/test/DefaultCoupling.cpp b/cs4home_core/test/DefaultCoupling.cpp index c2be7f4..a99f716 100644 --- a/cs4home_core/test/DefaultCoupling.cpp +++ b/cs4home_core/test/DefaultCoupling.cpp @@ -35,7 +35,7 @@ class DefaultCoupling : public cs4home_core::Coupling * @param parent Shared pointer to the lifecycle node managing this DefaultCoupling instance. */ explicit DefaultCoupling(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) - : Coupling(parent) + : Coupling("default_coupling", parent) { RCLCPP_DEBUG(parent_->get_logger(), "Coupling created: [DefaultCoupling]"); } diff --git a/cs4home_core/test/DefaultMeta.cpp b/cs4home_core/test/DefaultMeta.cpp index ea67afc..8c7b77a 100644 --- a/cs4home_core/test/DefaultMeta.cpp +++ b/cs4home_core/test/DefaultMeta.cpp @@ -35,7 +35,7 @@ class DefaultMeta : public cs4home_core::Meta * @param parent Shared pointer to the lifecycle node managing this DefaultMeta instance. */ explicit DefaultMeta(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) - : Meta(parent) + : Meta("default_meta", parent) { RCLCPP_DEBUG(parent_->get_logger(), "Meta created: [DefaultMeta]"); } diff --git a/cs4home_core/test/ImageFilter.cpp b/cs4home_core/test/ImageFilter.cpp index a1c4e2c..f103ef0 100644 --- a/cs4home_core/test/ImageFilter.cpp +++ b/cs4home_core/test/ImageFilter.cpp @@ -18,7 +18,6 @@ #include "sensor_msgs/msg/image.hpp" #include "rclcpp_lifecycle/lifecycle_node.hpp" -#include "rclcpp/macros.hpp" using std::placeholders::_1; using namespace std::chrono_literals; @@ -40,7 +39,7 @@ class ImageFilter : public cs4home_core::Core */ explicit ImageFilter(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) - : Core(parent) + : Core("image_filter", parent) { RCLCPP_DEBUG(parent_->get_logger(), "Core created: [ImageFilter]"); } @@ -59,7 +58,7 @@ class ImageFilter : public cs4home_core::Core counter = counter * 2; msg->header.frame_id = std::to_string(counter); - efferent_->publish(std::move(msg)); + efferent_->publish(0, std::move(msg)); } /** @@ -70,7 +69,7 @@ class ImageFilter : public cs4home_core::Core */ void timer_callback() { - auto msg = afferent_->get_msg(); + auto msg = afferent_->get_msg(0); if (msg != nullptr) { process_in_image(std::move(msg)); } diff --git a/cs4home_core/test/ImageFilterCB.cpp b/cs4home_core/test/ImageFilterCB.cpp index 07655af..c6d923f 100644 --- a/cs4home_core/test/ImageFilterCB.cpp +++ b/cs4home_core/test/ImageFilterCB.cpp @@ -21,6 +21,7 @@ #include "rclcpp/macros.hpp" using std::placeholders::_1; +using std::placeholders::_2; using namespace std::chrono_literals; /** @@ -38,7 +39,7 @@ class ImageFilterCB : public cs4home_core::Core * @param parent Shared pointer to the lifecycle node managing this ImageFilterCB instance. */ explicit ImageFilterCB(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) - : Core(parent) + : Core("image_filter_cb", parent) { RCLCPP_DEBUG(parent_->get_logger(), "Core created: [ImageFilterCB]"); } @@ -60,9 +61,20 @@ class ImageFilterCB : public cs4home_core::Core counter = counter * 2; image_msg->header.frame_id = std::to_string(counter); - efferent_->publish(std::move(image_msg)); + efferent_->publish(0, std::move(image_msg)); } + /** + * @brief Processes incoming serialized camera info image messages. Do nothing + * @param msg Unique pointer to the serialized incoming image message. + */ + void process_in_camerainfo(std::unique_ptr msg) + { + auto camerainfo_msgs = afferent_->get_msg(std::move(msg)); + // Nothing to do + } + + /** * @brief Configures the ImageFilterCB component. * @@ -75,8 +87,10 @@ class ImageFilterCB : public cs4home_core::Core { RCLCPP_DEBUG(parent_->get_logger(), "Core configured"); - afferent_->set_mode( - cs4home_core::Afferent::CALLBACK, std::bind(&ImageFilterCB::process_in_image, this, _1)); + afferent_->set_mode(0, cs4home_core::Afferent::CALLBACK, + std::bind(&ImageFilterCB::process_in_image, this, _1)); + afferent_->set_mode(1, cs4home_core::Afferent::CALLBACK, + std::bind(&ImageFilterCB::process_in_camerainfo, this, _1)); return true; } diff --git a/cs4home_core/test/SimpleImageInput.cpp b/cs4home_core/test/SimpleImageInput.cpp index 4b43f3d..7e2a41f 100644 --- a/cs4home_core/test/SimpleImageInput.cpp +++ b/cs4home_core/test/SimpleImageInput.cpp @@ -35,51 +35,19 @@ class SimpleImageInput : public cs4home_core::Afferent * @param parent Shared pointer to the lifecycle node managing this SimpleImageInput instance. */ explicit SimpleImageInput(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) - : Afferent(parent) + : Afferent("simple_image_input", parent) { RCLCPP_DEBUG(parent_->get_logger(), "Efferent created: [SimpleImageInput]"); - - // Declares the parameter for input topics. - parent_->declare_parameter("simple_image_input.topics", input_topic_names_); } /** * @brief Configures the SimpleImageInput by creating subscribers for each specified topic. - * - * This method retrieves the topic names from the parameter server and attempts to create - * a subscription for each topic to receive `sensor_msgs::msg::Image` messages. - * * @return True if all subscriptions are created successfully. */ bool configure() override { - std::string param_name = "simple_image_input.topics"; - parent_->get_parameter(param_name, input_topic_names_); - - RCLCPP_DEBUG( - parent_->get_logger(), "[SimpleImageInput] Configuring %zu inputs [%s]", - input_topic_names_.size(), param_name.c_str()); - - for (size_t i = 0; i < input_topic_names_.size(); i++) { - if (create_subscriber(input_topic_names_[i], "sensor_msgs/msg/Image")) { - RCLCPP_DEBUG( - parent_->get_logger(), - "[SimpleImageInput] created subscription to [%s, sensor_msgs/msg/Image]", - input_topic_names_[i].c_str()); - } else { - RCLCPP_WARN( - parent_->get_logger(), - "[SimpleImageInput] Couldn't create subscription to [%s, sensor_msgs/msg/Image]", - input_topic_names_[i].c_str()); - } - } - - return true; + return Afferent::configure(); } - -private: - /**< List of input topics to subscribe to for images. */ - std::vector input_topic_names_; }; /// Registers the SimpleImageInput component with the ROS 2 class loader diff --git a/cs4home_core/test/SimpleImageOutput.cpp b/cs4home_core/test/SimpleImageOutput.cpp index 28e3ec0..d604871 100644 --- a/cs4home_core/test/SimpleImageOutput.cpp +++ b/cs4home_core/test/SimpleImageOutput.cpp @@ -35,12 +35,9 @@ class SimpleImageOutput : public cs4home_core::Efferent * @param parent Shared pointer to the lifecycle node managing this SimpleImageOutput instance. */ explicit SimpleImageOutput(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) - : Efferent(parent) + : Efferent("simple_image_output", parent) { RCLCPP_DEBUG(parent_->get_logger(), "Afferent created: [SimpleImageOutput]"); - - // Declares the parameter for output topics. - parent_->declare_parameter("simple_image_output.topics", output_topic_names_); } /** @@ -53,22 +50,7 @@ class SimpleImageOutput : public cs4home_core::Efferent */ bool configure() { - parent_->get_parameter("simple_image_output.topics", output_topic_names_); - - for (size_t i = 0; i < output_topic_names_.size(); i++) { - if (create_publisher(output_topic_names_[i], "sensor_msgs/msg/Image")) { - RCLCPP_DEBUG( - parent_->get_logger(), - "[SimpleImageOutput] created publisher to [%s, sensor_msgs/msg/Image]", - output_topic_names_[i].c_str()); - } else { - RCLCPP_WARN( - parent_->get_logger(), - "[SimpleImageOutput] Couldn't create publisher to [%s, sensor_msgs/msg/Image]", - output_topic_names_[i].c_str()); - } - } - return true; + return Efferent::configure(); } /** @@ -77,11 +59,8 @@ class SimpleImageOutput : public cs4home_core::Efferent */ void publish_image(sensor_msgs::msg::Image::UniquePtr msg) { - publish(std::move(msg)); + publish(0, std::move(msg)); } - -private: - std::vector output_topic_names_; /**< List of output topics to publish images. */ }; /// Registers the SimpleImageOutput component with the ROS 2 class loader diff --git a/cs4home_core/test/cognitive_module_test.cpp b/cs4home_core/test/cognitive_module_test.cpp index 58a86c1..2fe5083 100644 --- a/cs4home_core/test/cognitive_module_test.cpp +++ b/cs4home_core/test/cognitive_module_test.cpp @@ -75,6 +75,7 @@ TEST(cognitive_module_test, afferent_on_demand) // Configure topics for afferent component std::vector topics {"/image"}; + std::vector types {"sensor_msgs/msg/Image"}; // Load afferent component and verify successful loading auto [afferent, error_afferent] = load_component( @@ -83,6 +84,7 @@ TEST(cognitive_module_test, afferent_on_demand) // Set the topics parameter and configure afferent component node->set_parameter(rclcpp::Parameter("simple_image_input.topics", topics)); + node->set_parameter(rclcpp::Parameter("simple_image_input.types", types)); ASSERT_TRUE(afferent->configure()); // Publish test messages to the afferent component's subscribed topic @@ -101,13 +103,13 @@ TEST(cognitive_module_test, afferent_on_demand) // Retrieve and verify messages from the afferent component's queue for (int i = 0; i < 10; i++) { - auto in_msg = afferent->get_msg(); + auto in_msg = afferent->get_msg("/image"); ASSERT_NE(in_msg, nullptr); ASSERT_EQ(i, std::atoi(in_msg->header.frame_id.c_str())); } // Verify that further retrieval attempts return `nullptr` as the queue is now empty - auto in_msg = afferent->get_msg(); + auto in_msg = afferent->get_msg("/image"); ASSERT_EQ(in_msg, nullptr); } @@ -134,6 +136,8 @@ TEST(cognitive_module_test, afferent_on_subscription) // Setup topics and image storage std::vector topics {"/image"}; + std::vector types {"sensor_msgs/msg/Image"}; + std::vector> images; // Load afferent component and verify successful loading @@ -143,11 +147,13 @@ TEST(cognitive_module_test, afferent_on_subscription) // Set the topics parameter and configure afferent mode node->set_parameter(rclcpp::Parameter("simple_image_input.topics", topics)); - afferent->set_mode(cs4home_core::Afferent::CALLBACK); + node->set_parameter(rclcpp::Parameter("simple_image_input.types", types)); + afferent->set_mode("/image", cs4home_core::Afferent::CALLBACK); ASSERT_EQ(afferent->get_mode(), cs4home_core::Afferent::ONDEMAND); afferent->set_mode( + "/image", cs4home_core::Afferent::CALLBACK, [&images](std::unique_ptr msg) { images.push_back(std::move(msg)); @@ -211,6 +217,7 @@ TEST(cognitive_module_test, efferent) // Configure topics for efferent component std::vector topics {"/image"}; + std::vector types {"sensor_msgs/msg/Image"}; // Load efferent component and verify successful loading auto [efferent, error_efferent] = load_component( @@ -219,13 +226,15 @@ TEST(cognitive_module_test, efferent) // Set the topics parameter and configure efferent component node->set_parameter(rclcpp::Parameter("simple_image_output.topics", topics)); + node->set_parameter(rclcpp::Parameter("simple_image_output.types", types)); + ASSERT_TRUE(efferent->configure()); // Publish test messages via the efferent component for (int i = 0; i < 10; i++) { auto msg = std::make_unique(); msg->header.frame_id = std::to_string(i); - efferent->publish(std::move(msg)); + efferent->publish(0, std::move(msg)); exe.spin_some(); } @@ -275,6 +284,7 @@ TEST(cognitive_module_test, core) // Setup topics for afferent and efferent components std::vector in_topics {"/in_image"}; std::vector out_topics {"/out_image"}; + std::vector types {"sensor_msgs/msg/Image"}; // Load components auto [afferent, error_afferent] = load_component( @@ -289,7 +299,9 @@ TEST(cognitive_module_test, core) // Set parameters and configure components node->set_parameter(rclcpp::Parameter("simple_image_input.topics", in_topics)); + node->set_parameter(rclcpp::Parameter("simple_image_input.types", types)); node->set_parameter(rclcpp::Parameter("simple_image_output.topics", out_topics)); + node->set_parameter(rclcpp::Parameter("simple_image_output.types", types)); ASSERT_TRUE(afferent->configure()); ASSERT_TRUE(efferent->configure()); core->set_afferent(afferent); @@ -352,6 +364,7 @@ TEST(cognitive_module_test, core_cb) // Setup topics for afferent and efferent components std::vector in_topics {"/in_image"}; std::vector out_topics {"/out_image"}; + std::vector types {"sensor_msgs/msg/Image"}; // Load components auto [afferent, error_afferent] = load_component( @@ -366,7 +379,9 @@ TEST(cognitive_module_test, core_cb) // Set parameters and configure components node->set_parameter(rclcpp::Parameter("simple_image_input.topics", in_topics)); + node->set_parameter(rclcpp::Parameter("simple_image_input.types", types)); node->set_parameter(rclcpp::Parameter("simple_image_output.topics", out_topics)); + node->set_parameter(rclcpp::Parameter("simple_image_output.types", types)); ASSERT_TRUE(afferent->configure()); ASSERT_TRUE(efferent->configure()); core->set_afferent(afferent); @@ -423,7 +438,7 @@ TEST(cognitive_module_test, startup_simple) // Verify the number of parameters loaded from the configuration file auto params = cm1->list_parameters({}, 0); - ASSERT_EQ(params.names.size(), 7u); + ASSERT_EQ(params.names.size(), 8u); // Set up publisher and subscriber nodes for testing message processing auto pub_node = rclcpp::Node::make_shared("pub_node"); diff --git a/cs4home_core/test/config/startup_simple_1.yaml b/cs4home_core/test/config/startup_simple_1.yaml index ff72d90..d927b14 100644 --- a/cs4home_core/test/config/startup_simple_1.yaml +++ b/cs4home_core/test/config/startup_simple_1.yaml @@ -1,11 +1,32 @@ +master_1: + ros__parameters: + flows: [flow_1] + flow_1: [cognitive_module_1, cognitive_module_2] + cognitive_module_1: ros__parameters: core: image_filter afferent: simple_image_input simple_image_input: - topics: ["/image_raw"] + topics: ["/image_raw", "/camera_info"] + types: ["sensor_msgs/msg/Image", "sensor_msgs/msg/CameraInfo"] efferent: simple_image_output simple_image_output: topics: ["/detections"] + types: ["sensor_msgs/msg/Image"] + meta: default_meta + coupling: default_coupling + +cognitive_module_2: + ros__parameters: + core: image_filter + afferent: simple_image_input + simple_image_input: + topics: ["/detections"] + types: ["sensor_msgs/msg/Image"] + efferent: simple_image_output + simple_image_output: + topics: ["/detections2"] + types: ["sensor_msgs/msg/Image"] meta: default_meta coupling: default_coupling diff --git a/cs4home_core/test/master_test.cpp b/cs4home_core/test/master_test.cpp index 53a3361..725feea 100644 --- a/cs4home_core/test/master_test.cpp +++ b/cs4home_core/test/master_test.cpp @@ -14,10 +14,112 @@ #include "ament_index_cpp/get_package_share_directory.hpp" +#include "lifecycle_msgs/msg/state.hpp" +#include "sensor_msgs/msg/image.hpp" + #include "cs4home_core/Flow.hpp" +#include "cs4home_core/Master.hpp" #include "cs4home_core/CognitiveModule.hpp" #include "gtest/gtest.h" + +using namespace std::chrono_literals; + +TEST(master_test, flow_creation) +{ + auto flow1 = cs4home_core::Flow::make_shared( + "flow1", std::vector({"A", "B", "C", "D"})); + auto flow2 = cs4home_core::Flow::make_shared( + "flow2", std::vector({"A", "B", "C", "E"})); + + ASSERT_EQ(flow1->get_flow(), std::vector({"A", "B", "C", "D"})); + ASSERT_EQ(flow2->get_flow(), std::vector({"A", "B", "C", "E"})); +} + +TEST(master_test, master_creation) +{ + std::string pkgpath = ament_index_cpp::get_package_share_directory("cs4home_core"); + std::string config_file = pkgpath + "/config/startup_simple_1.yaml"; + + rclcpp::NodeOptions options; + options.arguments( + {"--ros-args", "--params-file", config_file}); + + auto master = cs4home_core::Master::make_shared("master_1", options); + auto cm1 = cs4home_core::CognitiveModule::make_shared("cognitive_module_1", options); + auto cm2 = cs4home_core::CognitiveModule::make_shared("cognitive_module_2", options); + + auto pub_node = rclcpp::Node::make_shared("pub_node"); + auto sub_node = rclcpp::Node::make_shared("sub_node"); + auto pub = pub_node->create_publisher("/image", 100); + std::vector images; + auto sub = sub_node->create_subscription( + "/detections2", 100, [&images](sensor_msgs::msg::Image msg) { + images.push_back(msg); + }); + + rclcpp::executors::SingleThreadedExecutor exe; + exe.add_node(master->get_node_base_interface()); + exe.add_node(cm1->get_node_base_interface()); + exe.add_node(cm2->get_node_base_interface()); + exe.add_node(pub_node); + exe.add_node(sub_node); + + + master->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE); + cm1->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE); + cm2->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE); + ASSERT_EQ(master->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + ASSERT_EQ(cm1->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + ASSERT_EQ(cm2->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + + const auto & flows = master->get_flows(); + ASSERT_EQ(flows.size(), 1u); + + ASSERT_NE(flows.find("flow_1"), flows.end()); + const auto & flow_1 = flows.at("flow_1"); + const auto & flow_1_cms = flow_1->get_flow(); + + ASSERT_EQ(flow_1_cms, std::vector({"cognitive_module_1", "cognitive_module_2"})); + + + master->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_ACTIVATE); + ASSERT_EQ(master->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE); + + { + auto start = master->now(); + while (cm1->now() - start < 1s) { + exe.spin_some(); + } + } + + // They should be activated by master, as it is part of a flow + ASSERT_EQ(cm1->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE); + ASSERT_EQ(cm2->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE); + + sensor_msgs::msg::Image msg; + for (int i = 0; i < 10; i++) { + msg.header.frame_id = std::to_string(i); + pub->publish(msg); + exe.spin_some(); + } + + auto start = cm1->now(); + while (cm1->now() - start < 1s) { + exe.spin_some(); + } + + master->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_DEACTIVATE); + ASSERT_EQ(master->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + ASSERT_EQ(cm1->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + ASSERT_EQ(cm2->get_current_state().id(), lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE); + + ASSERT_EQ(images.size(), 10); + for (int i = 0; i < 10; i++) { + ASSERT_EQ(i * 4, std::atoi(images[i].header.frame_id.c_str())); + } +} + /** * @brief Unit test for verifying the creation and structure of Flow instances. * @@ -27,13 +129,13 @@ */ TEST(flow_test, flow_creation) { - cs4home_core::Flow flow1({"A", "B", "C", "D"}); - cs4home_core::Flow flow2({"A", "B", "C", "E"}); + cs4home_core::Flow flow1("flow1", {"A", "B", "C", "D"}); + cs4home_core::Flow flow2("flow2", {"A", "B", "C", "E"}); // Check that the nodes in flow1 match the expected sequence - ASSERT_EQ(flow1.get_nodes(), std::vector({"A", "B", "C", "D"})); + ASSERT_EQ(flow1.get_flow(), std::vector({"A", "B", "C", "D"})); // Check that the nodes in flow2 match the expected sequence - ASSERT_EQ(flow2.get_nodes(), std::vector({"A", "B", "C", "E"})); + ASSERT_EQ(flow2.get_flow(), std::vector({"A", "B", "C", "E"})); } /**