Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ namespace SplineTools
inline constexpr const char* SplineSubscriberComponentTypeId = "{89B8A92A-8F17-4C30-AE0D-6B088C133283}";
inline constexpr const char* SplineSubscriberConfigTypeId = "{44317FD2-51A1-41CA-BA44-F8BCAE9757CE}";

inline constexpr const char* SplinePublisherComponentTypeId = "{29C02686-04F6-416D-8F47-D2456A3E114C}";
inline constexpr const char* SplinePublisherConfigTypeId = "{DC7AC312-0F47-4EF2-A1B7-02E8716CF4EE}";
} // namespace SplineTools
149 changes: 149 additions & 0 deletions Gems/RobotecSplineTools/Code/Source/Clients/SplinePublisher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#include "SplinePublisher.h"

#include <ROS2/Frame/ROS2FrameComponent.h>
#include <ROS2/ROS2Bus.h>
#include <ROS2/Utilities/ROS2Names.h>

namespace SplineTools
{
SplinePublisherConfiguration::SplinePublisherConfiguration()
{
m_TopicConfig.m_type = "nav_msgs::msg::Path";
m_TopicConfig.m_topic = "spline_path";
}

void SplinePublisherConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SplinePublisherConfiguration>()->Version(0)->Field(
"m_topicName", &SplinePublisherConfiguration::m_TopicConfig);
if (auto editContext = serializeContext->GetEditContext())
{
editContext
->Class<SplinePublisherConfiguration>(
"SplinePublisherConfiguration", "Configuration for the SplineSubscriber component")
->ClassElement(AZ::Edit::ClassElements::Group, "SplineSubscriber Configuration")
->DataElement(
AZ::Edit::UIHandlers::Default, &SplinePublisherConfiguration::m_TopicConfig, "Topic Config", "Topic Config");
}
}
}

void SplinePublisher::Reflect(AZ::ReflectContext* context)
{
SplinePublisherConfiguration::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SplinePublisher, AZ::Component>()->Version(0)->Field("m_config", &SplinePublisher::m_config);
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<SplinePublisher>("SplinePathPublisher", "Enables to publish spline as a ros2 path.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "SplinePathPublisher")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
->Attribute(AZ::Edit::Attributes::Category, "RobotecTools")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(
AZ::Edit::UIHandlers::Default,
&SplinePublisher::m_config,
"Configuration",
"Configuration for the SplinePathPublisher component");
}
}
}

SplinePublisher::SplinePublisher(const SplinePublisherConfiguration& config)
: m_config(config)
{
}

void SplinePublisher::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("SplineService"));
required.push_back(AZ_CRC_CE("ROS2Frame"));
}

void SplinePublisher::Activate()
{
const ROS2::ROS2FrameComponent* ros2Frame = GetEntity()->FindComponent<ROS2::ROS2FrameComponent>();
if (!ros2Frame)
{
AZ_Warning("SplinePublisher::Activate", false, "ROS2 Frame Component is not available!");
return;
}

AZStd::string frameNamespace = ros2Frame->GetNamespace();
if (!frameNamespace.empty())
{
m_config.m_TopicConfig.m_topic = AZStd::string::format("%s/%s", frameNamespace.c_str(), m_config.m_TopicConfig.m_topic.c_str());
}

// Create the ROS2 Publisher
auto ros2Node = ROS2::ROS2Interface::Get()->GetNode();
if (ros2Node)
{
m_publisher =
ros2Node->create_publisher<nav_msgs::msg::Path>(m_config.m_TopicConfig.m_topic.c_str(), m_config.m_TopicConfig.GetQoS());

AZ::TickBus::Handler::BusConnect();
}
}

void SplinePublisher::Deactivate()
{
AZ::TickBus::Handler::BusDisconnect();
m_publisher.reset();
}

void SplinePublisher::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
PublishSplineAsPath();
}

