奥特QT DDS 插件库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

527 line
22KB

  1. #include "AQTSampleMachinePlug.hpp"
  2. #include <QDebug>
  3. #include <fastdds/dds/domain/DomainParticipantFactory.hpp>
  4. /**
  5. * @brief 构造函数
  6. * @param parent 父对象指针
  7. *
  8. * 初始化插件对象,包括:
  9. * - 初始化所有指针为空
  10. * - 创建DDS类型支持对象
  11. * - 初始化数据点信息
  12. */
  13. AQTSampleMachinePlug::AQTSampleMachinePlug(QObject *parent)
  14. : QObject(parent)
  15. , participant_(nullptr)
  16. , publisher_(nullptr)
  17. , subscriber_(nullptr)
  18. , commandTopic_(nullptr)
  19. , commandWriter_(nullptr)
  20. , statusTopic_(nullptr)
  21. , statusReader_(nullptr)
  22. , configTopic_(nullptr)
  23. , configWriter_(nullptr)
  24. , pointsTopic_(nullptr)
  25. , pointsWriter_(nullptr)
  26. , pointsReader_(nullptr)
  27. {
  28. // 初始化类型支持
  29. commandType_ = TypeSupport(new SampleCommandPubSubType());
  30. statusType_ = TypeSupport(new SampleMachineInfoPubSubType());
  31. configType_ = TypeSupport(new SampleConfigPubSubType());
  32. pointsType_ = TypeSupport(new SamplePointsPubSubType());
  33. }
  34. /**
  35. * @brief 析构函数
  36. *
  37. * 清理插件对象,主要是清理所有DDS相关的资源。
  38. */
  39. AQTSampleMachinePlug::~AQTSampleMachinePlug()
  40. {
  41. // 清理DDS资源
  42. cleanupDDSEntities();
  43. }
  44. /**
  45. * @brief 初始化插件
  46. * @param domainId DDS域ID
  47. * @param domainName DDS域名称
  48. * @return 初始化是否成功
  49. *
  50. * 初始化DDS通信环境,包括:
  51. * - 创建DDS参与者
  52. * - 初始化所有DDS实体
  53. */
  54. bool AQTSampleMachinePlug::init(uint32_t domainId, const QString& domainName)
  55. {
  56. // 创建DDS参与者
  57. DomainParticipantQos participantQos;
  58. participantQos.name(domainName.toStdString());
  59. participant_ = DomainParticipantFactory::get_instance()->create_participant(domainId, participantQos);
  60. if (!participant_) {
  61. qDebug() << "Failed to create participant";
  62. return false;
  63. }
  64. // 初始化其他DDS实体
  65. return initDDSEntities();
  66. }
  67. /**
  68. * @brief 获取数据点信息列表
  69. * @return 所有支持的数据点信息列表
  70. */
  71. QList<DataPointInfo> AQTSampleMachinePlug::getDataPoints()
  72. {
  73. // 在函数调用时创建并返回数据点列表
  74. return {
  75. // ===== SampleConfigData 相关数据点 (1-9) =====
  76. {1, "UnifiedDepth", "uint16"}, // 统一深度(全段面采样时使用)
  77. {2, "SampleOrder", "uint8"}, // 大车行进顺序(1:原点往外,2:往原点采)
  78. // ===== SampleCommand 相关数据点 (10-29) =====
  79. {10, "CommandMaskBits", "uint32"}, // 命令掩码,表示哪些字段被修改
  80. {11, "ControlType", "uint8"}, // 控制类型(1:就地,2:远程)
  81. {12, "StartSample", "int32"}, // 启动采样命令 (CommandState: STOP=0, START=1)
  82. {13, "StopSample", "int32"}, // 停止采样命令 (CommandState: STOP=0, START=1)
  83. {14, "Reset", "int32"}, // 系统复位命令 (CommandState: STOP=0, START=1)
  84. {15, "PositionCheck", "int32"}, // 位置确认命令 (CommandState: STOP=0, START=1)
  85. {16, "EmergencyUp", "int32"}, // 紧急提升命令 (CommandState: STOP=0, START=1)
  86. {17, "EmergencyStop", "int32"}, // 系统急停命令 (CommandState: STOP=0, START=1)
  87. {18, "ControlBelt1", "int32"}, // 启停皮带1 (CommandState: STOP=0, START=1)
  88. {19, "ControlBelt2", "int32"}, // 启停皮带2 (CommandState: STOP=0, START=1)
  89. {20, "ControlCrush1", "int32"}, // 启停破碎机1 (CommandState: STOP=0, START=1)
  90. {21, "ControlCrush2", "int32"}, // 启停破碎机2 (CommandState: STOP=0, START=1)
  91. {22, "ControlDivider", "int32"}, // 启停缩分器 (CommandState: STOP=0, START=1)
  92. {23, "ControlCoalDistributor", "int32"}, // 启停布煤器 (CommandState: STOP=0, START=1)
  93. {24, "ReductionRatio", "uint16"}, // 缩分比
  94. {25, "CommandVersion", "uint8"}, // 命令结构体版本号
  95. // ===== SampleInfo 相关数据点 (30-49) =====
  96. {30, "StatusMaskBits", "uint32"}, // 状态掩码,表示哪些字段被修改
  97. {31, "BigPoint", "uint16"}, // 大车位置
  98. {32, "SmallPoint", "uint16"}, // 小车位置
  99. {33, "DepthPoint", "uint16"}, // 深度位置
  100. {34, "HasToXY", "uint8"}, // 到达坐标点(0:未到达,1:已到达)
  101. {35, "HasSamplePoints", "uint8"}, // 已采样点数
  102. {36, "GameOver", "uint8"}, // 采样完成(0:未完成,1:完成)
  103. {37, "BackState", "uint16"}, // 采样状态返回
  104. {38, "SampleState", "int32"}, // 采样机状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  105. {39, "Belt1State", "int32"}, // 皮带1状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  106. {40, "Belt2State", "int32"}, // 皮带2状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  107. {41, "Crush1State", "int32"}, // 破碎机1状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  108. {42, "Crush2State", "int32"}, // 破碎机2状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  109. {43, "DividerState", "int32"}, // 缩分器状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  110. {44, "BigLimit", "uint8"}, // 大车限位(0:未限位,1:已限位)
  111. {45, "BigZeroLimit", "uint8"}, // 大车零点限位(0:未限位,1:已限位)
  112. {46, "SmallLimit", "uint8"}, // 小车限位(0:未限位,1:已限位)
  113. {47, "SmallZeroLimit", "uint8"}, // 小车零点限位(0:未限位,1:已限位)
  114. {48, "UpLimit", "uint8"}, // 升高限位(0:未限位,1:已限位)
  115. {49, "DownLimit", "uint8"}, // 降低限位(0:未限位,1:已限位)
  116. {50, "StatusVersion", "uint8"}, // 状态结构体版本号
  117. // ===== SamplePoints 相关数据点 (51-99) =====
  118. {51, "TotalPoints", "uint8"}, // 采样点数量
  119. // 点1
  120. {52, "Point1X", "uint16"}, // 采样点1 X坐标
  121. {53, "Point1Y", "uint16"}, // 采样点1 Y坐标
  122. {54, "Point1Z", "uint16"}, // 采样点1 Z坐标
  123. {55, "Point1Drop", "int32"}, // 采样点1 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  124. // 点2
  125. {56, "Point2X", "uint16"}, // 采样点2 X坐标
  126. {57, "Point2Y", "uint16"}, // 采样点2 Y坐标
  127. {58, "Point2Z", "uint16"}, // 采样点2 Z坐标
  128. {59, "Point2Drop", "int32"}, // 采样点2 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  129. // 点3
  130. {60, "Point3X", "uint16"}, // 采样点3 X坐标
  131. {61, "Point3Y", "uint16"}, // 采样点3 Y坐标
  132. {62, "Point3Z", "uint16"}, // 采样点3 Z坐标
  133. {63, "Point3Drop", "int32"}, // 采样点3 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  134. // 点4
  135. {64, "Point4X", "uint16"}, // 采样点4 X坐标
  136. {65, "Point4Y", "uint16"}, // 采样点4 Y坐标
  137. {66, "Point4Z", "uint16"}, // 采样点4 Z坐标
  138. {67, "Point4Drop", "int32"}, // 采样点4 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  139. // 点5
  140. {68, "Point5X", "uint16"}, // 采样点5 X坐标
  141. {69, "Point5Y", "uint16"}, // 采样点5 Y坐标
  142. {70, "Point5Z", "uint16"}, // 采样点5 Z坐标
  143. {71, "Point5Drop", "int32"}, // 采样点5 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  144. // 点6
  145. {72, "Point6X", "uint16"}, // 采样点6 X坐标
  146. {73, "Point6Y", "uint16"}, // 采样点6 Y坐标
  147. {74, "Point6Z", "uint16"}, // 采样点6 Z坐标
  148. {75, "Point6Drop", "int32"} // 采样点6 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  149. };
  150. }
  151. /**
  152. * @brief 发布数据到DDS网络
  153. * @param dataList 要发布的数据项列表
  154. * @return 发布是否成功
  155. *
  156. * 将数据项列表转换为相应的DDS消息并发布:
  157. * - 检查写入器是否可用
  158. * - 转换数据格式
  159. * - 发送数据
  160. */
  161. bool AQTSampleMachinePlug::publishData(const QList<DataItem>& dataList)
  162. {
  163. if (dataList.isEmpty()) {
  164. qDebug() << "Empty data list received";
  165. return false;
  166. }
  167. try {
  168. // 根据第一个数据项的ID判断主题类型
  169. int firstId = dataList.first().ID;
  170. if (firstId >= 1 && firstId <= 9) { // 配置数据
  171. if (!configWriter_) {
  172. qDebug() << "Config writer not initialized";
  173. return false;
  174. }
  175. auto config = dataItemsToConfig(dataList);
  176. return configWriter_->write(&config);
  177. }
  178. else if (firstId >= 10 && firstId <= 29) { // 命令数据
  179. if (!commandWriter_) {
  180. qDebug() << "Command writer not initialized";
  181. return false;
  182. }
  183. auto command = dataItemsToCommand(dataList);
  184. return commandWriter_->write(&command);
  185. }
  186. else if (firstId >= 51 && firstId <= 99) { // 采样点数据
  187. if (!pointsWriter_) {
  188. qDebug() << "Points writer not initialized";
  189. return false;
  190. }
  191. auto points = dataItemsToPoints(dataList);
  192. return pointsWriter_->write(&points);
  193. }
  194. else {
  195. qDebug() << "Invalid data ID range:" << firstId;
  196. return false;
  197. }
  198. }
  199. catch (const std::exception& e) {
  200. qDebug() << "Error publishing data:" << e.what();
  201. return false;
  202. }
  203. }
  204. /**
  205. * @brief 初始化DDS实体
  206. * @return 初始化是否成功
  207. *
  208. * 创建并初始化所有DDS通信所需的实体,包括:
  209. * - 注册数据类型
  210. * - 创建发布者和订阅者
  211. * - 创建主题
  212. * - 创建数据写入器和读取器
  213. * - 设置数据监听器
  214. */
  215. bool AQTSampleMachinePlug::initDDSEntities()
  216. {
  217. try {
  218. // 注册所有类型
  219. commandType_.register_type(participant_);
  220. statusType_.register_type(participant_);
  221. configType_.register_type(participant_);
  222. pointsType_.register_type(participant_);
  223. // 创建发布者和订阅者
  224. publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT);
  225. subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT);
  226. if (!publisher_ || !subscriber_) {
  227. qDebug() << "Failed to create publisher or subscriber";
  228. return false;
  229. }
  230. // 创建所有主题
  231. commandTopic_ = participant_->create_topic("SampleMachine/Command",
  232. commandType_.get_type_name(), TOPIC_QOS_DEFAULT);
  233. statusTopic_ = participant_->create_topic("SampleMachine/Status",
  234. statusType_.get_type_name(), TOPIC_QOS_DEFAULT);
  235. configTopic_ = participant_->create_topic("SampleMachine/Config",
  236. configType_.get_type_name(), TOPIC_QOS_DEFAULT);
  237. pointsTopic_ = participant_->create_topic("SampleMachine/Points",
  238. pointsType_.get_type_name(), TOPIC_QOS_DEFAULT);
  239. if (!commandTopic_ || !statusTopic_ || !configTopic_ || !pointsTopic_) {
  240. qDebug() << "Failed to create topics";
  241. return false;
  242. }
  243. // 创建写入器和读取器
  244. commandWriter_ = publisher_->create_datawriter(commandTopic_, DATAWRITER_QOS_DEFAULT);
  245. statusReader_ = subscriber_->create_datareader(statusTopic_, DATAREADER_QOS_DEFAULT);
  246. configWriter_ = publisher_->create_datawriter(configTopic_, DATAWRITER_QOS_DEFAULT);
  247. pointsWriter_ = publisher_->create_datawriter(pointsTopic_, DATAWRITER_QOS_DEFAULT);
  248. pointsReader_ = subscriber_->create_datareader(pointsTopic_, DATAREADER_QOS_DEFAULT);
  249. if (!commandWriter_ || !statusReader_ || !configWriter_ ||
  250. !pointsWriter_ || !pointsReader_) {
  251. qDebug() << "Failed to create writers or readers";
  252. return false;
  253. }
  254. // 设置状态和采样点的监听器
  255. statusReader_->set_listener(this, StatusMask::data_available());
  256. pointsReader_->set_listener(this, StatusMask::data_available());
  257. return true;
  258. }
  259. catch (const std::exception& e) {
  260. qDebug() << "Error initializing DDS entities:" << e.what();
  261. return false;
  262. }
  263. }
  264. /**
  265. * @brief 清理DDS实体
  266. *
  267. * 清理所有已创建的DDS资源,包括:
  268. * - 删除所有包含的实体(主题、读写器等)
  269. * - 删除参与者
  270. */
  271. void AQTSampleMachinePlug::cleanupDDSEntities()
  272. {
  273. // 清理所有DDS资源
  274. if (participant_) {
  275. participant_->delete_contained_entities();
  276. DomainParticipantFactory::get_instance()->delete_participant(participant_);
  277. }
  278. }
  279. /**
  280. * @brief 数据可用性回调函数
  281. * @param reader 触发回调的数据读取器
  282. *
  283. * 当订阅的主题收到新数据时被调用。根据读取器类型分别处理状态数据和采样点数据,
  284. * 将DDS数据转换为DataItem列表并通过信号和回调函数通知外部。
  285. */
  286. void AQTSampleMachinePlug::on_data_available(DataReader* reader)
  287. {
  288. if (reader == statusReader_) {
  289. SampleStatus status;
  290. SampleInfo info; // 添加正确的SampleInfo声明
  291. // 问题3: while循环中的info变量未声明
  292. while (reader->take_next_sample(&status, &info) == ReturnCode_t::RETCODE_OK) {
  293. if (info.valid_data) {
  294. auto dataItems = convertStatusToDataItems(status);
  295. emit onDataUpdated(dataItems);
  296. }
  297. }
  298. }
  299. else if (reader == pointsReader_) {
  300. // 这部分代码正确
  301. SamplePoints points;
  302. SampleInfo info;
  303. while (reader->take_next_sample(&points, &info) == ReturnCode_t::RETCODE_OK) {
  304. if (info.valid_data) {
  305. auto dataItems = convertPointsToDataItems(points);
  306. emit onDataUpdated(dataItems);
  307. }
  308. }
  309. }
  310. }
  311. /**
  312. * @brief 将采样机状态转换为数据项列表
  313. * @param infos 采样机状态数据
  314. * @return 转换后的数据项列表
  315. *
  316. * 按照预定义的数据点ID,将SampleInfo结构体中的状态信息转换为DataItem列表。
  317. * 包括设备状态、位置信息、限位信息等。
  318. */
  319. QList<DataItem> AQTSampleMachinePlug::infoToDataItems(const SampleMachineInfo& infos)
  320. {
  321. QList<DataItem> items;
  322. // 按照数据点定义的顺序转换状态数据
  323. items.append(DataItem(30, infos.maskBits()));
  324. items.append(DataItem(31, infos.bigPoint()));
  325. items.append(DataItem(32, infos.smallPoint()));
  326. items.append(DataItem(33, infos.depthPoint()));
  327. items.append(DataItem(34, infos.hasToXY()));
  328. items.append(DataItem(35, infos.hasSamplePoints()));
  329. items.append(DataItem(36, infos.gameOver()));
  330. items.append(DataItem(37, infos.backState()));
  331. items.append(DataItem(38, static_cast<int32_t>(infos.sampleState())));
  332. items.append(DataItem(39, static_cast<int32_t>(infos.belt1State())));
  333. items.append(DataItem(40, static_cast<int32_t>(infos.belt2State())));
  334. items.append(DataItem(41, static_cast<int32_t>(infos.crush1State())));
  335. items.append(DataItem(42, static_cast<int32_t>(infos.crush2State())));
  336. items.append(DataItem(43, static_cast<int32_t>(infos.dividerState())));
  337. items.append(DataItem(44, infos.bigLimit()));
  338. items.append(DataItem(45, infos.bigZeroLimit()));
  339. items.append(DataItem(46, infos.smallLimit()));
  340. items.append(DataItem(47, infos.smallZeroLimit()));
  341. items.append(DataItem(48, infos.upLimit()));
  342. items.append(DataItem(49, infos.downLimit()));
  343. items.append(DataItem(50, infos.version()));
  344. return items;
  345. }
  346. SampleCommand AQTSampleMachinePlug::dataItemsToCommand(const QList<DataItem>& items)
  347. {
  348. // 初始化命令对象
  349. SampleCommand command;
  350. command.maskBits(0); // 初始化掩码为0
  351. // 遍历输入的数据项列表
  352. for (const auto& item : items) {
  353. // 根据数据项的ID进行不同的处理
  354. switch (item.ID) {
  355. case 11: // 控制类型
  356. command.controlType(item.value.toUInt());
  357. command.maskBits(command.maskBits() | 0x00000002); // 设置对应的掩码位
  358. break;
  359. case 12: // 开始采样
  360. command.startSample(static_cast<SampleModule::CommandState>(item.value.toInt()));
  361. command.maskBits(command.maskBits() | 0x00000004);
  362. break;
  363. case 13: // 停止采样
  364. command.stopSample(static_cast<SampleModule::CommandState>(item.value.toInt()));
  365. command.maskBits(command.maskBits() | 0x00000008);
  366. break;
  367. case 14: // 复位
  368. command.reset(static_cast<SampleModule::CommandState>(item.value.toInt()));
  369. command.maskBits(command.maskBits() | 0x00000010);
  370. break;
  371. case 15: // 位置检查
  372. command.positionCheck(static_cast<SampleModule::CommandState>(item.value.toInt()));
  373. command.maskBits(command.maskBits() | 0x00000020);
  374. break;
  375. case 16: // 紧急上升
  376. command.emergencyUp(static_cast<SampleModule::CommandState>(item.value.toInt()));
  377. command.maskBits(command.maskBits() | 0x00000040);
  378. break;
  379. case 17: // 紧急停止
  380. command.emergencyStop(static_cast<SampleModule::CommandState>(item.value.toInt()));
  381. command.maskBits(command.maskBits() | 0x00000080);
  382. break;
  383. case 18: // 控制传送带1
  384. command.controlBelt1(static_cast<SampleModule::CommandState>(item.value.toInt()));
  385. command.maskBits(command.maskBits() | 0x00000100);
  386. break;
  387. case 19: // 控制传送带2
  388. command.controlBelt2(static_cast<SampleModule::CommandState>(item.value.toInt()));
  389. command.maskBits(command.maskBits() | 0x00000200);
  390. break;
  391. case 20: // 控制破碎机1
  392. command.controlCrush1(static_cast<SampleModule::CommandState>(item.value.toInt()));
  393. command.maskBits(command.maskBits() | 0x00000400);
  394. break;
  395. case 21: // 控制破碎机2
  396. command.controlCrush2(static_cast<SampleModule::CommandState>(item.value.toInt()));
  397. command.maskBits(command.maskBits() | 0x00000800);
  398. break;
  399. case 22: // 控制分流器
  400. command.controlDivider(static_cast<SampleModule::CommandState>(item.value.toInt()));
  401. command.maskBits(command.maskBits() | 0x00001000);
  402. break;
  403. case 23: // 控制分煤器
  404. command.controlCoalDistributor(static_cast<SampleModule::CommandState>(item.value.toInt()));
  405. command.maskBits(command.maskBits() | 0x00002000);
  406. break;
  407. case 24: // 减速比
  408. command.reductionRatio(item.value.toUInt());
  409. command.maskBits(command.maskBits() | 0x00004000);
  410. break;
  411. case 25: // 版本号
  412. command.version(item.value.toUInt());
  413. command.maskBits(command.maskBits() | 0x00008000);
  414. break;
  415. default: // 未知ID
  416. qWarning() << "Unknown command ID:" << item.ID;
  417. break;
  418. }
  419. }
  420. // 返回构建好的命令对象
  421. return command;
  422. }
  423. SampleConfig AQTSampleMachinePlug::dataItemsToConfig(const QList<DataItem>& items)
  424. {
  425. SampleConfig config;
  426. for (const auto& item : items) {
  427. switch (item.ID) {
  428. case 1: config.unifiedDepth(item.value.toUInt()); break;
  429. case 2: config.sampleOrder(item.value.toUInt()); break;
  430. // 添加其他配置项的转换
  431. default: break;
  432. }
  433. }
  434. return config;
  435. }
  436. SamplePoints AQTSampleMachinePlug::dataItemsToPoints(const QList<DataItem>& items)
  437. {
  438. SamplePoints points;
  439. for (const auto& item : items) {
  440. switch (item.ID) {
  441. // 从51开始添加新的数据项
  442. case 51: points.totalPoints(item.value.toUInt()); break;
  443. case 52: points.x1(item.value.toUInt()); break;
  444. case 53: points.y1(item.value.toUInt()); break;
  445. case 54: points.z1(item.value.toUInt()); break;
  446. case 55: points.o1(static_cast<SampleModule::DropChoice>(item.value.toInt())); break;
  447. case 56: points.x2(item.value.toUInt()); break;
  448. case 57: points.y2(item.value.toUInt()); break;
  449. case 58: points.z2(item.value.toUInt()); break;
  450. case 59: points.o2(static_cast<SampleModule::DropChoice>(item.value.toInt())); break;
  451. case 60: points.x3(item.value.toUInt()); break;
  452. case 61: points.y3(item.value.toUInt()); break;
  453. case 62: points.z3(item.value.toUInt()); break;
  454. case 63: points.o3(static_cast<SampleModule::DropChoice>(item.value.toInt())); break;
  455. case 64: points.x4(item.value.toUInt()); break;
  456. case 65: points.y4(item.value.toUInt()); break;
  457. case 66: points.z4(item.value.toUInt()); break;
  458. case 67: points.o4(static_cast<SampleModule::DropChoice>(item.value.toInt())); break;
  459. case 68: points.x5(item.value.toUInt()); break;
  460. case 69: points.y5(item.value.toUInt()); break;
  461. case 70: points.z5(item.value.toUInt()); break;
  462. case 71: points.o5(static_cast<SampleModule::DropChoice>(item.value.toInt())); break;
  463. case 72: points.x6(item.value.toUInt()); break;
  464. case 73: points.y6(item.value.toUInt()); break;
  465. case 74: points.z6(item.value.toUInt()); break;
  466. case 75: points.o6(static_cast<SampleModule::DropChoice>(item.value.toInt())); break;
  467. default:
  468. qWarning() << "Unknown command ID:" << item.ID;
  469. break;
  470. }
  471. }
  472. return points;
  473. }