奥特QT DDS 插件库
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

AQTSampleMachinePlug.cxx 29KB

5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
5 månader sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  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. , commandReader_(nullptr)
  20. , configTopic_(nullptr)
  21. , configReader_(nullptr)
  22. , pointsTopic_(nullptr)
  23. , pointsReader_(nullptr)
  24. , machineinfoTopic_(nullptr)
  25. , machineinfoWriter_(nullptr)
  26. , plcConnected_(false) // 初始化 PLC 连接状态
  27. {
  28. // 初始化类型支持
  29. commandType_ = TypeSupport(new SampleCommandPubSubType());
  30. configType_ = TypeSupport(new SampleConfigPubSubType());
  31. pointsType_ = TypeSupport(new SamplePointsPubSubType());
  32. machineinfoType_ = TypeSupport(new SampleMachineInfoPubSubType());
  33. }
  34. /**
  35. * @brief 析构函数
  36. *
  37. * 清理插件对象,主要是清理所有DDS相关的资源。
  38. */
  39. AQTSampleMachinePlug::~AQTSampleMachinePlug()
  40. {
  41. // PLC 资源会在 PLCController 的析构函数中自动清理
  42. // 清理DDS资源
  43. cleanupDDSEntities();
  44. }
  45. /**
  46. * @brief 初始化插件
  47. * @param domainId DDS域ID
  48. * @param domainName DDS域名称
  49. * @return 初始化是否成功
  50. *
  51. * 初始化DDS通信环境,包括:
  52. * - 创建DDS参与者
  53. * - 初始化所有DDS实体
  54. */
  55. bool AQTSampleMachinePlug::init(uint32_t domainId, const QString& domainName)
  56. {
  57. // 创建DDS参与者
  58. DomainParticipantQos participantQos;
  59. participantQos.name(domainName.toStdString());
  60. participant_ = DomainParticipantFactory::get_instance()->create_participant(domainId, participantQos);
  61. if (!participant_) {
  62. qDebug() << "Failed to create participant";
  63. return false;
  64. }
  65. // 初始化其他DDS实体
  66. if (!initDDSEntities()) {
  67. qDebug() << "Failed to initialize DDS entities";
  68. return false;
  69. }
  70. // 连接到 PLC
  71. plcConnected_ = plc_.connect("192.168.0.1", 0, 1); // 使用默认参数
  72. if (!plcConnected_) {
  73. qDebug() << "Failed to connect to PLC";
  74. return false;
  75. }
  76. return true;
  77. }
  78. /**
  79. * @brief 获取数据点信息列表
  80. * @return 所有支持的数据点信息列表
  81. */
  82. QList<DataPointInfo> AQTSampleMachinePlug::getDataPoints()
  83. {
  84. // 在函数调用时创建并返回数据点列表
  85. return {
  86. // ===== SampleConfigData 相关数据点 (1-9) =====
  87. {1, "UnifiedDepth", "uint16"}, // 统一深度(全段面采样时使用)
  88. {2, "SampleOrder", "uint8"}, // 大车行进顺序(1:原点往外,2:往原点采)
  89. // ===== SampleCommand 相关数据点 (10-29) =====
  90. {10, "CommandMaskBits", "uint32"}, // 命令掩码,表示哪些字段被修改
  91. {11, "ControlType", "uint8"}, // 控制类型(1:就地,2:远程)
  92. {12, "StartSample", "int32"}, // 启动采样命令 (CommandState: STOP=0, START=1)
  93. {13, "StopSample", "int32"}, // 停止采样命令 (CommandState: STOP=0, START=1)
  94. {14, "Reset", "int32"}, // 系统复位命令 (CommandState: STOP=0, START=1)
  95. {15, "PositionCheck", "int32"}, // 位置确认命令 (CommandState: STOP=0, START=1)
  96. {16, "EmergencyUp", "int32"}, // 紧急提升命令 (CommandState: STOP=0, START=1)
  97. {17, "EmergencyStop", "int32"}, // 系统急停命令 (CommandState: STOP=0, START=1)
  98. {18, "ControlBelt1", "int32"}, // 启停皮带1 (CommandState: STOP=0, START=1)
  99. {19, "ControlBelt2", "int32"}, // 启停皮带2 (CommandState: STOP=0, START=1)
  100. {20, "ControlCrush1", "int32"}, // 启停破碎机1 (CommandState: STOP=0, START=1)
  101. {21, "ControlCrush2", "int32"}, // 启停破碎机2 (CommandState: STOP=0, START=1)
  102. {22, "ControlDivider", "int32"}, // 启停缩分器 (CommandState: STOP=0, START=1)
  103. {23, "ControlCoalDistributor", "int32"}, // 启停布煤器 (CommandState: STOP=0, START=1)
  104. {24, "ReductionRatio", "uint16"}, // 缩分比
  105. {25, "CommandVersion", "uint8"}, // 命令结构体版本号
  106. // ===== SampleInfo 相关数据点 (30-49) =====
  107. {30, "StatusMaskBits", "uint32"}, // 状态掩码,表示哪些字段被修改
  108. {31, "BigPoint", "uint16"}, // 大车位置
  109. {32, "SmallPoint", "uint16"}, // 小车位置
  110. {33, "DepthPoint", "uint16"}, // 深度位置
  111. {34, "HasToXY", "uint8"}, // 到达坐标点(0:未到达,1:已到达)
  112. {35, "HasSamplePoints", "uint8"}, // 已采样点数
  113. {36, "GameOver", "uint8"}, // 采样完成(0:未完成,1:完成)
  114. {37, "BackState", "uint16"}, // 采样状态返回
  115. {38, "SampleState", "int32"}, // 采样机状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  116. {39, "Belt1State", "int32"}, // 皮带1状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  117. {40, "Belt2State", "int32"}, // 皮带2状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  118. {41, "Crush1State", "int32"}, // 破碎机1状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  119. {42, "Crush2State", "int32"}, // 破碎机2状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  120. {43, "DividerState", "int32"}, // 缩分器状态 (DeviceState: OFF=0, ON=1, ERROR=2)
  121. {44, "BigLimit", "uint8"}, // 大车限位(0:未限位,1:已限位)
  122. {45, "BigZeroLimit", "uint8"}, // 大车零点限位(0:未限位,1:已限位)
  123. {46, "SmallLimit", "uint8"}, // 小车限位(0:未限位,1:已限位)
  124. {47, "SmallZeroLimit", "uint8"}, // 小车零点限位(0:未限位,1:已限位)
  125. {48, "UpLimit", "uint8"}, // 升高限位(0:未限位,1:已限位)
  126. {49, "DownLimit", "uint8"}, // 降低限位(0:未限位,1:已限位)
  127. {50, "StatusVersion", "uint8"}, // 状态结构体版本号
  128. // ===== SamplePoints 相关数据点 (51-99) =====
  129. {51, "TotalPoints", "uint8"}, // 采样点数量
  130. // 点1
  131. {52, "Point1X", "uint16"}, // 采样点1 X坐标
  132. {53, "Point1Y", "uint16"}, // 采样点1 Y坐标
  133. {54, "Point1Z", "uint16"}, // 采样点1 Z坐标
  134. {55, "Point1Drop", "int32"}, // 采样点1 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  135. // 点2
  136. {56, "Point2X", "uint16"}, // 采样点2 X坐标
  137. {57, "Point2Y", "uint16"}, // 采样点2 Y坐标
  138. {58, "Point2Z", "uint16"}, // 采样点2 Z坐标
  139. {59, "Point2Drop", "int32"}, // 采样点2 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  140. // 点3
  141. {60, "Point3X", "uint16"}, // 采样点3 X坐标
  142. {61, "Point3Y", "uint16"}, // 采样点3 Y坐标
  143. {62, "Point3Z", "uint16"}, // 采样点3 Z坐标
  144. {63, "Point3Drop", "int32"}, // 采样点3 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  145. // 点4
  146. {64, "Point4X", "uint16"}, // 采样点4 X坐标
  147. {65, "Point4Y", "uint16"}, // 采样点4 Y坐标
  148. {66, "Point4Z", "uint16"}, // 采样点4 Z坐标
  149. {67, "Point4Drop", "int32"}, // 采样点4 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  150. // 点5
  151. {68, "Point5X", "uint16"}, // 采样点5 X坐标
  152. {69, "Point5Y", "uint16"}, // 采样点5 Y坐标
  153. {70, "Point5Z", "uint16"}, // 采样点5 Z坐标
  154. {71, "Point5Drop", "int32"}, // 采样点5 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  155. // 点6
  156. {72, "Point6X", "uint16"}, // 采样点6 X坐标
  157. {73, "Point6Y", "uint16"}, // 采样点6 Y坐标
  158. {74, "Point6Z", "uint16"}, // 采样点6 Z坐标
  159. {75, "Point6Drop", "int32"} // 采样点6 落料选择 (DropChoice: BYPASS=0, CRUSHER=1)
  160. };
  161. }
  162. /**
  163. * @brief 发布数据到DDS网络
  164. * @param dataList 要发布的数据项列表
  165. * @return 发布是否成功
  166. *
  167. * 将数据项列表转换为相应的DDS消息并发布:
  168. * - 检查写入器是否可用
  169. * - 转换数据格式
  170. * - 发送数据
  171. */
  172. bool AQTSampleMachinePlug::publishData(const QList<DataItem>& dataList)
  173. {
  174. if (dataList.isEmpty()) {
  175. qDebug() << "Empty data list received";
  176. return false;
  177. }
  178. try {
  179. int firstId = dataList.first().ID;
  180. if (firstId >= 30 && firstId <= 50) { // 机器信息数据
  181. if (!machineinfoWriter_) {
  182. qDebug() << "Machineinfo writer not initialized";
  183. return false;
  184. }
  185. SampleMachineInfo info = dataItemsToMachineInfo(dataList);
  186. machineinfoWriter_->write(&info);
  187. return true;
  188. }
  189. else {
  190. qDebug() << "Invalid data ID range:" << firstId;
  191. return false;
  192. }
  193. }
  194. catch (const std::exception& e) {
  195. qDebug() << "Error publishing data:" << e.what();
  196. return false;
  197. }
  198. }
  199. /**
  200. * @brief 初始化DDS实体
  201. * @return 初始化是否成功
  202. *
  203. * 创建并初始化所有DDS通信所需的实体,包括:
  204. * - 注册数据类型
  205. * - 创建发布者和订阅者
  206. * - 创建主题
  207. * - 创建数据写入器和读取器
  208. * - 设置数据监听器
  209. */
  210. bool AQTSampleMachinePlug::initDDSEntities()
  211. {
  212. try {
  213. // 注册所有类型
  214. commandType_.register_type(participant_);
  215. machineinfoType_.register_type(participant_);
  216. configType_.register_type(participant_);
  217. pointsType_.register_type(participant_);
  218. // 创建发布者和订阅者
  219. publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT);
  220. subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT);
  221. if (!publisher_ || !subscriber_) {
  222. qDebug() << "Failed to create publisher or subscriber";
  223. return false;
  224. }
  225. // 创建所有主题
  226. commandTopic_ = participant_->create_topic("SampleMachine/Command",
  227. commandType_.get_type_name(), TOPIC_QOS_DEFAULT);
  228. machineinfoTopic_ = participant_->create_topic("SampleMachine/MachineInfo",
  229. machineinfoType_.get_type_name(), TOPIC_QOS_DEFAULT);
  230. configTopic_ = participant_->create_topic("SampleMachine/Config",
  231. configType_.get_type_name(), TOPIC_QOS_DEFAULT);
  232. pointsTopic_ = participant_->create_topic("SampleMachine/Points",
  233. pointsType_.get_type_name(), TOPIC_QOS_DEFAULT);
  234. if (!commandTopic_ || !machineinfoTopic_ || !configTopic_ || !pointsTopic_) {
  235. qDebug() << "Failed to create topics";
  236. return false;
  237. }
  238. // 创建写入器和读取器
  239. commandReader_ = subscriber_->create_datareader(commandTopic_, DATAREADER_QOS_DEFAULT);
  240. configReader_ = subscriber_->create_datareader(configTopic_, DATAREADER_QOS_DEFAULT);
  241. pointsReader_ = subscriber_->create_datareader(pointsTopic_, DATAREADER_QOS_DEFAULT);
  242. machineinfoWriter_ = publisher_->create_datawriter(machineinfoTopic_, DATAWRITER_QOS_DEFAULT);
  243. if (!commandReader_ || !configReader_ || !pointsReader_ || !machineinfoWriter_) {
  244. qDebug() << "Failed to create readers and writers";
  245. return false;
  246. }
  247. // 设置监听器
  248. commandReader_->set_listener(this, StatusMask::data_available());
  249. configReader_->set_listener(this, StatusMask::data_available());
  250. pointsReader_->set_listener(this, StatusMask::data_available());
  251. return true;
  252. }
  253. catch (const std::exception& e) {
  254. qDebug() << "Error initializing DDS entities:" << e.what();
  255. return false;
  256. }
  257. }
  258. /**
  259. * @brief 清理DDS实体
  260. *
  261. * 清理所有已创建的DDS资源,包括:
  262. * - 删除所有包含的实体(主题、读写器等)
  263. * - 删除参与者
  264. */
  265. void AQTSampleMachinePlug::cleanupDDSEntities()
  266. {
  267. // 清理所有DDS资源
  268. if (participant_) {
  269. participant_->delete_contained_entities();
  270. DomainParticipantFactory::get_instance()->delete_participant(participant_);
  271. }
  272. }
  273. /**
  274. * @brief 数据可用性回调函数
  275. * @param reader 触发回调的数据读取器
  276. *
  277. * 当订阅的主题收到新数据时被调用。根据读取器类型分别处理状态数据和采样点数据,
  278. * 将DDS数据转换为DataItem列表并通过信号和回调函数通知外部。
  279. */
  280. void AQTSampleMachinePlug::on_data_available(DataReader* reader)
  281. {
  282. // if (reader == commandReader_) {
  283. // SampleCommand command;
  284. // SampleInfo info;
  285. // while (reader->take_next_sample(&command, &info) == ReturnCode_t::RETCODE_OK) {
  286. // if (info.valid_data) {
  287. // auto dataItems = commandToDataItems(command);
  288. // emit onDataUpdated(dataItems);
  289. // }
  290. // }
  291. // }
  292. // else if (reader == configReader_) {
  293. // SampleConfig config;
  294. // SampleInfo info;
  295. // while (reader->take_next_sample(&config, &info) == ReturnCode_t::RETCODE_OK) {
  296. // if (info.valid_data) {
  297. // auto dataItems = configToDataItems(config);
  298. // emit onDataUpdated(dataItems);
  299. // }
  300. // }
  301. // }
  302. // else if (reader == pointsReader_) {
  303. // SamplePoints points;
  304. // SampleInfo info;
  305. // while (reader->take_next_sample(&points, &info) == ReturnCode_t::RETCODE_OK) {
  306. // if (info.valid_data) {
  307. // auto dataItems = pointsToDataItems(points);
  308. // emit onDataUpdated(dataItems);
  309. // }
  310. // }
  311. // }
  312. }
  313. /**
  314. * @brief 将采样机状态转换为数据项列表
  315. * @param infos 采样机状态数据
  316. * @return 转换后的数据项列表
  317. *
  318. * 按照预定义的数据点ID,将SampleInfo结构体中的状态信息转换为DataItem列表。
  319. * 包括设备状态、位置信息、限位信息等。
  320. */
  321. QList<DataItem> AQTSampleMachinePlug::machineInfoToDataItems(const SampleMachineInfo& info)
  322. {
  323. QList<DataItem> items;
  324. // 按照数据点定义的顺序转换状态数据
  325. items.append(DataItem(30, info.maskBits()));
  326. items.append(DataItem(31, info.bigPoint()));
  327. items.append(DataItem(32, info.smallPoint()));
  328. items.append(DataItem(33, info.depthPoint()));
  329. items.append(DataItem(34, info.hasToXY()));
  330. items.append(DataItem(35, info.hasSamplePoints()));
  331. items.append(DataItem(36, info.gameOver()));
  332. items.append(DataItem(37, info.backState()));
  333. items.append(DataItem(38, static_cast<int32_t>(info.sampleState())));
  334. items.append(DataItem(39, static_cast<int32_t>(info.belt1State())));
  335. items.append(DataItem(40, static_cast<int32_t>(info.belt2State())));
  336. items.append(DataItem(41, static_cast<int32_t>(info.crush1State())));
  337. items.append(DataItem(42, static_cast<int32_t>(info.crush2State())));
  338. items.append(DataItem(43, static_cast<int32_t>(info.dividerState())));
  339. items.append(DataItem(44, info.bigLimit()));
  340. items.append(DataItem(45, info.bigZeroLimit()));
  341. items.append(DataItem(46, info.smallLimit()));
  342. items.append(DataItem(47, info.smallZeroLimit()));
  343. items.append(DataItem(48, info.upLimit()));
  344. items.append(DataItem(49, info.downLimit()));
  345. items.append(DataItem(50, info.version()));
  346. return items;
  347. }
  348. // Config 相关转换
  349. SampleConfig AQTSampleMachinePlug::dataItemsToConfig(const QList<DataItem>& items)
  350. {
  351. SampleConfig config;
  352. for (const auto& item : items) {
  353. switch (item.ID) {
  354. case 1: config.unifiedDepth(item.value.toUInt()); break;
  355. case 2: config.sampleOrder(item.value.toUInt()); break;
  356. default: break;
  357. }
  358. }
  359. return config;
  360. }
  361. QList<DataItem> AQTSampleMachinePlug::configToDataItems(const SampleConfig& config)
  362. {
  363. QList<DataItem> items;
  364. items.append(DataItem(1, config.unifiedDepth()));
  365. items.append(DataItem(2, config.sampleOrder()));
  366. return items;
  367. }
  368. // Command 相关转换
  369. SampleCommand AQTSampleMachinePlug::dataItemsToCommand(const QList<DataItem>& items)
  370. {
  371. SampleCommand command;
  372. command.maskBits(0); // 初始化掩码为0
  373. for (const auto& item : items) {
  374. switch (item.ID) {
  375. case 10: // 命令掩码
  376. command.maskBits(item.value.toUInt());
  377. break;
  378. case 11: // 控制类型
  379. command.controlType(item.value.toUInt());
  380. command.maskBits(command.maskBits() | 0x00000002);
  381. break;
  382. case 12: // 开始采样
  383. command.startSample(static_cast<CommandState>(item.value.toInt()));
  384. command.maskBits(command.maskBits() | 0x00000004);
  385. break;
  386. case 13: // 停止采样
  387. command.stopSample(static_cast<CommandState>(item.value.toInt()));
  388. command.maskBits(command.maskBits() | 0x00000008);
  389. break;
  390. case 14: // 复位
  391. command.reset(static_cast<CommandState>(item.value.toInt()));
  392. command.maskBits(command.maskBits() | 0x00000010);
  393. break;
  394. case 15: // 位置检查
  395. command.positionCheck(static_cast<CommandState>(item.value.toInt()));
  396. command.maskBits(command.maskBits() | 0x00000020);
  397. break;
  398. case 16: // 紧急上升
  399. command.emergencyUp(static_cast<CommandState>(item.value.toInt()));
  400. command.maskBits(command.maskBits() | 0x00000040);
  401. break;
  402. case 17: // 紧急停止
  403. command.emergencyStop(static_cast<CommandState>(item.value.toInt()));
  404. command.maskBits(command.maskBits() | 0x00000080);
  405. break;
  406. case 18: // 控制传送带1
  407. command.controlBelt1(static_cast<CommandState>(item.value.toInt()));
  408. command.maskBits(command.maskBits() | 0x00000100);
  409. break;
  410. case 19: // 控制传送带2
  411. command.controlBelt2(static_cast<CommandState>(item.value.toInt()));
  412. command.maskBits(command.maskBits() | 0x00000200);
  413. break;
  414. case 20: // 控制破碎机1
  415. command.controlCrush1(static_cast<CommandState>(item.value.toInt()));
  416. command.maskBits(command.maskBits() | 0x00000400);
  417. break;
  418. case 21: // 控制破碎机2
  419. command.controlCrush2(static_cast<CommandState>(item.value.toInt()));
  420. command.maskBits(command.maskBits() | 0x00000800);
  421. break;
  422. case 22: // 控制分流器
  423. command.controlDivider(static_cast<CommandState>(item.value.toInt()));
  424. command.maskBits(command.maskBits() | 0x00001000);
  425. break;
  426. case 23: // 控制分煤器
  427. command.controlCoalDistributor(static_cast<CommandState>(item.value.toInt()));
  428. command.maskBits(command.maskBits() | 0x00002000);
  429. break;
  430. case 24: // 减速比
  431. command.reductionRatio(item.value.toUInt());
  432. command.maskBits(command.maskBits() | 0x00004000);
  433. break;
  434. case 25: // 版本号
  435. command.version(item.value.toUInt());
  436. command.maskBits(command.maskBits() | 0x00008000);
  437. break;
  438. }
  439. }
  440. return command;
  441. }
  442. QList<DataItem> AQTSampleMachinePlug::commandToDataItems(const SampleCommand& command)
  443. {
  444. QList<DataItem> items;
  445. items.append(DataItem(10, command.maskBits()));
  446. items.append(DataItem(11, command.controlType()));
  447. items.append(DataItem(12, static_cast<int32_t>(command.startSample())));
  448. items.append(DataItem(13, static_cast<int32_t>(command.stopSample())));
  449. items.append(DataItem(14, static_cast<int32_t>(command.reset())));
  450. items.append(DataItem(15, static_cast<int32_t>(command.positionCheck())));
  451. items.append(DataItem(16, static_cast<int32_t>(command.emergencyUp())));
  452. items.append(DataItem(17, static_cast<int32_t>(command.emergencyStop())));
  453. items.append(DataItem(18, static_cast<int32_t>(command.controlBelt1())));
  454. items.append(DataItem(19, static_cast<int32_t>(command.controlBelt2())));
  455. items.append(DataItem(20, static_cast<int32_t>(command.controlCrush1())));
  456. items.append(DataItem(21, static_cast<int32_t>(command.controlCrush2())));
  457. items.append(DataItem(22, static_cast<int32_t>(command.controlDivider())));
  458. items.append(DataItem(23, static_cast<int32_t>(command.controlCoalDistributor())));
  459. items.append(DataItem(24, command.reductionRatio()));
  460. items.append(DataItem(25, command.version()));
  461. return items;
  462. }
  463. // Points 相关转换
  464. SamplePoints AQTSampleMachinePlug::dataItemsToPoints(const QList<DataItem>& items)
  465. {
  466. SamplePoints points;
  467. for (const auto& item : items) {
  468. switch (item.ID) {
  469. case 51: points.totalPoints(item.value.toUInt()); break;
  470. case 52: points.x1(item.value.toUInt()); break;
  471. case 53: points.y1(item.value.toUInt()); break;
  472. case 54: points.z1(item.value.toUInt()); break;
  473. case 55: points.o1(static_cast<DropChoice>(item.value.toInt())); break;
  474. case 56: points.x2(item.value.toUInt()); break;
  475. case 57: points.y2(item.value.toUInt()); break;
  476. case 58: points.z2(item.value.toUInt()); break;
  477. case 59: points.o2(static_cast<DropChoice>(item.value.toInt())); break;
  478. case 60: points.x3(item.value.toUInt()); break;
  479. case 61: points.y3(item.value.toUInt()); break;
  480. case 62: points.z3(item.value.toUInt()); break;
  481. case 63: points.o3(static_cast<DropChoice>(item.value.toInt())); break;
  482. case 64: points.x4(item.value.toUInt()); break;
  483. case 65: points.y4(item.value.toUInt()); break;
  484. case 66: points.z4(item.value.toUInt()); break;
  485. case 67: points.o4(static_cast<DropChoice>(item.value.toInt())); break;
  486. case 68: points.x5(item.value.toUInt()); break;
  487. case 69: points.y5(item.value.toUInt()); break;
  488. case 70: points.z5(item.value.toUInt()); break;
  489. case 71: points.o5(static_cast<DropChoice>(item.value.toInt())); break;
  490. case 72: points.x6(item.value.toUInt()); break;
  491. case 73: points.y6(item.value.toUInt()); break;
  492. case 74: points.z6(item.value.toUInt()); break;
  493. case 75: points.o6(static_cast<DropChoice>(item.value.toInt())); break;
  494. }
  495. }
  496. return points;
  497. }
  498. QList<DataItem> AQTSampleMachinePlug::pointsToDataItems(const SamplePoints& points)
  499. {
  500. QList<DataItem> items;
  501. items.append(DataItem(51, points.totalPoints()));
  502. items.append(DataItem(52, points.x1()));
  503. items.append(DataItem(53, points.y1()));
  504. items.append(DataItem(54, points.z1()));
  505. items.append(DataItem(55, static_cast<int32_t>(points.o1())));
  506. items.append(DataItem(56, points.x2()));
  507. items.append(DataItem(57, points.y2()));
  508. items.append(DataItem(58, points.z2()));
  509. items.append(DataItem(59, static_cast<int32_t>(points.o2())));
  510. items.append(DataItem(60, points.x3()));
  511. items.append(DataItem(61, points.y3()));
  512. items.append(DataItem(62, points.z3()));
  513. items.append(DataItem(63, static_cast<int32_t>(points.o3())));
  514. items.append(DataItem(64, points.x4()));
  515. items.append(DataItem(65, points.y4()));
  516. items.append(DataItem(66, points.z4()));
  517. items.append(DataItem(67, static_cast<int32_t>(points.o4())));
  518. items.append(DataItem(68, points.x5()));
  519. items.append(DataItem(69, points.y5()));
  520. items.append(DataItem(70, points.z5()));
  521. items.append(DataItem(71, static_cast<int32_t>(points.o5())));
  522. items.append(DataItem(72, points.x6()));
  523. items.append(DataItem(73, points.y6()));
  524. items.append(DataItem(74, points.z6()));
  525. items.append(DataItem(75, static_cast<int32_t>(points.o6())));
  526. return items;
  527. }
  528. // MachineInfo 相关转换
  529. SampleMachineInfo AQTSampleMachinePlug::dataItemsToMachineInfo(const QList<DataItem>& items)
  530. {
  531. SampleMachineInfo info;
  532. for (const auto& item : items) {
  533. switch (item.ID) {
  534. case 30: info.maskBits(item.value.toUInt()); break;
  535. case 31: info.bigPoint(item.value.toUInt()); break;
  536. case 32: info.smallPoint(item.value.toUInt()); break;
  537. case 33: info.depthPoint(item.value.toUInt()); break;
  538. case 34: info.hasToXY(item.value.toUInt()); break;
  539. case 35: info.hasSamplePoints(item.value.toUInt()); break;
  540. case 36: info.gameOver(item.value.toUInt()); break;
  541. case 37: info.backState(item.value.toUInt()); break;
  542. case 38: info.sampleState(static_cast<DeviceState>(item.value.toInt())); break;
  543. case 39: info.belt1State(static_cast<DeviceState>(item.value.toInt())); break;
  544. case 40: info.belt2State(static_cast<DeviceState>(item.value.toInt())); break;
  545. case 41: info.crush1State(static_cast<DeviceState>(item.value.toInt())); break;
  546. case 42: info.crush2State(static_cast<DeviceState>(item.value.toInt())); break;
  547. case 43: info.dividerState(static_cast<DeviceState>(item.value.toInt())); break;
  548. case 44: info.bigLimit(item.value.toUInt()); break;
  549. case 45: info.bigZeroLimit(item.value.toUInt()); break;
  550. case 46: info.smallLimit(item.value.toUInt()); break;
  551. case 47: info.smallZeroLimit(item.value.toUInt()); break;
  552. case 48: info.upLimit(item.value.toUInt()); break;
  553. case 49: info.downLimit(item.value.toUInt()); break;
  554. case 50: info.version(item.value.toUInt()); break;
  555. }
  556. }
  557. return info;
  558. }
  559. bool AQTSampleMachinePlug::writeToPLC(const SampleConfig& config) {
  560. if (!plcConnected_) return false;
  561. // 准备数据缓冲区
  562. uint8_t buffer[32];
  563. memset(buffer, 0, sizeof(buffer));
  564. // 填充数据
  565. *reinterpret_cast<uint16_t*>(buffer) = config.unifiedDepth();
  566. *reinterpret_cast<uint8_t*>(buffer + 2) = config.sampleOrder();
  567. // 写入 PLC
  568. return plc_.writeDB(PLCDataAreas::DB_CONFIG, 0, sizeof(buffer), buffer);
  569. }
  570. bool AQTSampleMachinePlug::readFromPLC(SampleMachineInfo& info) {
  571. if (!plcConnected_) return false;
  572. // 准备数据缓冲区
  573. uint8_t buffer[64];
  574. // 从 PLC 读取数据
  575. if (!plc_.readDB(PLCDataAreas::DB_STATUS, 0, sizeof(buffer), buffer)) {
  576. return false;
  577. }
  578. // 解析数据
  579. info.maskBits(*reinterpret_cast<uint32_t*>(buffer));
  580. info.bigPoint(*reinterpret_cast<uint16_t*>(buffer + 4));
  581. info.smallPoint(*reinterpret_cast<uint16_t*>(buffer + 6));
  582. // ... 解析其他数据
  583. return true;
  584. }
  585. bool AQTSampleMachinePlug::config()
  586. {
  587. if (!configDialog_) {
  588. configDialog_ = new ConfigDialog();
  589. connect(configDialog_, &ConfigDialog::configFinished,
  590. this, &AQTSampleMachinePlug::onDialogConfigurationChanged);
  591. }
  592. configDialog_->show();
  593. return true;
  594. }
  595. void AQTSampleMachinePlug::onDialogConfigurationChanged(const QString& config)
  596. {
  597. if (applyConfiguration(config)) {
  598. emit onConfigurationChanged(config);
  599. }
  600. }
  601. bool AQTSampleMachinePlug::applyConfiguration(const QString& configuration)
  602. {
  603. QJsonDocument doc = QJsonDocument::fromJson(configuration.toUtf8());
  604. if (!doc.isObject()) {
  605. qDebug() << "Invalid configuration format";
  606. return false;
  607. }
  608. QJsonObject config = doc.object();
  609. // 断开现有连接
  610. if (plcConnected_) {
  611. plc_.disconnect();
  612. plcConnected_ = false;
  613. }
  614. // 应用新配置
  615. plcConnected_ = plc_.connect(
  616. config["ip"].toString().toStdString().c_str(),
  617. config["rack"].toInt(),
  618. config["slot"].toInt()
  619. );
  620. if (!plcConnected_) {
  621. qDebug() << "Failed to connect to PLC with new configuration";
  622. return false;
  623. }
  624. // 更新数据块配置
  625. PLCDataAreas::DB_CONFIG = config["db_config"].toInt();
  626. PLCDataAreas::DB_COMMAND = config["db_command"].toInt();
  627. PLCDataAreas::DB_STATUS = config["db_status"].toInt();
  628. PLCDataAreas::DB_POINTS = config["db_points"].toInt();
  629. return true;
  630. }