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

223 regels
9.1 KiB

  1. #pragma once
  2. #include "domain/control_logic_model.h"
  3. #include "domain/register_repository.h"
  4. #include <chrono>
  5. #include <cstdint>
  6. #include <map>
  7. #include <string>
  8. #include <unordered_map>
  9. #include <utility>
  10. #include <vector>
  11. // 一轮软件逻辑扫描可能返回的错误
  12. enum class LogicScanError
  13. {
  14. None, // 扫描成功
  15. InvalidLogic, // 逻辑结构或运行配置无效
  16. ConflictingOutput, // 多个输出以不兼容的线圈模式写入同一地址
  17. RegisterReadFailed, // 扫描读取寄存器失败
  18. RegisterWriteFailed // 扫描写入寄存器失败
  19. };
  20. // 扫描结果;失败时携带出错的逻辑、网络和节点 ID
  21. struct LogicScanResult
  22. {
  23. bool succeeded = false; // 本轮扫描是否完成
  24. LogicScanError error = LogicScanError::None; // 失败时的分类
  25. std::string message; // UTF-8 可读结果或失败原因
  26. std::string logicId; // 失败关联的控制逻辑 ID
  27. std::string rungId; // 失败关联的网络 ID
  28. std::string nodeId; // 失败关联的节点 ID
  29. };
  30. // TON 节点在最近一轮扫描中的可视化状态
  31. struct TonTraceValue
  32. {
  33. bool input = false; // 本轮 TON 输入状态
  34. bool done = false; // 当前是否已经到达预置时间
  35. std::int64_t elapsedMs = 0; // 已累计时间,单位为毫秒
  36. int presetMs = 0; // 预置时间,单位为毫秒
  37. };
  38. // CTU/CTD 节点在最近一轮扫描中的可视化状态
  39. struct CounterTraceValue
  40. {
  41. bool input = false; // 本轮计数输入状态
  42. bool reset = false; // 本轮复位输入状态
  43. bool done = false; // 当前计数值是否达到预置值
  44. std::int16_t value = 0; // 当前计数值
  45. std::int16_t preset = 0; // 计数预置值
  46. };
  47. // MOVE/ADD/SUB 节点的结果值和溢出标记
  48. struct WordTraceValue
  49. {
  50. std::int16_t value = 0; // MOVE/ADD/SUB 的结果值
  51. bool overflow = false; // ADD/SUB 是否发生饱和溢出
  52. };
  53. // 一条控制逻辑的运行轨迹快照;各映射使用节点、表达式或网络 ID 作为键
  54. struct LogicTraceValues
  55. {
  56. std::unordered_map<std::string, bool> nodeValues; // 节点最终逻辑值
  57. std::unordered_map<std::string, bool> nodePowerValues; // 节点输入电源状态
  58. std::unordered_map<std::string, bool> expressionValues; // 条件表达式最终值
  59. std::unordered_map<std::string, bool> expressionInputValues; // 表达式输入状态
  60. std::unordered_map<std::string, bool> expressionPowerValues; // 表达式输出电源状态
  61. std::unordered_map<std::string, bool> rungValues; // 网络最终逻辑值
  62. std::unordered_map<std::string, TonTraceValue> tonValues; // TON 节点状态
  63. std::unordered_map<std::string, CounterTraceValue> counterValues; // CTU/CTD 节点状态
  64. std::unordered_map<std::string, WordTraceValue> wordValues; // MOVE/ADD/SUB 节点状态
  65. // 清除当前逻辑或网络的全部轨迹
  66. void clear();
  67. };
  68. // 全工程轨迹;logicValues 按逻辑 ID 隔离,避免不同逻辑中的节点 ID 互相覆盖
  69. struct LogicTraceSnapshot : LogicTraceValues
  70. {
  71. std::unordered_map<std::string, LogicTraceValues> logicValues; // 按逻辑 ID 保存的轨迹
  72. // 清除全工程轨迹及按逻辑分组的轨迹
  73. void clear();
  74. // 返回指定逻辑的轨迹投影;不存在时返回空轨迹
  75. LogicTraceSnapshot forLogic(const std::string &logic_id) const;
  76. };
  77. // 按工程顺序执行受限梯形图的一次确定性扫描
  78. class SoftwareLogicExecutor
  79. {
  80. public:
  81. using Clock = std::chrono::steady_clock;
  82. using TimePoint = Clock::time_point;
  83. /**
  84. * @brief 检查逻辑是否满足软件扫描的运行要求
  85. * @param logics 待检查的控制逻辑集合
  86. * @return 成功或包含逻辑、网络、节点定位信息的失败结果
  87. *
  88. * 检查逻辑结构、启用逻辑的运行配置、共享输出冲突以及 T/C 资源引用
  89. */
  90. LogicScanResult validate(const std::vector<ControlLogic> &logics) const;
  91. /**
  92. * @brief 清除所有跨扫描运行状态
  93. *
  94. * 重置边沿触发、TON 定时器和 CTU/CTD 计数器;不修改寄存器仓库和轨迹对象
  95. */
  96. void resetRuntime();
  97. /**
  98. * @brief 使用当前 steady_clock 执行一轮完整扫描
  99. * @param logics 按工程顺序扫描的控制逻辑集合
  100. * @param repository 扫描读取和写入的寄存器仓库
  101. * @param trace 可选的轨迹输出;非空时会先清空再写入本轮结果
  102. * @return 扫描结果,失败时不会返回部分成功状态作为成功结果
  103. */
  104. LogicScanResult executeScan(
  105. const std::vector<ControlLogic> &logics,
  106. RegisterRepository &repository,
  107. LogicTraceSnapshot *trace = nullptr);
  108. /**
  109. * @brief 使用指定时间执行一轮扫描
  110. * @param logics 按工程顺序扫描的控制逻辑集合
  111. * @param repository 扫描读取和写入的寄存器仓库
  112. * @param now 本轮扫描使用的单调时钟时间点
  113. * @param trace 可选的轨迹输出;适合测试 TON 等时间相关逻辑
  114. * @return 扫描结果
  115. */
  116. LogicScanResult executeScanAt(
  117. const std::vector<ControlLogic> &logics,
  118. RegisterRepository &repository,
  119. TimePoint now,
  120. LogicTraceSnapshot *trace = nullptr);
  121. private:
  122. // 单个 TON 定时器跨扫描保存的计时状态
  123. struct TonRuntimeState
  124. {
  125. bool timing = false; // 是否正在累计输入有效时间
  126. bool done = false; // 是否已经完成预置时间
  127. TimePoint startedAt{}; // 本次计时开始时间
  128. std::chrono::milliseconds elapsed{0}; // 最近一次计算的累计时间
  129. };
  130. /**
  131. * @brief 计算单个条件节点的导通结果
  132. * @param logic_id 所属控制逻辑 ID,用于隔离边沿触点的跨扫描状态
  133. * @param node 待读取和计算的条件节点
  134. * @param repository 提供 M/D 值的寄存器仓库;T/C 条件读取执行器内部状态
  135. * @param value 输出节点导通结果,不能为空
  136. * @return 成功结果,或包含节点 ID 的逻辑/寄存器读取失败结果
  137. */
  138. LogicScanResult evaluateCondition(
  139. const std::string &logic_id,
  140. const LogicNode &node,
  141. RegisterRepository &repository,
  142. bool *value);
  143. /**
  144. * @brief 递归计算串联、并联或叶子条件表达式
  145. * @param logic_id 所属控制逻辑 ID,用于传递给叶子节点状态计算
  146. * @param expression 待读取的条件表达式树
  147. * @param repository 提供 M/D 值的寄存器仓库
  148. * @param trace 可选的轨迹输出,用于记录表达式值、电源输入和电源输出
  149. * @param input_power 进入当前表达式的电源状态
  150. * @param value 输出表达式最终值,不能为空
  151. * @return 成功结果,或包含失败节点 ID 的扫描错误
  152. */
  153. LogicScanResult evaluateExpression(
  154. const std::string &logic_id,
  155. const ConditionExpression &expression,
  156. RegisterRepository &repository,
  157. LogicTraceValues *trace,
  158. bool input_power,
  159. bool *value);
  160. /**
  161. * @brief 在网络结果驱动下执行单个输出节点
  162. * @param logic_id 所属控制逻辑 ID,用于隔离计数器等跨扫描状态
  163. * @param node 待读取配置并执行的输出节点
  164. * @param rung_value 当前网络的逻辑结果,决定输出是否动作
  165. * @param repository 提供输出读写所需的 M/D 寄存器仓库
  166. * @param now 本轮扫描的单调时钟时间点,TON 使用它计算累计时间
  167. * @param trace 可选的轨迹输出,用于记录 TON、计数器和字操作结果
  168. * @param output_value 输出节点最终逻辑值,不能为空
  169. * @return 成功结果,或包含节点 ID 的逻辑/寄存器读写失败结果
  170. */
  171. LogicScanResult executeOutput(
  172. const std::string &logic_id,
  173. const LogicNode &node,
  174. bool rung_value,
  175. RegisterRepository &repository,
  176. TimePoint now,
  177. LogicTraceValues *trace,
  178. bool *output_value);
  179. /**
  180. * @brief 解析并读取一个字操作数
  181. * @param operand 常量或 D 寄存器操作数配置
  182. * @param repository 读取寄存器操作数时使用的仓库
  183. * @param value 输出解析后的 int16 数值,不能为空
  184. * @param node_id 关联节点 ID,用于构造可定位的失败结果
  185. * @return 常量读取成功,或 D 寄存器读取/操作数类型校验失败
  186. */
  187. LogicScanResult readWordOperand(
  188. const WordOperand &operand,
  189. RegisterRepository &repository,
  190. std::int16_t *value,
  191. const std::string &node_id) const;
  192. // 按逻辑 ID 和节点 ID 保存上一轮边沿输入
  193. std::map<std::pair<std::string, std::string>, bool> previous_edge_inputs_;
  194. // 按逻辑 ID 和节点 ID 保存上一轮计数输入
  195. std::map<std::pair<std::string, std::string>, bool> previous_counter_inputs_;
  196. // 按定时器编号保存跨扫描状态
  197. std::unordered_map<int, TonRuntimeState> ton_states_;
  198. // 单个计数器跨扫描保存的完成状态
  199. struct CounterRuntimeState
  200. {
  201. bool done = false; // 是否已经达到预置值
  202. };
  203. std::unordered_map<int, CounterRuntimeState> counter_states_; // 按计数器编号保存状态
  204. };