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

755 lines
23 KiB

  1. #include "software_logic_executor.h"
  2. #include <algorithm>
  3. #include <limits>
  4. #include <map>
  5. #include <type_traits>
  6. namespace {
  7. LogicScanResult success()
  8. {
  9. return {true, LogicScanError::None, {}, {}, {}, {}};
  10. }
  11. LogicScanResult failure(
  12. LogicScanError error,
  13. const std::string &message,
  14. const std::string &logic_id = {},
  15. const std::string &rung_id = {},
  16. const std::string &node_id = {})
  17. {
  18. return {false, error, message, logic_id, rung_id, node_id};
  19. }
  20. bool compareWord(
  21. std::int16_t actual,
  22. ComparisonOperator comparison,
  23. std::int16_t expected)
  24. {
  25. switch (comparison)
  26. {
  27. case ComparisonOperator::Equal:
  28. {
  29. return actual == expected;
  30. }
  31. case ComparisonOperator::NotEqual:
  32. {
  33. return actual != expected;
  34. }
  35. case ComparisonOperator::LessThan:
  36. {
  37. return actual < expected;
  38. }
  39. case ComparisonOperator::LessThanOrEqual:
  40. {
  41. return actual <= expected;
  42. }
  43. case ComparisonOperator::GreaterThan:
  44. {
  45. return actual > expected;
  46. }
  47. case ComparisonOperator::GreaterThanOrEqual:
  48. {
  49. return actual >= expected;
  50. }
  51. default:
  52. {
  53. return false;
  54. }
  55. }
  56. }
  57. bool coilModesAreCompatible(CoilMode existing, CoilMode current)
  58. {
  59. if (existing == current)
  60. {
  61. return true;
  62. }
  63. return existing != CoilMode::Normal && current != CoilMode::Normal;
  64. }
  65. } // namespace
  66. void LogicTraceValues::clear()
  67. {
  68. nodeValues.clear();
  69. expressionValues.clear();
  70. rungValues.clear();
  71. tonValues.clear();
  72. counterValues.clear();
  73. wordValues.clear();
  74. }
  75. void LogicTraceSnapshot::clear()
  76. {
  77. LogicTraceValues::clear();
  78. logicValues.clear();
  79. }
  80. LogicTraceSnapshot LogicTraceSnapshot::forLogic(
  81. const std::string &logic_id) const
  82. {
  83. LogicTraceSnapshot projection;
  84. const auto values = logicValues.find(logic_id);
  85. if (values != logicValues.cend())
  86. {
  87. projection.nodeValues = values->second.nodeValues;
  88. projection.expressionValues = values->second.expressionValues;
  89. projection.rungValues = values->second.rungValues;
  90. projection.tonValues = values->second.tonValues;
  91. projection.counterValues = values->second.counterValues;
  92. projection.wordValues = values->second.wordValues;
  93. }
  94. return projection;
  95. }
  96. LogicScanResult SoftwareLogicExecutor::validate(
  97. const std::vector<ControlLogic> &logics) const
  98. {
  99. std::map<int, CoilMode> output_modes;
  100. for (const ControlLogic &logic : logics)
  101. {
  102. std::string validation_error;
  103. if (!logic.validateStructure(&validation_error))
  104. {
  105. return failure(
  106. LogicScanError::InvalidLogic,
  107. validation_error,
  108. logic.id);
  109. }
  110. if (!logic.enabled)
  111. {
  112. continue;
  113. }
  114. if (!logic.validateForRunning(&validation_error))
  115. {
  116. return failure(
  117. LogicScanError::InvalidLogic,
  118. validation_error,
  119. logic.id);
  120. }
  121. for (const LadderRung &rung : logic.rungs)
  122. {
  123. if (!rung.output.has_value())
  124. {
  125. continue;
  126. }
  127. const auto *output = std::get_if<CoilNodeConfig>(&rung.output->config);
  128. if (output == nullptr)
  129. {
  130. if (std::holds_alternative<TonNodeConfig>(rung.output->config)
  131. || std::holds_alternative<CounterNodeConfig>(rung.output->config)
  132. || std::holds_alternative<MoveNodeConfig>(rung.output->config)
  133. || std::holds_alternative<ArithmeticNodeConfig>(
  134. rung.output->config))
  135. {
  136. continue;
  137. }
  138. return failure(
  139. LogicScanError::InvalidLogic,
  140. "梯形图输出不是有效的输出指令",
  141. logic.id,
  142. rung.id,
  143. rung.output->id);
  144. }
  145. const auto existing = output_modes.find(output->address.index());
  146. if (existing != output_modes.end()
  147. && !coilModesAreCompatible(existing->second, output->mode))
  148. {
  149. return failure(
  150. LogicScanError::ConflictingOutput,
  151. "同一地址使用了不同线圈模式:" + output->address.toString(),
  152. logic.id,
  153. rung.id,
  154. rung.output->id);
  155. }
  156. output_modes[output->address.index()] = output->mode;
  157. }
  158. }
  159. std::string resource_error;
  160. if (!validateLogicResourceReferencesForRunning(logics, &resource_error))
  161. {
  162. return failure(LogicScanError::InvalidLogic, resource_error);
  163. }
  164. return success();
  165. }
  166. void SoftwareLogicExecutor::resetRuntime()
  167. {
  168. previous_edge_inputs_.clear();
  169. previous_counter_inputs_.clear();
  170. ton_states_.clear();
  171. counter_states_.clear();
  172. }
  173. LogicScanResult SoftwareLogicExecutor::executeScan(
  174. const std::vector<ControlLogic> &logics,
  175. RegisterRepository &repository,
  176. LogicTraceSnapshot *trace)
  177. {
  178. return executeScanAt(logics, repository, Clock::now(), trace);
  179. }
  180. LogicScanResult SoftwareLogicExecutor::executeScanAt(
  181. const std::vector<ControlLogic> &logics,
  182. RegisterRepository &repository,
  183. TimePoint now,
  184. LogicTraceSnapshot *trace)
  185. {
  186. const LogicScanResult validation = validate(logics);
  187. if (!validation.succeeded)
  188. {
  189. return validation;
  190. }
  191. if (trace != nullptr)
  192. {
  193. trace->clear();
  194. }
  195. for (const ControlLogic &logic : logics)
  196. {
  197. if (!logic.enabled)
  198. {
  199. continue;
  200. }
  201. LogicTraceValues *logic_trace = trace == nullptr
  202. ? nullptr : &trace->logicValues[logic.id];
  203. for (const LadderRung &rung : logic.rungs)
  204. {
  205. if (!rung.condition.has_value() && !rung.output.has_value())
  206. {
  207. continue;
  208. }
  209. bool rung_value = false;
  210. LogicScanResult result = evaluateExpression(
  211. logic.id, *rung.condition, repository, logic_trace, &rung_value);
  212. if (!result.succeeded)
  213. {
  214. result.logicId = logic.id;
  215. result.rungId = rung.id;
  216. return result;
  217. }
  218. if (logic_trace != nullptr)
  219. {
  220. logic_trace->rungValues[rung.id] = rung_value;
  221. logic_trace->nodeValues[rung.output->id] = rung_value;
  222. }
  223. bool output_value = false;
  224. result = executeOutput(
  225. logic.id,
  226. *rung.output,
  227. rung_value,
  228. repository,
  229. now,
  230. logic_trace,
  231. &output_value);
  232. if (!result.succeeded)
  233. {
  234. result.logicId = logic.id;
  235. result.rungId = rung.id;
  236. return result;
  237. }
  238. if (logic_trace != nullptr)
  239. {
  240. logic_trace->nodeValues[rung.output->id] = output_value;
  241. }
  242. }
  243. }
  244. if (trace != nullptr)
  245. {
  246. const auto first_enabled = std::find_if(
  247. logics.cbegin(), logics.cend(),
  248. [](const ControlLogic &logic) { return logic.enabled; });
  249. if (first_enabled != logics.cend())
  250. {
  251. const auto values = trace->logicValues.find(first_enabled->id);
  252. if (values != trace->logicValues.cend())
  253. {
  254. trace->nodeValues = values->second.nodeValues;
  255. trace->expressionValues = values->second.expressionValues;
  256. trace->rungValues = values->second.rungValues;
  257. trace->tonValues = values->second.tonValues;
  258. trace->counterValues = values->second.counterValues;
  259. trace->wordValues = values->second.wordValues;
  260. }
  261. }
  262. }
  263. return success();
  264. }
  265. LogicScanResult SoftwareLogicExecutor::evaluateExpression(
  266. const std::string &logic_id,
  267. const ConditionExpression &expression,
  268. RegisterRepository &repository,
  269. LogicTraceValues *trace,
  270. bool *value)
  271. {
  272. if (value == nullptr)
  273. {
  274. return failure(LogicScanError::InvalidLogic, "缺少表达式结果接收对象");
  275. }
  276. if (expression.kind == ConditionExpressionKind::Node)
  277. {
  278. LogicScanResult result = evaluateCondition(
  279. logic_id, *expression.node, repository, value);
  280. if (result.succeeded && trace != nullptr)
  281. {
  282. trace->nodeValues[expression.node->id] = *value;
  283. trace->expressionValues[expression.id] = *value;
  284. }
  285. return result;
  286. }
  287. bool accumulated = expression.kind == ConditionExpressionKind::Series;
  288. for (const ConditionExpression &child : expression.children)
  289. {
  290. bool child_value = false;
  291. LogicScanResult result = evaluateExpression(
  292. logic_id, child, repository, trace, &child_value);
  293. if (!result.succeeded)
  294. {
  295. return result;
  296. }
  297. accumulated = expression.kind == ConditionExpressionKind::Series
  298. ? accumulated && child_value : accumulated || child_value;
  299. }
  300. *value = accumulated;
  301. if (trace != nullptr)
  302. {
  303. trace->expressionValues[expression.id] = accumulated;
  304. }
  305. return success();
  306. }
  307. LogicScanResult SoftwareLogicExecutor::evaluateCondition(
  308. const std::string &logic_id,
  309. const LogicNode &node,
  310. RegisterRepository &repository,
  311. bool *value)
  312. {
  313. if (value == nullptr)
  314. {
  315. return failure(
  316. LogicScanError::InvalidLogic,
  317. "缺少条件结果接收对象",
  318. {},
  319. {},
  320. node.id);
  321. }
  322. return std::visit(
  323. [this, &logic_id, &repository, value, &node](
  324. const auto &config) -> LogicScanResult
  325. {
  326. using Config = std::decay_t<decltype(config)>;
  327. if constexpr (std::is_same_v<Config, ContactNodeConfig>)
  328. {
  329. const BitReadResult read = repository.readBit(config.address);
  330. if (!read.succeeded)
  331. {
  332. return failure(
  333. LogicScanError::RegisterReadFailed,
  334. "读取寄存器失败:" + config.address.toString(),
  335. {},
  336. {},
  337. node.id);
  338. }
  339. *value = config.mode == ContactMode::NormallyOpen
  340. ? read.value : !read.value;
  341. return success();
  342. }
  343. else if constexpr (std::is_same_v<Config, EdgeContactNodeConfig>)
  344. {
  345. const BitReadResult read = repository.readBit(config.address);
  346. if (!read.succeeded)
  347. {
  348. return failure(
  349. LogicScanError::RegisterReadFailed,
  350. "读取寄存器失败:" + config.address.toString(),
  351. {},
  352. {},
  353. node.id);
  354. }
  355. const auto runtime_key = std::make_pair(logic_id, node.id);
  356. const bool previous = previous_edge_inputs_[runtime_key];
  357. *value = config.mode == EdgeMode::Rising
  358. ? read.value && !previous : !read.value && previous;
  359. previous_edge_inputs_[runtime_key] = read.value;
  360. return success();
  361. }
  362. else if constexpr (std::is_same_v<Config, TimerContactNodeConfig>)
  363. {
  364. const bool done = ton_states_[config.address.index()].done;
  365. *value = config.mode == ContactMode::NormallyOpen ? done : !done;
  366. return success();
  367. }
  368. else if constexpr (std::is_same_v<Config, CounterContactNodeConfig>)
  369. {
  370. const bool done = counter_states_[config.address.index()].done;
  371. *value = config.mode == ContactMode::NormallyOpen ? done : !done;
  372. return success();
  373. }
  374. else if constexpr (std::is_same_v<Config, CompareNodeConfig>)
  375. {
  376. const WordReadResult read = repository.readWord(config.address);
  377. if (!read.succeeded)
  378. {
  379. return failure(
  380. LogicScanError::RegisterReadFailed,
  381. "读取寄存器失败:" + config.address.toString(),
  382. {},
  383. {},
  384. node.id);
  385. }
  386. *value = compareWord(read.value, config.comparison, config.value);
  387. return success();
  388. }
  389. else
  390. {
  391. return failure(
  392. LogicScanError::InvalidLogic,
  393. "条件节点不能包含输出节点",
  394. {},
  395. {},
  396. node.id);
  397. }
  398. },
  399. node.config);
  400. }
  401. LogicScanResult SoftwareLogicExecutor::readWordOperand(
  402. const WordOperand &operand,
  403. RegisterRepository &repository,
  404. std::int16_t *value,
  405. const std::string &node_id) const
  406. {
  407. if (value == nullptr)
  408. {
  409. return failure(
  410. LogicScanError::InvalidLogic,
  411. "缺少字操作数结果接收对象",
  412. {},
  413. {},
  414. node_id);
  415. }
  416. if (operand.kind == WordOperandKind::Constant)
  417. {
  418. *value = operand.constant;
  419. return success();
  420. }
  421. if (operand.kind != WordOperandKind::Register)
  422. {
  423. return failure(
  424. LogicScanError::InvalidLogic,
  425. "字操作数类型无效",
  426. {},
  427. {},
  428. node_id);
  429. }
  430. const WordReadResult read = repository.readWord(operand.address);
  431. if (!read.succeeded)
  432. {
  433. return failure(
  434. LogicScanError::RegisterReadFailed,
  435. "读取寄存器失败:" + operand.address.toString(),
  436. {},
  437. {},
  438. node_id);
  439. }
  440. *value = read.value;
  441. return success();
  442. }
  443. LogicScanResult SoftwareLogicExecutor::executeOutput(
  444. const std::string &logic_id,
  445. const LogicNode &node,
  446. bool rung_value,
  447. RegisterRepository &repository,
  448. TimePoint now,
  449. LogicTraceValues *trace,
  450. bool *output_value)
  451. {
  452. if (output_value == nullptr)
  453. {
  454. return failure(
  455. LogicScanError::InvalidLogic,
  456. "缺少输出指令结果接收对象",
  457. {},
  458. {},
  459. node.id);
  460. }
  461. if (const auto *ton = std::get_if<TonNodeConfig>(&node.config))
  462. {
  463. TonRuntimeState &state = ton_states_[ton->address.index()];
  464. if (!rung_value)
  465. {
  466. state = TonRuntimeState{};
  467. }
  468. else if (!state.timing)
  469. {
  470. state.timing = true;
  471. state.startedAt = now;
  472. state.elapsed = std::chrono::milliseconds{0};
  473. state.done = false;
  474. }
  475. else
  476. {
  477. state.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
  478. now >= state.startedAt ? now - state.startedAt : TimePoint::duration::zero());
  479. state.done = state.elapsed.count() >= ton->presetMs;
  480. }
  481. *output_value = state.done;
  482. if (trace != nullptr)
  483. {
  484. trace->tonValues[node.id] = {
  485. rung_value,
  486. state.done,
  487. state.elapsed.count(),
  488. ton->presetMs};
  489. }
  490. return success();
  491. }
  492. if (const auto *counter = std::get_if<CounterNodeConfig>(&node.config))
  493. {
  494. const BitReadResult reset_read = repository.readBit(counter->resetAddress);
  495. if (!reset_read.succeeded)
  496. {
  497. return failure(
  498. LogicScanError::RegisterReadFailed,
  499. "读取计数器复位输入失败:" + counter->resetAddress.toString(),
  500. {},
  501. {},
  502. node.id);
  503. }
  504. std::int16_t preset = 0;
  505. LogicScanResult result = readWordOperand(
  506. counter->preset, repository, &preset, node.id);
  507. if (!result.succeeded)
  508. {
  509. return result;
  510. }
  511. const std::int32_t bounded_preset = std::max<std::int32_t>(0, preset);
  512. preset = static_cast<std::int16_t>(
  513. std::min<std::int32_t>(bounded_preset, std::numeric_limits<std::int16_t>::max()));
  514. const WordReadResult current_read = repository.readWord(
  515. counter->currentValueAddress);
  516. if (!current_read.succeeded)
  517. {
  518. return failure(
  519. LogicScanError::RegisterReadFailed,
  520. "读取计数当前值失败:" + counter->currentValueAddress.toString(),
  521. {},
  522. {},
  523. node.id);
  524. }
  525. std::int32_t current = std::max<std::int32_t>(0, current_read.value);
  526. current = std::min<std::int32_t>(current, std::numeric_limits<std::int16_t>::max());
  527. const auto runtime_key = std::make_pair(logic_id, node.id);
  528. const bool previous = previous_counter_inputs_[runtime_key];
  529. const bool rising = rung_value && !previous;
  530. previous_counter_inputs_[runtime_key] = rung_value;
  531. bool done = counter_states_[counter->address.index()].done;
  532. if (reset_read.value)
  533. {
  534. current = counter->mode == CounterMode::Up ? 0 : preset;
  535. done = counter->mode == CounterMode::Down && current == 0;
  536. }
  537. else if (rising)
  538. {
  539. if (counter->mode == CounterMode::Up)
  540. {
  541. if (current < preset)
  542. {
  543. ++current;
  544. }
  545. done = current >= preset;
  546. }
  547. else
  548. {
  549. if (current > 0)
  550. {
  551. --current;
  552. }
  553. done = current <= 0;
  554. }
  555. }
  556. if (counter->mode == CounterMode::Up && current >= preset)
  557. {
  558. done = true;
  559. }
  560. if (counter->mode == CounterMode::Down && current <= 0)
  561. {
  562. done = true;
  563. }
  564. const std::int16_t current_value = static_cast<std::int16_t>(current);
  565. const RegisterWriteResult write = repository.writeWord(
  566. counter->currentValueAddress, current_value);
  567. if (!write.succeeded)
  568. {
  569. return failure(
  570. LogicScanError::RegisterWriteFailed,
  571. "写入计数当前值失败:" + counter->currentValueAddress.toString(),
  572. {},
  573. {},
  574. node.id);
  575. }
  576. counter_states_[counter->address.index()].done = done;
  577. *output_value = done;
  578. if (trace != nullptr)
  579. {
  580. trace->counterValues[node.id] = {
  581. rung_value,
  582. reset_read.value,
  583. done,
  584. current_value,
  585. preset};
  586. }
  587. return success();
  588. }
  589. if (const auto *move = std::get_if<MoveNodeConfig>(&node.config))
  590. {
  591. if (!rung_value)
  592. {
  593. *output_value = false;
  594. return success();
  595. }
  596. std::int16_t source = 0;
  597. LogicScanResult result = readWordOperand(
  598. move->source, repository, &source, node.id);
  599. if (!result.succeeded)
  600. {
  601. return result;
  602. }
  603. const RegisterWriteResult write = repository.writeWord(
  604. move->destination, source);
  605. if (!write.succeeded)
  606. {
  607. return failure(
  608. LogicScanError::RegisterWriteFailed,
  609. "MOVE 写入寄存器失败:" + move->destination.toString(),
  610. {},
  611. {},
  612. node.id);
  613. }
  614. *output_value = true;
  615. if (trace != nullptr)
  616. {
  617. trace->wordValues[node.id] = {source, false};
  618. }
  619. return success();
  620. }
  621. if (const auto *arithmetic = std::get_if<ArithmeticNodeConfig>(&node.config))
  622. {
  623. if (!rung_value)
  624. {
  625. *output_value = false;
  626. return success();
  627. }
  628. std::int16_t left = 0;
  629. std::int16_t right = 0;
  630. LogicScanResult result = readWordOperand(
  631. arithmetic->left, repository, &left, node.id);
  632. if (!result.succeeded)
  633. {
  634. return result;
  635. }
  636. result = readWordOperand(arithmetic->right, repository, &right, node.id);
  637. if (!result.succeeded)
  638. {
  639. return result;
  640. }
  641. const std::int32_t raw = arithmetic->operation == ArithmeticOperation::Add
  642. ? static_cast<std::int32_t>(left) + right
  643. : static_cast<std::int32_t>(left) - right;
  644. const bool overflow = raw < std::numeric_limits<std::int16_t>::min()
  645. || raw > std::numeric_limits<std::int16_t>::max();
  646. const std::int32_t bounded = std::max<std::int32_t>(
  647. std::numeric_limits<std::int16_t>::min(),
  648. std::min<std::int32_t>(raw, std::numeric_limits<std::int16_t>::max()));
  649. const std::int16_t result_value = static_cast<std::int16_t>(bounded);
  650. const RegisterWriteResult write = repository.writeWord(
  651. arithmetic->destination, result_value);
  652. if (!write.succeeded)
  653. {
  654. return failure(
  655. LogicScanError::RegisterWriteFailed,
  656. "算术指令写入寄存器失败:" + arithmetic->destination.toString(),
  657. {},
  658. {},
  659. node.id);
  660. }
  661. *output_value = true;
  662. if (trace != nullptr)
  663. {
  664. trace->wordValues[node.id] = {result_value, overflow};
  665. }
  666. return success();
  667. }
  668. const auto *config = std::get_if<CoilNodeConfig>(&node.config);
  669. if (config == nullptr)
  670. {
  671. return failure(
  672. LogicScanError::InvalidLogic,
  673. "梯形图输出不是有效的输出指令",
  674. {},
  675. {},
  676. node.id);
  677. }
  678. bool should_write = true;
  679. bool register_value = rung_value;
  680. switch (config->mode)
  681. {
  682. case CoilMode::Normal:
  683. {
  684. break;
  685. }
  686. case CoilMode::Set:
  687. {
  688. should_write = rung_value;
  689. register_value = true;
  690. break;
  691. }
  692. case CoilMode::Reset:
  693. {
  694. should_write = rung_value;
  695. register_value = false;
  696. break;
  697. }
  698. default:
  699. {
  700. return failure(
  701. LogicScanError::InvalidLogic,
  702. "不支持的线圈模式",
  703. {},
  704. {},
  705. node.id);
  706. }
  707. }
  708. if (!should_write)
  709. {
  710. *output_value = rung_value;
  711. return success();
  712. }
  713. const RegisterWriteResult write = repository.writeBit(
  714. config->address, register_value);
  715. if (!write.succeeded)
  716. {
  717. return failure(
  718. LogicScanError::RegisterWriteFailed,
  719. "写入寄存器失败:" + config->address.toString(),
  720. {},
  721. {},
  722. node.id);
  723. }
  724. *output_value = rung_value;
  725. return success();
  726. }