Program Listing for File ros_msgs_plugin.hpp
↰ Return to documentation for file (include/cx_ros_msgs_plugin/ros_msgs_plugin.hpp)
// Copyright (c) 2024-2026 Carologistics
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CX_ROS_MSGS_PLUGIN__ROS_MSGS_PLUGIN_HPP_
#define CX_ROS_MSGS_PLUGIN__ROS_MSGS_PLUGIN_HPP_
#include <map>
#include <memory>
#include <queue>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "cx_plugin/clips_plugin.hpp"
#include "rosidl_typesupport_introspection_cpp/message_introspection.hpp"
#ifdef HAVE_GENERIC_CLIENT
#include "rclcpp/generic_client.hpp"
#endif
#ifdef HAVE_GENERIC_SERVICE
#include "rclcpp/generic_service.hpp"
#endif
#ifdef HAVE_GENERIC_ACTION_CLIENT
#include "action_msgs/srv/cancel_goal.hpp"
#include "rclcpp_action/generic_client.hpp"
#endif
namespace cx
{
class RosMsgsPlugin : public ClipsPlugin
{
public:
RosMsgsPlugin();
~RosMsgsPlugin();
void initialize();
void finalize();
bool clips_env_init(std::shared_ptr<clips::Environment> & env) override;
bool clips_env_destroyed(std::shared_ptr<clips::Environment> & env) override;
private:
struct MessageInfo
{
std::shared_ptr<void> external_created_ptr;
void * msg_ptr;
const rosidl_typesupport_introspection_cpp::MessageMembers * members;
bool is_sub_msg;
rcutils_allocator_t allocator;
MessageInfo(
const rosidl_typesupport_introspection_cpp::MessageMembers * members,
void * parent_ptr = nullptr)
: msg_ptr(nullptr), members(members)
{
is_sub_msg = (parent_ptr != nullptr);
if (parent_ptr) {
msg_ptr = parent_ptr;
} else {
// Allocate memory for the message
allocator = rcutils_get_default_allocator();
msg_ptr = allocator.zero_allocate(1, members->size_of_, allocator.state);
if (members->init_function) {
members->init_function(
msg_ptr,
rosidl_runtime_cpp::MessageInitialization::ALL); // Full
// initialization
}
}
}
MessageInfo(
const rosidl_typesupport_introspection_cpp::MessageMembers * members,
std::shared_ptr<void> external_created_ptr)
: external_created_ptr(external_created_ptr),
msg_ptr(external_created_ptr.get()),
members(members)
{
is_sub_msg = true;
}
~MessageInfo()
{
if (!is_sub_msg) {
// Finalize the message and free memory
if (members && members->fini_function) {
members->fini_function(msg_ptr);
}
allocator.deallocate(msg_ptr, allocator.state);
}
}
// Disable copy constructor and copy assignment to prevent double freeing
MessageInfo(const MessageInfo &) = delete;
MessageInfo & operator=(const MessageInfo &) = delete;
// Enable move constructor and move assignment for efficiency
MessageInfo(MessageInfo && other) noexcept
: external_created_ptr(other.external_created_ptr),
msg_ptr(other.msg_ptr),
members(other.members),
is_sub_msg(other.is_sub_msg),
allocator(other.allocator)
{
RCLCPP_WARN(rclcpp::get_logger("MESSAGE INFO"), "ASSIGN");
other.msg_ptr = nullptr;
other.external_created_ptr.reset();
other.members = nullptr;
other.is_sub_msg = false;
}
};
struct MessageInfoHasher
{
std::size_t operator()(const MessageInfo & key) const
{
return std::hash<void *>()(key.external_created_ptr.get()) ^
std::hash<void *>()(key.msg_ptr) ^
std::hash<const rosidl_typesupport_introspection_cpp::MessageMembers *>()(key.members);
}
};
clips::UDFValue create_message(clips::Environment * env, const std::string & type);
void destroy_msg(void * msg);
clips::UDFValue ros_msg_member_to_udf_value(
clips::Environment * env, std::shared_ptr<MessageInfo> & msg_info,
const rosidl_typesupport_introspection_cpp::MessageMember & member);
std::shared_ptr<MessageInfo> process_nested_msg(
void * nested_msg, const rosidl_typesupport_introspection_cpp::MessageMember & member);
clips::CLIPSValue ros_to_clips_value(clips::Environment * env, void * val, uint8_t ros_type);
clips::UDFValue get_field(
clips::Environment * env, void * deserialized_msg, const std::string & field,
clips::UDFContext * udfc);
void udf_value_to_ros_message_member(
clips::Environment * env, void * deserialized_msg,
const rosidl_typesupport_introspection_cpp::MessageMember & member, clips::UDFValue & val);
void clips_to_ros_value(
clips::Environment * env, const clips::CLIPSValue & val,
const rosidl_typesupport_introspection_cpp::MessageMember & member, uint8_t * field_ptr);
void set_field(
clips::Environment * env, void * deserialized_msg, const std::string & field,
clips::UDFValue & val, clips::UDFContext * udfc);
void subscribe_to_topic(
clips::Environment * env, const std::string & topic_name, const std::string & topic_type);
std::shared_ptr<RosMsgsPlugin::MessageInfo> create_deserialized_msg(
const std::string & topic_type);
void topic_callback(
std::shared_ptr<const rclcpp::SerializedMessage> msg, const std::string & topic_name,
const std::string & topic_type, clips::Environment * env);
void unsubscribe_from_topic(clips::Environment * env, const std::string & topic_name);
void create_new_publisher(
clips::Environment * env, const std::string & topic_name, const std::string & topic_type);
void destroy_publisher(clips::Environment * env, const std::string & topic_name);
void publish_to_topic(
clips::Environment * env, void * deserialized_msg, const std::string & topic_name);
std::string get_msg_type(const rosidl_typesupport_introspection_cpp::MessageMembers * members);
const rosidl_typesupport_introspection_cpp::MessageMembers * get_msg_members(
const std::string & members);
std::shared_ptr<MessageInfo> deserialize_msg(
std::shared_ptr<const rclcpp::SerializedMessage> msg, const std::string & msg_type);
rclcpp::SerializedMessage serialize_msg(
std::shared_ptr<MessageInfo> msg_info, const std::string & msg_type);
void move_field_to_parent(
void * parent_msg, const rosidl_typesupport_introspection_cpp::MessageMember * parent_member,
void * source_msg);
#ifdef HAVE_GENERIC_CLIENT
void finalize_generic_client_bindings();
void init_generic_client_bindings(std::shared_ptr<clips::Environment> env);
void destroy_generic_client_bindings(std::shared_ptr<clips::Environment> env);
clips::UDFValue create_request(clips::Environment * env, const std::string & service_type);
void create_new_client(
clips::Environment * env, const std::string & service_name, const std::string & service_type);
void destroy_client(clips::Environment * env, const std::string & service_name);
clips::UDFValue send_request(
clips::Environment * env, void * deserialized_msg, const std::string & service_name);
#endif
#ifdef HAVE_GENERIC_SERVICE
void finalize_generic_service_bindings();
void init_generic_service_bindings(std::shared_ptr<clips::Environment> env);
void destroy_generic_service_bindings(std::shared_ptr<clips::Environment> env);
void create_new_service(
clips::Environment * env, const std::string & service_name, const std::string & service_type);
void destroy_service(clips::Environment * env, const std::string & service_name);
void service_callback(
clips::Environment * env, const std::string service_name,
rclcpp::GenericService::SharedRequest request, rclcpp::GenericService::SharedResponse response);
#endif
#ifdef HAVE_GENERIC_ACTION_CLIENT
void finalize_generic_action_client_bindings();
void init_generic_action_client_bindings(std::shared_ptr<clips::Environment> env);
void destroy_generic_action_client_bindings(std::shared_ptr<clips::Environment> env);
void create_new_action_client(
clips::Environment * env, const std::string & server_name, const std::string & server_type);
void destroy_action_client(clips::Environment * env, const std::string & server_name);
void destroy_client_goal_handle(void * client_goal_handle);
clips::UDFValue client_goal_handle_get_goal_id(
clips::Environment * env, clips::UDFContext * udfc, void * client_goal_handle);
clips::UDFValue client_goal_handle_get_goal_stamp(
clips::Environment * env, clips::UDFContext * udfc, void * client_goal_handle);
clips::UDFValue client_goal_handle_get_status(
clips::Environment * env, clips::UDFContext * udfc, void * client_goal_handle);
clips::UDFValue create_goal(clips::Environment * env, const std::string & action_type);
void async_send_new_goal(
clips::Environment * env, const std::string & action_server, void * goal_request);
void deep_copy_msg(
const void * src, void * dest, const rosidl_typesupport_introspection_cpp::MessageMembers *);
std::shared_ptr<RosMsgsPlugin::MessageInfo> store_generic_action_cancel_response(
std::shared_ptr<action_msgs::srv::CancelGoal_Response> cancel_response);
void process_cancel_response(
clips::Environment * env, const std::string & server_name,
std::shared_ptr<rclcpp_action::GenericClientGoalHandle> goal_handle,
const action_msgs::srv::CancelGoal::Response::SharedPtr & response);
#endif
std::unique_ptr<rclcpp::Logger> logger_;
rclcpp::CallbackGroup::SharedPtr cb_group_;
std::unordered_set<std::string> function_names_;
// env -> (topic_name -> subscription)
std::map<std::string, std::map<std::string, std::shared_ptr<rclcpp::GenericSubscription>>>
subscriptions_;
// env -> (topic_name -> publisher)
std::map<std::string, std::map<std::string, std::shared_ptr<rclcpp::GenericPublisher>>>
publishers_;
std::unordered_map<std::string, std::shared_ptr<rcpputils::SharedLibrary>> libs_;
std::mutex map_mtx_;
bool stop_flag_;
// MessageInfo* -> shared_ptr holding the MessageInfo*
std::unordered_map<void *, std::shared_ptr<MessageInfo>> messages_;
// parent msg MessageInfo* -> nested msg MessageInfo*
std::unordered_map<void *, std::vector<std::shared_ptr<MessageInfo>>> sub_messages_;
// message_type -> message_info
std::unordered_map<std::string, const rosidl_message_type_support_t *> type_support_cache_;
std::queue<std::function<void()>> task_queue_;
std::thread clips_worker_thread_;
std::condition_variable cv_;
std::mutex queue_mutex_;
#ifdef HAVE_GENERIC_CLIENT
// env -> (service_name -> client)
std::map<std::string, std::map<std::string, std::shared_ptr<rclcpp::GenericClient>>> clients_;
// env -> (service_name -> service_type)
std::map<std::string, std::unordered_map<std::string, std::string>> service_types_;
std::unordered_map<std::string, const rosidl_service_type_support_t *>
service_type_support_cache_;
#endif
#ifdef HAVE_GENERIC_SERVICE
// env -> (service_name -> service)
std::map<std::string, std::map<std::string, std::shared_ptr<rclcpp::GenericService>>> services_;
#endif
#ifdef HAVE_GENERIC_ACTION_CLIENT
// env -> (action_server_name -> client)
std::map<std::string, std::map<std::string, std::shared_ptr<rclcpp_action::GenericClient>>>
action_clients_;
// env -> (action_server_name -> action_type)
std::map<std::string, std::unordered_map<std::string, std::string>> action_types_;
std::unordered_map<std::string, const rosidl_action_type_support_t *> action_type_support_cache_;
std::unordered_map<void *, std::shared_ptr<rclcpp_action::GenericClientGoalHandle>>
client_goal_handles_;
#endif
};
} // namespace cx
#endif // CX_ROS_MSGS_PLUGIN__ROS_MSGS_PLUGIN_HPP_