综合平台编程器项目的远程存储
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

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