2025-04-28 12:25:20 +08:00
|
|
|
|
#pragma once
|
|
|
|
|
|
2025-05-22 08:53:29 +08:00
|
|
|
|
#include "XNFramework.h"
|
|
|
|
|
#include "XNDDSManager.h"
|
2025-05-21 12:17:50 +08:00
|
|
|
|
#include "XNByteArray.h"
|
|
|
|
|
#include "XNTypeTraits.h"
|
|
|
|
|
#include <stdexcept>
|
2025-05-19 09:49:54 +08:00
|
|
|
|
|
|
|
|
|
// 定义UDP包的最大大小
|
|
|
|
|
constexpr size_t MAX_UDP_PACKET_SIZE = 40000;
|
|
|
|
|
|
|
|
|
|
class XNDDSInterface
|
2025-04-28 12:25:20 +08:00
|
|
|
|
{
|
|
|
|
|
public:
|
2025-05-19 09:49:54 +08:00
|
|
|
|
XNDDSInterface() = default;
|
|
|
|
|
virtual ~XNDDSInterface() = default;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* @brief 初始化
|
|
|
|
|
* @param framework: 框架
|
|
|
|
|
*/
|
2025-05-22 08:53:29 +08:00
|
|
|
|
virtual void Initialize(XNFrameworkPtr framework, uint32_t modelID) = 0;
|
2025-05-21 12:17:50 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 获取该接口的UDP包
|
|
|
|
|
* @return 字节数组
|
|
|
|
|
*/
|
|
|
|
|
XNByteArray getUDPPackage();
|
|
|
|
|
|
|
|
|
|
/**
|
2025-05-22 08:53:29 +08:00
|
|
|
|
* @brief 批量获取指定变量的数据
|
|
|
|
|
* @param varNames: 变量名列表
|
|
|
|
|
* @return: 变量名到数据的映射
|
2025-05-21 12:17:50 +08:00
|
|
|
|
*/
|
2025-05-22 08:53:29 +08:00
|
|
|
|
std::unordered_map<std::string, std::string> getStringData(std::vector<std::string> varNames);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 批量设置指定变量的数据
|
|
|
|
|
* @param data: 变量名到数据的映射
|
|
|
|
|
*/
|
|
|
|
|
void setDataByString(std::unordered_map<std::string, std::string> data);
|
2025-05-19 09:49:54 +08:00
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
protected:
|
|
|
|
|
/**
|
|
|
|
|
* @brief 获取指定变量的字节数据,用于网络通信
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @return 字节数组
|
|
|
|
|
*/
|
|
|
|
|
template <typename T>
|
|
|
|
|
XNByteArray getByteArray(eprosima::fastcdr::optional<T> data)
|
|
|
|
|
{
|
|
|
|
|
XNByteArray result(getTypeSize<T>());
|
2025-05-19 09:49:54 +08:00
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
|
|
|
|
if (data) {
|
|
|
|
|
std::memcpy(result.data(), &data.value(), sizeof(T));
|
2025-05-19 09:49:54 +08:00
|
|
|
|
} else {
|
2025-05-21 12:17:50 +08:00
|
|
|
|
T zero = 0;
|
|
|
|
|
std::memcpy(result.data(), &zero, sizeof(T));
|
2025-05-19 09:49:54 +08:00
|
|
|
|
}
|
2025-05-21 12:17:50 +08:00
|
|
|
|
} else if constexpr (is_std_array_v<T>) {
|
|
|
|
|
if (data) {
|
|
|
|
|
return getByteArrayFromStdArray(data.value());
|
|
|
|
|
} else {
|
|
|
|
|
T zero = {};
|
|
|
|
|
return getByteArrayFromStdArray(zero);
|
2025-04-28 12:25:20 +08:00
|
|
|
|
}
|
2025-05-21 12:17:50 +08:00
|
|
|
|
} else {
|
|
|
|
|
static_assert(std::is_arithmetic_v<T> || is_std_array_v<T>,
|
|
|
|
|
"Type must be arithmetic or std::array");
|
2025-04-28 12:25:20 +08:00
|
|
|
|
}
|
2025-05-19 09:49:54 +08:00
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
return result;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief 设置指定变量的字节数据,用于网络通信
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @param byteArray: 字节数组
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
template <typename T>
|
2025-05-21 12:17:50 +08:00
|
|
|
|
void setByteArray(eprosima::fastcdr::optional<T> &data, const XNByteArray &byteArray)
|
2025-05-20 15:39:40 +08:00
|
|
|
|
{
|
2025-05-21 12:17:50 +08:00
|
|
|
|
if (byteArray.size() < getTypeSize<T>())
|
2025-05-20 15:39:40 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
2025-05-21 12:17:50 +08:00
|
|
|
|
T temp;
|
|
|
|
|
std::memcpy(&temp, byteArray.data(), sizeof(T));
|
|
|
|
|
data = temp;
|
2025-05-20 15:39:40 +08:00
|
|
|
|
} else if constexpr (is_std_array_v<T>) {
|
2025-05-21 12:17:50 +08:00
|
|
|
|
if (!data) {
|
|
|
|
|
data = T{};
|
|
|
|
|
}
|
|
|
|
|
setByteArrayFromStdArray(data.value(), byteArray);
|
|
|
|
|
} else {
|
|
|
|
|
static_assert(std::is_arithmetic_v<T> || is_std_array_v<T>,
|
|
|
|
|
"Type must be arithmetic or std::array");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 获取指定数组变量的字节数据,用于网络通信
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @return 字节数组
|
|
|
|
|
*/
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
|
XNByteArray getByteArrayFromStdArray(const std::array<T, N> &data)
|
|
|
|
|
{
|
|
|
|
|
XNByteArray result(getTypeSize<T>() * N);
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < N; ++i) {
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
|
|
|
|
std::memcpy(result.data() + i * getTypeSize<T>(), &data[i], getTypeSize<T>());
|
2025-05-20 15:39:40 +08:00
|
|
|
|
} else {
|
2025-05-21 12:17:50 +08:00
|
|
|
|
XNByteArray subArray = getByteArrayFromStdArray(data[i]);
|
|
|
|
|
std::memcpy(result.data() + i * getTypeSize<T>(), subArray.data(),
|
|
|
|
|
getTypeSize<T>());
|
2025-05-20 15:39:40 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-21 12:17:50 +08:00
|
|
|
|
|
|
|
|
|
return result;
|
2025-05-20 15:39:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief 设置指定数组变量的字节数据,用于网络通信
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @param byteArray: 字节数组
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
template <typename T, std::size_t N>
|
2025-05-21 12:17:50 +08:00
|
|
|
|
void setByteArrayFromStdArray(std::array<T, N> &data, const XNByteArray &byteArray)
|
2025-05-20 15:39:40 +08:00
|
|
|
|
{
|
2025-05-21 12:17:50 +08:00
|
|
|
|
if (byteArray.size() < getTypeSize<T>() * N)
|
2025-05-20 15:39:40 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < N; ++i) {
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
2025-05-21 12:17:50 +08:00
|
|
|
|
std::memcpy(&data[i], byteArray.data() + i * getTypeSize<T>(), getTypeSize<T>());
|
2025-05-20 15:39:40 +08:00
|
|
|
|
} else {
|
2025-05-21 12:17:50 +08:00
|
|
|
|
XNByteArray subArray(byteArray.data() + i * getTypeSize<T>(), getTypeSize<T>());
|
|
|
|
|
setByteArrayFromStdArray(data[i], subArray);
|
2025-05-20 15:39:40 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief 获取指定变量的字符串数据,用于JSON前端
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @return: 数据(字符串格式)
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
template <typename T>
|
|
|
|
|
std::string getString(eprosima::fastcdr::optional<T> data)
|
|
|
|
|
{
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
|
|
|
|
if (data) {
|
|
|
|
|
return std::to_string(data.value());
|
|
|
|
|
} else {
|
|
|
|
|
return std::to_string(0);
|
|
|
|
|
}
|
2025-05-22 08:53:29 +08:00
|
|
|
|
} else if constexpr (is_std_array_v<T>) {
|
2025-05-20 15:39:40 +08:00
|
|
|
|
if (data) {
|
|
|
|
|
return getStringFromStdArray(data.value());
|
|
|
|
|
} else {
|
|
|
|
|
T zero = {};
|
|
|
|
|
return getStringFromStdArray(zero);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 08:53:29 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief 通过字符串数据设置指定变量,用于JSON前端
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @param value: 字符串数据
|
|
|
|
|
*/
|
|
|
|
|
template <typename T>
|
|
|
|
|
void setDataFromString(eprosima::fastcdr::optional<T> &data, const std::string &value)
|
|
|
|
|
{
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
|
|
|
|
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
|
|
|
|
data = std::stod(value);
|
|
|
|
|
} else {
|
|
|
|
|
data = std::stoll(value);
|
|
|
|
|
}
|
|
|
|
|
} else if constexpr (is_std_array_v<T>) {
|
|
|
|
|
// 解析输入字符串
|
|
|
|
|
std::stringstream ss(value);
|
|
|
|
|
std::string item;
|
|
|
|
|
std::vector<std::string> items;
|
|
|
|
|
while (std::getline(ss, item, ',')) {
|
|
|
|
|
items.push_back(item);
|
|
|
|
|
}
|
|
|
|
|
T temp;
|
|
|
|
|
setStdArrayFromString(temp, items, 0);
|
|
|
|
|
data = temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief 获取指定数组变量的字符串数据,用于JSON前端
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @return: 数据(字符串格式)
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
|
std::string getStringFromStdArray(std::array<T, N> data)
|
|
|
|
|
{
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
for (std::size_t i = 0; i < N; ++i) {
|
|
|
|
|
if (i > 0)
|
|
|
|
|
ss << ",";
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
|
|
|
|
ss << data[i];
|
|
|
|
|
} else {
|
|
|
|
|
ss << getStringFromStdArray(data[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
/**
|
2025-05-22 08:53:29 +08:00
|
|
|
|
* @brief 通过字符串数据设置指定数组变量,用于JSON前端
|
|
|
|
|
* @param data: 数据
|
|
|
|
|
* @param value: 字符串数据,格式为"数字,数字,..."
|
|
|
|
|
* @param start_pos: 当前数组在输入字符串中的起始位置
|
|
|
|
|
* @return 处理完当前数组后,下一个数组的起始位置
|
|
|
|
|
* @throw std::runtime_error: 当输入数据格式不正确时抛出异常
|
2025-05-21 12:17:50 +08:00
|
|
|
|
*/
|
2025-05-22 08:53:29 +08:00
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
|
size_t setStdArrayFromString(std::array<T, N> &data, const std::vector<std::string> &value,
|
|
|
|
|
size_t start_pos = 0)
|
|
|
|
|
{
|
|
|
|
|
// 递归处理每个元素
|
|
|
|
|
for (std::size_t i = 0; i < N; ++i) {
|
|
|
|
|
try {
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
|
|
|
|
// 对于基本类型,直接转换
|
|
|
|
|
if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
|
|
|
|
|
data[i] = static_cast<T>(std::stod(value[start_pos + i]));
|
|
|
|
|
} else {
|
|
|
|
|
data[i] = static_cast<T>(std::stoll(value[start_pos + i]));
|
|
|
|
|
}
|
|
|
|
|
} else if constexpr (is_std_array_v<T>) {
|
|
|
|
|
// 对于嵌套数组,递归处理
|
|
|
|
|
start_pos = setStdArrayFromString(data[i], value, start_pos + i);
|
|
|
|
|
} else {
|
|
|
|
|
static_assert(std::is_arithmetic_v<T> || is_std_array_v<T>,
|
|
|
|
|
"Type must be arithmetic or std::array");
|
|
|
|
|
}
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
throw std::runtime_error("Error parsing element " + std::to_string(i) + ": "
|
|
|
|
|
+ e.what());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return start_pos + N;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T1, typename T2>
|
|
|
|
|
void assign_value_get(eprosima::fastcdr::optional<T1> &data, T2 &model_data)
|
|
|
|
|
{
|
|
|
|
|
if (data) {
|
|
|
|
|
auto temp = data.value();
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T1>) {
|
|
|
|
|
model_data = temp;
|
|
|
|
|
} else if constexpr (is_std_array_v<T1>) {
|
|
|
|
|
size_t arraySize = array_size_v<T1>;
|
|
|
|
|
for (size_t i = 0; i < arraySize; ++i) {
|
|
|
|
|
auto temp2 = temp[i];
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T2>) {
|
|
|
|
|
model_data[i] = temp2;
|
|
|
|
|
} else if constexpr (is_std_array_v<T2>) {
|
|
|
|
|
size_t arraySize2 = array_size_v<T2>;
|
|
|
|
|
for (size_t j = 0; j < arraySize2; ++j) {
|
|
|
|
|
model_data[i][j] = temp2[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 09:42:52 +08:00
|
|
|
|
template <typename T1, typename T2>
|
|
|
|
|
void assign_value_set(eprosima::fastcdr::optional<T1> &data, T2 &model_data)
|
|
|
|
|
{
|
|
|
|
|
if constexpr (std::is_arithmetic_v<T1>) {
|
|
|
|
|
data = model_data;
|
|
|
|
|
} else if constexpr (is_std_array_v<T1>) {
|
|
|
|
|
T1 temp;
|
|
|
|
|
size_t arraySize = array_size_v<T1>;
|
|
|
|
|
for (size_t i = 0; i < arraySize; ++i) {
|
|
|
|
|
if constexpr (std::is_arithmetic_v<typename T1::value_type>) {
|
|
|
|
|
temp[i] = model_data[i];
|
|
|
|
|
} else if constexpr (is_std_array_v<typename T1::value_type>) {
|
|
|
|
|
size_t arraySize2 = array_size_v<typename T1::value_type>;
|
|
|
|
|
for (size_t j = 0; j < arraySize2; ++j) {
|
|
|
|
|
temp[i][j] = model_data[i][j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
data = temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 08:53:29 +08:00
|
|
|
|
virtual void clearOutData() {}
|
|
|
|
|
virtual void sendOutData() {}
|
2025-05-21 12:17:50 +08:00
|
|
|
|
|
2025-04-28 12:25:20 +08:00
|
|
|
|
protected:
|
2025-05-19 09:49:54 +08:00
|
|
|
|
struct ByteArrayFunc {
|
2025-05-21 12:17:50 +08:00
|
|
|
|
std::function<XNByteArray(void)> func;
|
2025-05-19 09:49:54 +08:00
|
|
|
|
size_t size;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::unordered_map<std::string, std::function<std::string()>> getDataFunction;
|
2025-05-22 08:53:29 +08:00
|
|
|
|
std::unordered_map<std::string, std::function<void(std::string)>> setDataFunction;
|
2025-05-19 09:49:54 +08:00
|
|
|
|
std::vector<ByteArrayFunc> getByteArrayFunction;
|
2025-05-21 12:17:50 +08:00
|
|
|
|
std::unordered_map<std::string, std::function<void(XNByteArray)>> setByteArrayFunction;
|
2025-05-19 09:49:54 +08:00
|
|
|
|
std::mutex mutex;
|
|
|
|
|
uint8_t header[5]{}; // 固定大小的头部
|
2025-05-21 13:56:35 +08:00
|
|
|
|
size_t headerSize = 5;
|
2025-05-22 08:53:29 +08:00
|
|
|
|
FAST_DDS_MACRO::DataWriter *dataWriter;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
2025-05-21 12:17:50 +08:00
|
|
|
|
#define MAP_DATA_FUNC(NAME) \
|
2025-05-19 09:49:54 +08:00
|
|
|
|
getDataFunction[#NAME] = [this]() { return getString(data.NAME()); }; \
|
2025-05-22 08:53:29 +08:00
|
|
|
|
setDataFunction[#NAME] = [this](std::string value) { \
|
|
|
|
|
setDataFromString(out_data.NAME(), value); \
|
|
|
|
|
}; \
|
2025-05-19 09:49:54 +08:00
|
|
|
|
getByteArrayFunction.push_back( \
|
2025-05-21 12:17:50 +08:00
|
|
|
|
{[this]() { return getByteArray(data.NAME()); }, getTypeSize<decltype(data.NAME())>()}); \
|
|
|
|
|
setByteArrayFunction[#NAME] = [this](XNByteArray byteArray) { \
|
|
|
|
|
setByteArray(data.NAME(), byteArray); \
|
|
|
|
|
}
|