void SplinePublisher::PublishSplineAsPath() const
{
if (!m_publisher)
{
return;
}

const ROS2::ROS2FrameComponent* ros2Frame = GetEntity()->FindComponent<ROS2::ROS2FrameComponent>();
if (!ros2Frame)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, the ros2Frame can be assumed to exist because you connect to Tick bus after the check in line 68.

As far as I know, you can't remove/add components when entity is active, therefore we can be sure that nothing strange happen between the ticks (without deactivate/activate and frame re-check). Additionally, you can save the reference and reuse it here if needed - no need to find component in every tick.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I've replaced it with AZ_Assert check.
ros2FramePtr now is initialized OnActivate and reused in PublishSplinePath.

{
AZ_Warning("SplinePublisher::PublishSplineAsPath", false, "ROS2 Frame Component is not available!");
return;
}

nav_msgs::msg::Path pathMessage;
pathMessage.header.frame_id = ros2Frame->GetFrameID().data();
pathMessage.header.stamp = ROS2::ROS2Interface::Get()->GetROSTimestamp();

// Retrieve spline data
AZStd::shared_ptr<AZ::Spline> spline;
LmbrCentral::SplineComponentRequestBus::EventResult(spline, GetEntityId(), &LmbrCentral::SplineComponentRequests::GetSpline);

if (!spline)
{
AZ_Warning("SplinePublisher::PublishSplineAsPath", false, "Spline not found. Cannot generate spline path.");
return;
}

// Retrieve vertices from the spline
const size_t vertexCount = spline->GetVertexCount();
for (size_t i = 0; i < vertexCount; ++i)
{
const AZ::Vector3& vertex = spline->GetVertex(i);

// Use emplace_back to construct PoseStamped in-place
pathMessage.poses.emplace_back();
auto& poseStamped = pathMessage.poses.back();

// Set the pose values directly
poseStamped.pose.position.x = vertex.GetX();
poseStamped.pose.position.y = vertex.GetY();
poseStamped.pose.position.z = vertex.GetZ();
}

m_publisher->publish(pathMessage);
}
} // namespace SplineTools
52 changes: 52 additions & 0 deletions Gems/RobotecSplineTools/Code/Source/Clients/SplinePublisher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#include <AzCore/Component/Component.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <LmbrCentral/Shape/SplineComponentBus.h>
#include <ROS2/Communication/TopicConfiguration.h>
#include <SplineTools/SplineToolsTypeIds.h>

#include <nav_msgs/msg/path.hpp>
#include <rclcpp/rclcpp.hpp>

namespace SplineTools
{
struct SplinePublisherConfiguration
{
SplinePublisherConfiguration();

AZ_TYPE_INFO(SplinePublisherConfiguration, SplinePublisherConfigTypeId);
static void Reflect(AZ::ReflectContext* context);
ROS2::TopicConfiguration m_TopicConfig{ rclcpp::ServicesQoS() };
};

class SplinePublisher
: public AZ::Component
, public AZ::TickBus::Handler
{
public:
AZ_COMPONENT(SplinePublisher, SplinePublisherComponentTypeId);

static void Reflect(AZ::ReflectContext* context);

SplinePublisher() = default;
~SplinePublisher() override = default;
explicit SplinePublisher(const SplinePublisherConfiguration& config);

static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);

// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;

// AZ::TickBus::Handler interface implementation
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;

private:
void PublishSplineAsPath() const;

SplinePublisherConfiguration m_config;
rclcpp::Publisher<nav_msgs::msg::Path>::SharedPtr m_publisher;
};
} // namespace SplineTools
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@

#include "SplineToolsModuleInterface.h"

#include <AzCore/Memory/Memory.h>

#include <SplineTools/SplineToolsTypeIds.h>

#include <Clients/SplinePublisher.h>
#include <Clients/SplineSubscriber.h>
#include <Clients/SplineToolsSystemComponent.h>
#include <Clients/VisualizeSplineComponent.h>
Expand All @@ -24,7 +26,8 @@ namespace SplineTools
m_descriptors.end(),
{ SplineToolsSystemComponent::CreateDescriptor(),
VisualizeSplineComponent::CreateDescriptor(),
SplineSubscriber::CreateDescriptor() });
SplineSubscriber::CreateDescriptor(),
SplinePublisher::CreateDescriptor() });
}

AZ::ComponentTypeList SplineToolsModuleInterface::GetRequiredSystemComponents() const
Expand Down
22 changes: 12 additions & 10 deletions Gems/RobotecSplineTools/Code/splinetools_private_files.cmake
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@

set(FILES
Source/SplineToolsModuleInterface.cpp
Source/SplineToolsModuleInterface.h
Source/Clients/SplineToolsSystemComponent.cpp
Source/Clients/SplineToolsSystemComponent.h
Source/Clients/VisualizeSplineComponent.cpp
Source/Clients/VisualizeSplineComponent.h
Source/Clients/SplineSubscriber.h
Source/Clients/SplineSubscriber.cpp
Source/Clients/SplineSubscriberConfig.h
Source/Clients/SplineSubscriberConfig.cpp
Source/Clients/SplinePublisher.cpp
Source/Clients/SplinePublisher.h
Source/Clients/SplineSubscriber.cpp
Source/Clients/SplineSubscriber.h
Source/Clients/SplineSubscriberConfig.cpp
Source/Clients/SplineSubscriberConfig.h
Source/Clients/SplineToolsSystemComponent.cpp
Source/Clients/SplineToolsSystemComponent.h
Source/Clients/VisualizeSplineComponent.cpp
Source/Clients/VisualizeSplineComponent.h
Source/SplineToolsModuleInterface.cpp
Source/SplineToolsModuleInterface.h
)