综合平台编程器项目的远程存储
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.
 
 
 
 

757 line
24 KiB

  1. #include "plc_communication_service.h"
  2. #include "plc_communication_error_classifier.h"
  3. #include "plc_register_repository.h"
  4. #include <QModbusDataUnit>
  5. #include <QModbusDevice>
  6. #include <QModbusReply>
  7. #include <QModbusRtuSerialMaster>
  8. #include <QSerialPort>
  9. #include <QVariant>
  10. #include <algorithm>
  11. #include <utility>
  12. namespace {
  13. constexpr int kRecoveryProbeIntervalMs = 2000;
  14. std::string toUtf8(const QString &value)
  15. {
  16. const QByteArray bytes = value.toUtf8();
  17. return std::string(bytes.constData(), static_cast<std::size_t>(bytes.size()));
  18. }
  19. QModbusDataUnit::RegisterType registerType(RegisterArea area)
  20. {
  21. return area == RegisterArea::M
  22. ? QModbusDataUnit::Coils : QModbusDataUnit::HoldingRegisters;
  23. }
  24. bool isRecoverableTimeout(PlcCommunicationError error)
  25. {
  26. return error == PlcCommunicationError::PlcNotResponding
  27. || error == PlcCommunicationError::CommunicationTimeout;
  28. }
  29. bool isReadingState(PlcConnectionState state)
  30. {
  31. return state == PlcConnectionState::Connected
  32. || state == PlcConnectionState::Recovering;
  33. }
  34. bool normalizePollAddresses(
  35. const std::vector<RegisterAddress> &addresses,
  36. std::vector<RegisterAddress> *normalized,
  37. std::string *error)
  38. {
  39. *normalized = addresses;
  40. if (std::any_of(
  41. normalized->cbegin(), normalized->cend(),
  42. [](const RegisterAddress &address) { return !address.isValid(); }))
  43. {
  44. *error = "PLC 轮询地址中包含无效的 M/D 地址";
  45. return false;
  46. }
  47. std::sort(
  48. normalized->begin(), normalized->end(),
  49. [](const RegisterAddress &left, const RegisterAddress &right)
  50. {
  51. return left.area() == right.area()
  52. ? left.index() < right.index()
  53. : left.area() == RegisterArea::M;
  54. });
  55. normalized->erase(
  56. std::unique(normalized->begin(), normalized->end()), normalized->end());
  57. if (normalized->size() > ProjectLimits::kMaximumPollAddresses)
  58. {
  59. *error = "PLC 轮询的去重 M/D 地址最多为 1024 个";
  60. return false;
  61. }
  62. return true;
  63. }
  64. std::size_t pollBlockCount(const std::vector<RegisterAddress> &addresses)
  65. {
  66. if (addresses.empty())
  67. {
  68. return 2U;
  69. }
  70. std::size_t blocks = 0U;
  71. RegisterArea current_area = RegisterArea::M;
  72. int start_address = 0;
  73. int count = 0;
  74. for (const RegisterAddress &address : addresses)
  75. {
  76. if (count == 0
  77. || address.area() != current_area
  78. || address.index() > start_address + count
  79. || count >= ProjectLimits::kMaximumModbusReadCount)
  80. {
  81. ++blocks;
  82. current_area = address.area();
  83. start_address = address.index();
  84. count = 1;
  85. }
  86. else
  87. {
  88. count = address.index() - start_address + 1;
  89. }
  90. }
  91. return blocks;
  92. }
  93. } // namespace
  94. PlcCommunicationService::PlcCommunicationService(
  95. PlcRegisterRepository &repository,
  96. QObject *parent)
  97. : QObject(parent),
  98. repository_(repository),
  99. master_(std::make_unique<QModbusRtuSerialMaster>())
  100. {
  101. repository_.setWriteHandlers(
  102. [this](const RegisterAddress &address, bool value)
  103. {
  104. return sendBitWrite(address, value);
  105. },
  106. [this](const RegisterAddress &address, std::int16_t value)
  107. {
  108. return sendWordWrite(address, value);
  109. });
  110. connect(&poll_timer_, &QTimer::timeout, this, &PlcCommunicationService::pollNextBlock);
  111. recovery_timer_.setSingleShot(true);
  112. connect(
  113. &recovery_timer_,
  114. &QTimer::timeout,
  115. this,
  116. &PlcCommunicationService::probeRecovery);
  117. connect(master_.get(), &QModbusClient::stateChanged,
  118. this,
  119. [this](QModbusDevice::State device_state)
  120. {
  121. if (device_state == QModbusDevice::ConnectedState)
  122. {
  123. serial_session_opened_ = true;
  124. setState(PlcConnectionState::Connected);
  125. poll_timer_.start(configuration_.pollIntervalMs);
  126. pollNextBlock();
  127. }
  128. else if (device_state == QModbusDevice::ConnectingState)
  129. {
  130. serial_session_opened_ = false;
  131. setState(PlcConnectionState::Connecting);
  132. }
  133. else if (device_state == QModbusDevice::UnconnectedState)
  134. {
  135. if (disconnecting_)
  136. {
  137. serial_session_opened_ = false;
  138. }
  139. else if (serial_session_opened_)
  140. {
  141. handleUnexpectedDisconnect();
  142. }
  143. else if (state_ != PlcConnectionState::Faulted)
  144. {
  145. setState(PlcConnectionState::Disconnected);
  146. }
  147. }
  148. });
  149. connect(master_.get(), &QModbusClient::errorOccurred,
  150. this,
  151. [this](QModbusDevice::Error error)
  152. {
  153. if (error != QModbusDevice::NoError)
  154. {
  155. handleModbusError(error);
  156. }
  157. });
  158. }
  159. PlcCommunicationService::~PlcCommunicationService() = default;
  160. PlcCommunicationResult PlcCommunicationService::connectDevice(
  161. const PlcSerialConfiguration &configuration)
  162. {
  163. const PlcCommunicationResult validation =
  164. validatePlcSerialConfiguration(configuration);
  165. if (!validation.succeeded)
  166. {
  167. return validation;
  168. }
  169. if (state_ == PlcConnectionState::Disconnected
  170. && master_->state() != QModbusDevice::UnconnectedState)
  171. {
  172. closeSerialSession();
  173. }
  174. if (master_->state() != QModbusDevice::UnconnectedState)
  175. {
  176. return {false, "PLC 连接已经启动,请先断开当前连接"};
  177. }
  178. // 每次连接都递增代次;旧连接的异步回复即使晚到也不能污染新缓存
  179. configuration_ = configuration;
  180. ++connection_generation_;
  181. disconnecting_ = false;
  182. serial_session_opened_ = false;
  183. received_valid_response_ = false;
  184. last_error_type_ = PlcCommunicationError::None;
  185. last_error_.clear();
  186. master_->setConnectionParameter(
  187. QModbusDevice::SerialPortNameParameter,
  188. QString::fromStdString(configuration.portName));
  189. master_->setConnectionParameter(
  190. QModbusDevice::SerialBaudRateParameter, configuration.baudRate);
  191. master_->setConnectionParameter(
  192. QModbusDevice::SerialDataBitsParameter,
  193. static_cast<QSerialPort::DataBits>(configuration.dataBits));
  194. master_->setConnectionParameter(
  195. QModbusDevice::SerialParityParameter,
  196. static_cast<QSerialPort::Parity>(configuration.parity));
  197. master_->setConnectionParameter(
  198. QModbusDevice::SerialStopBitsParameter,
  199. static_cast<QSerialPort::StopBits>(configuration.stopBits));
  200. master_->setTimeout(configuration.responseTimeoutMs);
  201. master_->setNumberOfRetries(configuration.retries);
  202. // 新连接必须重新完成完整首读,不能沿用上一条连接的运行资格
  203. repository_.invalidate();
  204. updateInitialReadCompleted(false, true);
  205. rebuildPollBlocks();
  206. setState(PlcConnectionState::Connecting);
  207. if (!master_->connectDevice())
  208. {
  209. if (last_error_.empty())
  210. {
  211. const QModbusDevice::Error error = master_->error() == QModbusDevice::NoError
  212. ? QModbusDevice::ConnectionError : master_->error();
  213. handleModbusError(error);
  214. }
  215. return {false, last_error_};
  216. }
  217. return {true, {}};
  218. }
  219. void PlcCommunicationService::disconnectDevice()
  220. {
  221. closeSerialSession();
  222. repository_.invalidate();
  223. updateInitialReadCompleted(false, true);
  224. last_error_type_ = PlcCommunicationError::None;
  225. last_error_.clear();
  226. setState(PlcConnectionState::Disconnected);
  227. }
  228. PlcCommunicationResult PlcCommunicationService::setPollAddresses(
  229. const std::vector<RegisterAddress> &addresses)
  230. {
  231. std::vector<RegisterAddress> normalized;
  232. std::string error;
  233. if (!normalizePollAddresses(addresses, &normalized, &error))
  234. {
  235. return {false, error};
  236. }
  237. if (pollBlockCount(normalized) > ProjectLimits::kMaximumPollBlocks)
  238. {
  239. return {false, "PLC 轮询地址拆分后最多允许 64 个读块"};
  240. }
  241. // 当前读请求完成前暂存新集合,避免按半旧半新的地址解析回复
  242. if (pending_reply_ != nullptr)
  243. {
  244. pending_poll_addresses_ = std::move(normalized);
  245. poll_update_pending_ = true;
  246. return {true, {}};
  247. }
  248. applyPollAddresses(normalized);
  249. return {true, {}};
  250. }
  251. PlcConnectionState PlcCommunicationService::state() const
  252. {
  253. return state_;
  254. }
  255. bool PlcCommunicationService::initialReadCompleted() const
  256. {
  257. return initial_read_completed_;
  258. }
  259. PlcCommunicationError PlcCommunicationService::lastErrorType() const
  260. {
  261. return last_error_type_;
  262. }
  263. const std::string &PlcCommunicationService::lastError() const
  264. {
  265. return last_error_;
  266. }
  267. const PlcSerialConfiguration &PlcCommunicationService::configuration() const
  268. {
  269. return configuration_;
  270. }
  271. void PlcCommunicationService::setCallbacks(
  272. std::function<void()> state_changed,
  273. std::function<void(bool)> initial_read_changed,
  274. std::function<void()> cache_updated,
  275. std::function<void(const std::string &)> error_reported)
  276. {
  277. state_changed_callback_ = std::move(state_changed);
  278. initial_read_changed_callback_ = std::move(initial_read_changed);
  279. cache_updated_callback_ = std::move(cache_updated);
  280. error_reported_callback_ = std::move(error_reported);
  281. }
  282. void PlcCommunicationService::rebuildPollBlocks()
  283. {
  284. std::vector<RegisterAddress> addresses = poll_addresses_;
  285. if (addresses.empty())
  286. {
  287. addresses = {
  288. RegisterAddress{RegisterArea::M, 0},
  289. RegisterAddress{RegisterArea::D, 0}};
  290. }
  291. std::sort(
  292. addresses.begin(), addresses.end(),
  293. [](const RegisterAddress &left, const RegisterAddress &right)
  294. {
  295. if (left.area() != right.area())
  296. {
  297. return left.area() == RegisterArea::M;
  298. }
  299. return left.index() < right.index();
  300. });
  301. addresses.erase(std::unique(addresses.begin(), addresses.end()), addresses.end());
  302. // 将相邻地址合并为读块,同时遵守 Modbus 单次读取上限
  303. poll_blocks_.clear();
  304. for (const RegisterAddress &address : addresses)
  305. {
  306. if (!address.isValid())
  307. {
  308. continue;
  309. }
  310. if (poll_blocks_.empty()
  311. || poll_blocks_.back().area != address.area()
  312. || address.index() > poll_blocks_.back().startAddress
  313. + poll_blocks_.back().count
  314. || poll_blocks_.back().count
  315. >= ProjectLimits::kMaximumModbusReadCount)
  316. {
  317. poll_blocks_.push_back({address.area(), address.index(), 1});
  318. }
  319. else
  320. {
  321. poll_blocks_.back().count = address.index()
  322. - poll_blocks_.back().startAddress + 1;
  323. }
  324. }
  325. next_poll_block_ = 0;
  326. if (!initial_read_completed_)
  327. {
  328. initial_blocks_read_.assign(poll_blocks_.size(), false);
  329. }
  330. else
  331. {
  332. initial_blocks_read_.clear();
  333. }
  334. }
  335. void PlcCommunicationService::applyPollAddresses(
  336. const std::vector<RegisterAddress> &addresses)
  337. {
  338. poll_addresses_ = addresses;
  339. rebuildPollBlocks();
  340. poll_update_pending_ = false;
  341. pending_poll_addresses_.clear();
  342. if (isReadingState(state_) && pending_reply_ == nullptr)
  343. {
  344. pollNextBlock();
  345. }
  346. }
  347. void PlcCommunicationService::pollNextBlock()
  348. {
  349. if (!isReadingState(state_)
  350. || pending_reply_ != nullptr || poll_blocks_.empty())
  351. {
  352. return;
  353. }
  354. // 每次只发一个异步请求,完成回调中再推进到下一个块
  355. const std::size_t block_index = next_poll_block_;
  356. const PollBlock block = poll_blocks_.at(block_index);
  357. next_poll_block_ = (next_poll_block_ + 1U) % poll_blocks_.size();
  358. QModbusDataUnit request(
  359. registerType(block.area), block.startAddress, static_cast<quint16>(block.count));
  360. QModbusReply *reply = master_->sendReadRequest(request, configuration_.serverAddress);
  361. if (reply == nullptr)
  362. {
  363. handleModbusError(master_->error());
  364. return;
  365. }
  366. pending_reply_ = reply;
  367. const std::uint64_t generation = connection_generation_;
  368. connect(reply, &QModbusReply::finished,
  369. this,
  370. [this, reply, block, block_index, generation]
  371. {
  372. // 连接已经重建时,直接丢弃旧回复
  373. if (generation != connection_generation_)
  374. {
  375. reply->deleteLater();
  376. return;
  377. }
  378. handleReadFinished(reply, block);
  379. if (isReadingState(state_)
  380. && reply->error() == QModbusDevice::NoError
  381. && !poll_update_pending_
  382. && block_index < initial_blocks_read_.size())
  383. {
  384. initial_blocks_read_[block_index] = true;
  385. const bool completed = std::all_of(
  386. initial_blocks_read_.cbegin(),
  387. initial_blocks_read_.cend(),
  388. [](bool read) { return read; });
  389. // 所有轮询块都成功读过,才授予真机运行资格
  390. if (completed && !initial_read_completed_)
  391. {
  392. updateInitialReadCompleted(true);
  393. if (state_ == PlcConnectionState::Recovering)
  394. {
  395. setState(PlcConnectionState::Connected);
  396. }
  397. }
  398. }
  399. if (pending_reply_ == reply)
  400. {
  401. pending_reply_ = nullptr;
  402. }
  403. reply->deleteLater();
  404. if (poll_update_pending_)
  405. {
  406. const std::vector<RegisterAddress> addresses = pending_poll_addresses_;
  407. applyPollAddresses(addresses);
  408. }
  409. });
  410. }
  411. void PlcCommunicationService::probeRecovery()
  412. {
  413. if (state_ != PlcConnectionState::Faulted
  414. || !isRecoverableTimeout(last_error_type_))
  415. {
  416. return;
  417. }
  418. if (master_->state() != QModbusDevice::ConnectedState)
  419. {
  420. return;
  421. }
  422. if (pending_reply_ != nullptr)
  423. {
  424. recovery_timer_.start(kRecoveryProbeIntervalMs);
  425. return;
  426. }
  427. if (poll_blocks_.empty())
  428. {
  429. return;
  430. }
  431. const PollBlock block = poll_blocks_.front();
  432. const QModbusDataUnit request(
  433. registerType(block.area), block.startAddress, 1);
  434. QModbusReply *reply = master_->sendReadRequest(request, configuration_.serverAddress);
  435. if (reply == nullptr)
  436. {
  437. handleRecoveryProbeFailure(master_->error());
  438. return;
  439. }
  440. pending_reply_ = reply;
  441. const std::uint64_t generation = connection_generation_;
  442. connect(reply, &QModbusReply::finished,
  443. this,
  444. [this, reply, generation]
  445. {
  446. if (generation != connection_generation_)
  447. {
  448. reply->deleteLater();
  449. return;
  450. }
  451. if (pending_reply_ == reply)
  452. {
  453. pending_reply_ = nullptr;
  454. }
  455. const QModbusDevice::Error error = reply->error();
  456. reply->deleteLater();
  457. if (poll_update_pending_)
  458. {
  459. const std::vector<RegisterAddress> addresses = pending_poll_addresses_;
  460. applyPollAddresses(addresses);
  461. }
  462. if (state_ != PlcConnectionState::Faulted
  463. || !isRecoverableTimeout(last_error_type_))
  464. {
  465. return;
  466. }
  467. if (error != QModbusDevice::NoError)
  468. {
  469. handleRecoveryProbeFailure(error);
  470. return;
  471. }
  472. restoreCommunication();
  473. });
  474. }
  475. void PlcCommunicationService::handleRecoveryProbeFailure(QModbusDevice::Error error)
  476. {
  477. if (state_ != PlcConnectionState::Faulted
  478. || !isRecoverableTimeout(last_error_type_))
  479. {
  480. return;
  481. }
  482. if (error == QModbusDevice::ConnectionError)
  483. {
  484. const PlcCommunicationErrorContext context{
  485. QString::fromStdString(configuration_.portName),
  486. serial_session_opened_,
  487. received_valid_response_};
  488. setError(classifyPlcCommunicationError(error, context));
  489. return;
  490. }
  491. recovery_timer_.start(kRecoveryProbeIntervalMs);
  492. }
  493. void PlcCommunicationService::restoreCommunication()
  494. {
  495. received_valid_response_ = true;
  496. last_error_type_ = PlcCommunicationError::None;
  497. last_error_.clear();
  498. rebuildPollBlocks();
  499. setState(PlcConnectionState::Recovering);
  500. poll_timer_.start(configuration_.pollIntervalMs);
  501. pollNextBlock();
  502. }
  503. void PlcCommunicationService::handleReadFinished(QModbusReply *reply, PollBlock block)
  504. {
  505. if (!isReadingState(state_))
  506. {
  507. return;
  508. }
  509. if (reply->error() != QModbusDevice::NoError)
  510. {
  511. handleModbusError(reply->error());
  512. return;
  513. }
  514. // 只有成功回复才能更新 PLC 缓存;写请求不会直接改缓存
  515. const QModbusDataUnit result = reply->result();
  516. for (uint index = 0; index < result.valueCount(); ++index)
  517. {
  518. const int address = block.startAddress + static_cast<int>(index);
  519. if (block.area == RegisterArea::M)
  520. {
  521. repository_.updateBit(address, result.value(index) != 0U);
  522. }
  523. else
  524. {
  525. repository_.updateWord(address, static_cast<std::int16_t>(result.value(index)));
  526. }
  527. }
  528. received_valid_response_ = true;
  529. last_error_type_ = PlcCommunicationError::None;
  530. last_error_.clear();
  531. emit cacheUpdated();
  532. if (cache_updated_callback_)
  533. {
  534. cache_updated_callback_();
  535. }
  536. }
  537. RegisterWriteResult PlcCommunicationService::sendBitWrite(
  538. const RegisterAddress &address, bool value)
  539. {
  540. if (state_ != PlcConnectionState::Connected)
  541. {
  542. return {false, RegisterError::Unavailable};
  543. }
  544. if (pending_write_reply_ != nullptr)
  545. {
  546. return {false, RegisterError::WriteRejected};
  547. }
  548. QModbusDataUnit unit(QModbusDataUnit::Coils, address.index(), 1);
  549. unit.setValue(0, value ? 1U : 0U);
  550. QModbusReply *reply = master_->sendWriteRequest(unit, configuration_.serverAddress);
  551. if (reply == nullptr)
  552. {
  553. handleModbusError(master_->error());
  554. return {false, RegisterError::WriteRejected};
  555. }
  556. pending_write_reply_ = reply;
  557. connect(reply, &QModbusReply::finished,
  558. this,
  559. [this, reply, generation = connection_generation_]
  560. {
  561. if (pending_write_reply_ == reply)
  562. {
  563. pending_write_reply_ = nullptr;
  564. }
  565. if (generation != connection_generation_)
  566. {
  567. reply->deleteLater();
  568. return;
  569. }
  570. if (reply->error() != QModbusDevice::NoError)
  571. {
  572. handleModbusError(reply->error());
  573. }
  574. reply->deleteLater();
  575. });
  576. return {true, RegisterError::None};
  577. }
  578. RegisterWriteResult PlcCommunicationService::sendWordWrite(
  579. const RegisterAddress &address, std::int16_t value)
  580. {
  581. if (state_ != PlcConnectionState::Connected)
  582. {
  583. return {false, RegisterError::Unavailable};
  584. }
  585. if (pending_write_reply_ != nullptr)
  586. {
  587. return {false, RegisterError::WriteRejected};
  588. }
  589. QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, address.index(), 1);
  590. unit.setValue(0, static_cast<quint16>(value));
  591. QModbusReply *reply = master_->sendWriteRequest(unit, configuration_.serverAddress);
  592. if (reply == nullptr)
  593. {
  594. handleModbusError(master_->error());
  595. return {false, RegisterError::WriteRejected};
  596. }
  597. pending_write_reply_ = reply;
  598. connect(reply, &QModbusReply::finished,
  599. this,
  600. [this, reply, generation = connection_generation_]
  601. {
  602. if (pending_write_reply_ == reply)
  603. {
  604. pending_write_reply_ = nullptr;
  605. }
  606. if (generation != connection_generation_)
  607. {
  608. reply->deleteLater();
  609. return;
  610. }
  611. if (reply->error() != QModbusDevice::NoError)
  612. {
  613. handleModbusError(reply->error());
  614. }
  615. reply->deleteLater();
  616. });
  617. return {true, RegisterError::None};
  618. }
  619. void PlcCommunicationService::updateInitialReadCompleted(
  620. bool completed, bool force_notification)
  621. {
  622. if (initial_read_completed_ == completed && !force_notification)
  623. {
  624. return;
  625. }
  626. initial_read_completed_ = completed;
  627. emit initialReadCompletedChanged(completed);
  628. if (initial_read_changed_callback_)
  629. {
  630. initial_read_changed_callback_(completed);
  631. }
  632. }
  633. void PlcCommunicationService::handleUnexpectedDisconnect()
  634. {
  635. serial_session_opened_ = false;
  636. const QString port_name = QString::fromStdString(configuration_.portName).trimmed();
  637. setError({
  638. PlcCommunicationError::SerialConnectionLost,
  639. QStringLiteral(
  640. "PLC 串口 %1 连接已中断;请检查 USB 转串口是否被拔出或已经失效")
  641. .arg(port_name)});
  642. }
  643. void PlcCommunicationService::handleModbusError(QModbusDevice::Error error)
  644. {
  645. if (disconnecting_
  646. || (error == QModbusDevice::ReplyAbortedError
  647. && state_ == PlcConnectionState::Disconnected)
  648. || (state_ == PlcConnectionState::Disconnected
  649. && last_error_type_ == PlcCommunicationError::SerialConnectionLost)
  650. || state_ == PlcConnectionState::Faulted)
  651. {
  652. return;
  653. }
  654. const PlcCommunicationErrorContext context{
  655. QString::fromStdString(configuration_.portName),
  656. serial_session_opened_,
  657. received_valid_response_};
  658. setError(classifyPlcCommunicationError(error, context));
  659. }
  660. void PlcCommunicationService::closeSerialSession()
  661. {
  662. ++connection_generation_;
  663. disconnecting_ = true;
  664. poll_timer_.stop();
  665. recovery_timer_.stop();
  666. pending_reply_ = nullptr;
  667. pending_write_reply_ = nullptr;
  668. poll_update_pending_ = false;
  669. pending_poll_addresses_.clear();
  670. if (master_->state() != QModbusDevice::UnconnectedState)
  671. {
  672. master_->disconnectDevice();
  673. }
  674. serial_session_opened_ = false;
  675. received_valid_response_ = false;
  676. disconnecting_ = false;
  677. }
  678. void PlcCommunicationService::setState(PlcConnectionState state)
  679. {
  680. if (state_ == state)
  681. {
  682. return;
  683. }
  684. state_ = state;
  685. emit stateChanged();
  686. if (state_changed_callback_)
  687. {
  688. state_changed_callback_();
  689. }
  690. }
  691. void PlcCommunicationService::setError(const PlcCommunicationFailure &failure)
  692. {
  693. last_error_type_ = failure.type;
  694. last_error_ = toUtf8(failure.message);
  695. poll_timer_.stop();
  696. recovery_timer_.stop();
  697. // 任意通信故障都会撤销首读资格;恢复后必须重新完整读取
  698. updateInitialReadCompleted(false);
  699. const bool disconnected = failure.type == PlcCommunicationError::SerialPortOpenFailed
  700. || failure.type == PlcCommunicationError::SerialConnectionLost
  701. || failure.type == PlcCommunicationError::UsbSerialAdapterRemoved;
  702. if (disconnected)
  703. {
  704. closeSerialSession();
  705. }
  706. setState(disconnected
  707. ? PlcConnectionState::Disconnected : PlcConnectionState::Faulted);
  708. emit communicationError(failure.message);
  709. if (error_reported_callback_)
  710. {
  711. error_reported_callback_(last_error_);
  712. }
  713. if (isRecoverableTimeout(failure.type)
  714. && master_->state() == QModbusDevice::ConnectedState)
  715. {
  716. recovery_timer_.start(kRecoveryProbeIntervalMs);
  717. }
  718. }