XNSim/XNMonitor/TopicManager/TopicManager.h
2025-04-28 12:25:20 +08:00

315 lines
9.7 KiB
C++
Executable File

/**
* @file TopicManager.h
* @author jinchao
* @brief 主题管理类
* @version 1.0
* @date 2025-03-10
*
* @copyright Copyright (c) 2025 COMAC
*
*/
#pragma once
#include <QObject>
#include <QMap>
#include <QMutex>
#include <QMutexLocker>
#include <QScopedPointer>
#include "DataReaderListenerImpl.h"
#include "../TypeDefine.h"
#include <fastdds/dds/core/status/StatusMask.hpp>
/**
* @brief 主题管理类
*/
class TopicManager : public QObject
{
Q_OBJECT
public:
/**
* @brief 删除拷贝构造
*/
TopicManager(const TopicManager &) = delete;
/**
* @brief 删除赋值操作
*/
TopicManager &operator=(const TopicManager &) = delete;
/**
* @brief 获取单例
* @return TopicManager*: 主题管理类实例
*/
static TopicManager *Instance()
{
if (instance == nullptr) {
QMutexLocker locker(&instanceMutex);
if (instance == nullptr) { // 双重检查锁定
instance = new TopicManager();
}
}
return instance;
}
/**
* @brief 清理参与者
*/
static void cleanupParticipant()
{
QMutexLocker locker(&instanceMutex);
if (instance != nullptr) {
instance->clearAllTopic(); // 清理所有主题
if (instance->m_Participant != nullptr) {
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()
->delete_participant(instance->m_Participant); // 删除参与者
instance->m_Participant = nullptr; // 设置参与者为空
}
delete instance; // 删除单例
instance = nullptr; // 设置单例为空
}
}
private:
/**
* @brief 显式构造函数
* @param parent: 父对象
*/
explicit TopicManager(QObject *parent = nullptr) : QObject(parent)
{
XNParticipantQos participantQos;
participantQos.name("XNMonitor"); // 设置参与者名称
m_Participant =
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant(
10, participantQos); // 创建参与者
}
/**
* @brief 析构函数
*/
virtual ~TopicManager() {}
signals:
/**
* @brief 发送调试信息信号
* @param type: 类型
* @param message: 信息
*/
void sendDebugMessage(int type, const QString &message);
public:
/**
* @brief 注册发布者模板函数
* @tparam T: 类型
* @param topicName: 主题名称
* @return XNDataWriter*: 数据写入器
*/
template <typename T>
XNDataWriter *registerPublisher(const QString &topicName)
{
QMutexLocker locker(&m_Mutex);
if (!topics_.contains(topicName)) {
topics_[topicName] = TopicInfo(); // 创建主题信息
TopicInfo &tmp = topics_[topicName]; // 获取主题信息
XNTypeSupport typeSupport(new T()); // 创建类型支持
typeSupport.register_type(m_Participant); // 注册类型
tmp.topic = m_Participant->create_topic(
topicName.toStdString().c_str(), typeSupport.get_type_name(),
eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); // 创建主题
if (tmp.topic == nullptr) {
emit sendDebugMessage(
1, QString("Failed to create topic %1").arg(topicName)); // 发送调试信息
topics_.remove(topicName); // 移除主题
return nullptr; // 返回空
}
}
TopicInfo &topicInfo = topics_[topicName]; // 获取主题信息
if (topicInfo.publisher == nullptr) {
topicInfo.publisher = m_Participant->create_publisher(
eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT); // 创建发布者
if (topicInfo.publisher == nullptr) {
emit sendDebugMessage(
1, QString("Failed to create publisher %1").arg(topicName)); // 发送调试信息
return nullptr; // 返回空
}
}
if (topicInfo.dataWriter == nullptr) {
XNDataWriterQos dataWriterQos;
// 设置数据写入器的持久性策略, 使用瞬态本地持久性
dataWriterQos.durability().kind = eprosima::fastdds::dds::VOLATILE_DURABILITY_QOS;
// 设置数据写入器的生命周期策略, 设置为5秒
dataWriterQos.lifespan().duration = eprosima::fastdds::dds::Duration_t(5, 0);
topicInfo.dataWriter = topicInfo.publisher->create_datawriter(
topicInfo.topic, dataWriterQos); // 创建数据写入器
if (topicInfo.dataWriter == nullptr) {
emit sendDebugMessage(
1, QString("Failed to create data writer %1").arg(topicName)); // 发送调试信息
topics_.remove(topicName); // 移除主题
return nullptr; // 返回空
}
}
return topicInfo.dataWriter; // 返回数据写入器
}
/**
* @brief 注销发布者
* @param topicName: 主题名称
*/
void unregisterPublisher(const QString &topicName)
{
QMutexLocker locker(&m_Mutex);
if (topics_.contains(topicName)) {
TopicInfo &topicInfo = topics_[topicName]; // 获取主题信息
if (topicInfo.dataWriter != nullptr) {
topicInfo.publisher->delete_datawriter(topicInfo.dataWriter); // 删除数据写入器
topicInfo.dataWriter = nullptr; // 设置数据写入器为空
}
if (topicInfo.publisher != nullptr) {
m_Participant->delete_publisher(topicInfo.publisher); // 删除发布者
topicInfo.publisher = nullptr; // 设置发布者为空
}
if (topicInfo.publisher == nullptr && topicInfo.subscriber == nullptr
&& topicInfo.topic != nullptr) {
m_Participant->delete_topic(topicInfo.topic); // 删除主题
topicInfo.topic = nullptr; // 设置主题为空
topics_.remove(topicName); // 移除主题
}
}
}
/**
* @brief 注册订阅者模板函数
* @tparam T: 类型
* @param topicName: 主题名称
* @param fun: 回调函数
*/
template <typename T>
void registerSubscriber(const QString &topicName,
std::function<void(const typename T::type &)> fun)
{
QMutexLocker locker(&m_Mutex);
if (!topics_.contains(topicName)) {
topics_[topicName] = TopicInfo(); // 创建主题信息
TopicInfo &tmp = topics_[topicName]; // 获取主题信息
XNTypeSupport typeSupport(new T()); // 创建类型支持
typeSupport.register_type(m_Participant); // 注册类型
tmp.topic = m_Participant->create_topic(
topicName.toStdString().c_str(), typeSupport.get_type_name(),
eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); // 创建主题
if (tmp.topic == nullptr) {
emit sendDebugMessage(
1, QString("Failed to create topic %1").arg(topicName)); // 发送调试信息
topics_.remove(topicName); // 移除主题
return; // 返回
}
}
TopicInfo &topicInfo = topics_[topicName]; // 获取主题信息
if (topicInfo.subscriber == nullptr) {
topicInfo.subscriber = m_Participant->create_subscriber(
eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT); // 创建订阅者
if (topicInfo.subscriber == nullptr) {
emit sendDebugMessage(
1, QString("Failed to create subscriber %1").arg(topicName)); // 发送调试信息
return; // 返回
}
}
if (topicInfo.dataReader == nullptr) {
XNDataReaderQos dataReaderQos;
dataReaderQos.durability().kind =
eprosima::fastdds::dds::VOLATILE_DURABILITY_QOS; // 设置数据读取器的持久性策略
topicInfo.listener =
new DataReaderListenerImpl<typename T::type>(fun); // 创建数据读取器监听器
topicInfo.dataReader = topicInfo.subscriber->create_datareader(
topicInfo.topic, dataReaderQos, topicInfo.listener); // 创建数据读取器
if (topicInfo.dataReader == nullptr) {
emit sendDebugMessage(
1, QString("Failed to create data reader %1").arg(topicName)); // 发送调试信息
return; // 返回
}
} else {
auto oldListener = topicInfo.dataReader->get_listener(); // 获取旧的监听器
topicInfo.listener =
new DataReaderListenerImpl<typename T::type>(fun); // 创建新的监听器
topicInfo.dataReader->set_listener(
topicInfo.listener,
eprosima::fastdds::dds::StatusMask::all()); // 设置新的监听器
delete oldListener; // 删除旧的监听器
}
}
/**
* @brief 注销订阅者
* @param topicName: 主题名称
*/
void unregisterSubscriber(const QString &topicName)
{
QMutexLocker locker(&m_Mutex);
if (topics_.contains(topicName)) {
TopicInfo &topicInfo = topics_[topicName]; // 获取主题信息
if (topicInfo.dataReader != nullptr) {
topicInfo.subscriber->delete_datareader(topicInfo.dataReader); // 删除数据读取器
topicInfo.dataReader = nullptr; // 设置数据读取器为空
}
if (topicInfo.listener != nullptr) {
delete topicInfo.listener; // 删除监听器
topicInfo.listener = nullptr; // 设置监听器为空
}
if (topicInfo.subscriber != nullptr) {
m_Participant->delete_subscriber(topicInfo.subscriber); // 删除订阅者
topicInfo.subscriber = nullptr; // 设置订阅者为空
}
if (topicInfo.subscriber == nullptr && topicInfo.publisher == nullptr
&& topicInfo.topic != nullptr) {
m_Participant->delete_topic(topicInfo.topic); // 删除主题
topicInfo.topic = nullptr; // 设置主题为空
topics_.remove(topicName); // 移除主题
emit sendDebugMessage(
0, QString("unregisterSubscriber %1").arg(topicName)); // 发送调试信息
}
}
}
private:
/**
* @brief 清除所有主题
*/
void clearAllTopic()
{
QMutexLocker locker(&m_Mutex);
if (m_Participant != nullptr) {
while (topics_.size() > 0) {
emit sendDebugMessage(
0, QString("clearAllTopic %1").arg(topics_.begin().key())); // 发送调试信息
unregisterPublisher(topics_.begin().key()); // 注销发布者
unregisterSubscriber(topics_.begin().key()); // 注销订阅者
}
}
}
private:
/**
* @brief 单例指针
*/
static TopicManager *instance;
/**
* @brief 单例互斥锁
*/
static QMutex instanceMutex;
/**
* @brief 参与者
*/
XNParticipant *m_Participant = nullptr;
/**
* @brief 主题映射
*/
QMap<QString, TopicInfo> topics_;
/**
* @brief 主题访问互斥锁
*/
QMutex m_Mutex;
};