|
- #pragma once
-
- #include "domain/control_logic_model.h"
- #include "domain/register_repository.h"
-
- #include <chrono>
- #include <cstdint>
- #include <map>
- #include <string>
- #include <unordered_map>
- #include <utility>
- #include <vector>
-
- // 一轮软件逻辑扫描可能返回的错误
- enum class LogicScanError
- {
- None, // 扫描成功
- InvalidLogic, // 逻辑结构或运行配置无效
- ConflictingOutput, // 多个输出以不兼容的线圈模式写入同一地址
- RegisterReadFailed, // 扫描读取寄存器失败
- RegisterWriteFailed // 扫描写入寄存器失败
- };
-
- // 扫描结果;失败时携带出错的逻辑、网络和节点 ID
- struct LogicScanResult
- {
- bool succeeded = false; // 本轮扫描是否完成
- LogicScanError error = LogicScanError::None; // 失败时的分类
- std::string message; // UTF-8 可读结果或失败原因
- std::string logicId; // 失败关联的控制逻辑 ID
- std::string rungId; // 失败关联的网络 ID
- std::string nodeId; // 失败关联的节点 ID
- };
-
- // TON 节点在最近一轮扫描中的可视化状态
- struct TonTraceValue
- {
- bool input = false; // 本轮 TON 输入状态
- bool done = false; // 当前是否已经到达预置时间
- std::int64_t elapsedMs = 0; // 已累计时间,单位为毫秒
- int presetMs = 0; // 预置时间,单位为毫秒
- };
-
- // CTU/CTD 节点在最近一轮扫描中的可视化状态
- struct CounterTraceValue
- {
- bool input = false; // 本轮计数输入状态
- bool reset = false; // 本轮复位输入状态
- bool done = false; // 当前计数值是否达到预置值
- std::int16_t value = 0; // 当前计数值
- std::int16_t preset = 0; // 计数预置值
- };
-
- // MOVE/ADD/SUB 节点的结果值和溢出标记
- struct WordTraceValue
- {
- std::int16_t value = 0; // MOVE/ADD/SUB 的结果值
- bool overflow = false; // ADD/SUB 是否发生饱和溢出
- };
-
- // 一条控制逻辑的运行轨迹快照;各映射使用节点、表达式或网络 ID 作为键
- struct LogicTraceValues
- {
- std::unordered_map<std::string, bool> nodeValues; // 节点最终逻辑值
- std::unordered_map<std::string, bool> nodePowerValues; // 节点输入电源状态
- std::unordered_map<std::string, bool> expressionValues; // 条件表达式最终值
- std::unordered_map<std::string, bool> expressionInputValues; // 表达式输入状态
- std::unordered_map<std::string, bool> expressionPowerValues; // 表达式输出电源状态
- std::unordered_map<std::string, bool> rungValues; // 网络最终逻辑值
- std::unordered_map<std::string, TonTraceValue> tonValues; // TON 节点状态
- std::unordered_map<std::string, CounterTraceValue> counterValues; // CTU/CTD 节点状态
- std::unordered_map<std::string, WordTraceValue> wordValues; // MOVE/ADD/SUB 节点状态
-
- // 清除当前逻辑或网络的全部轨迹
- void clear();
- };
-
- // 全工程轨迹;logicValues 按逻辑 ID 隔离,避免不同逻辑中的节点 ID 互相覆盖
- struct LogicTraceSnapshot : LogicTraceValues
- {
- std::unordered_map<std::string, LogicTraceValues> logicValues; // 按逻辑 ID 保存的轨迹
-
- // 清除全工程轨迹及按逻辑分组的轨迹
- void clear();
- // 返回指定逻辑的轨迹投影;不存在时返回空轨迹
- LogicTraceSnapshot forLogic(const std::string &logic_id) const;
- };
-
- // 按工程顺序执行受限梯形图的一次确定性扫描
- class SoftwareLogicExecutor
- {
- public:
- using Clock = std::chrono::steady_clock;
- using TimePoint = Clock::time_point;
-
- /**
- * @brief 检查逻辑是否满足软件扫描的运行要求
- * @param logics 待检查的控制逻辑集合
- * @return 成功或包含逻辑、网络、节点定位信息的失败结果
- *
- * 检查逻辑结构、启用逻辑的运行配置、共享输出冲突以及 T/C 资源引用
- */
- LogicScanResult validate(const std::vector<ControlLogic> &logics) const;
-
- /**
- * @brief 清除所有跨扫描运行状态
- *
- * 重置边沿触发、TON 定时器和 CTU/CTD 计数器;不修改寄存器仓库和轨迹对象
- */
- void resetRuntime();
-
- /**
- * @brief 使用当前 steady_clock 执行一轮完整扫描
- * @param logics 按工程顺序扫描的控制逻辑集合
- * @param repository 扫描读取和写入的寄存器仓库
- * @param trace 可选的轨迹输出;非空时会先清空再写入本轮结果
- * @return 扫描结果,失败时不会返回部分成功状态作为成功结果
- */
- LogicScanResult executeScan(
- const std::vector<ControlLogic> &logics,
- RegisterRepository &repository,
- LogicTraceSnapshot *trace = nullptr);
- /**
- * @brief 使用指定时间执行一轮扫描
- * @param logics 按工程顺序扫描的控制逻辑集合
- * @param repository 扫描读取和写入的寄存器仓库
- * @param now 本轮扫描使用的单调时钟时间点
- * @param trace 可选的轨迹输出;适合测试 TON 等时间相关逻辑
- * @return 扫描结果
- */
- LogicScanResult executeScanAt(
- const std::vector<ControlLogic> &logics,
- RegisterRepository &repository,
- TimePoint now,
- LogicTraceSnapshot *trace = nullptr);
-
- private:
- // 单个 TON 定时器跨扫描保存的计时状态
- struct TonRuntimeState
- {
- bool timing = false; // 是否正在累计输入有效时间
- bool done = false; // 是否已经完成预置时间
- TimePoint startedAt{}; // 本次计时开始时间
- std::chrono::milliseconds elapsed{0}; // 最近一次计算的累计时间
- };
-
- /**
- * @brief 计算单个条件节点的导通结果
- * @param logic_id 所属控制逻辑 ID,用于隔离边沿触点的跨扫描状态
- * @param node 待读取和计算的条件节点
- * @param repository 提供 M/D 值的寄存器仓库;T/C 条件读取执行器内部状态
- * @param value 输出节点导通结果,不能为空
- * @return 成功结果,或包含节点 ID 的逻辑/寄存器读取失败结果
- */
- LogicScanResult evaluateCondition(
- const std::string &logic_id,
- const LogicNode &node,
- RegisterRepository &repository,
- bool *value);
- /**
- * @brief 递归计算串联、并联或叶子条件表达式
- * @param logic_id 所属控制逻辑 ID,用于传递给叶子节点状态计算
- * @param expression 待读取的条件表达式树
- * @param repository 提供 M/D 值的寄存器仓库
- * @param trace 可选的轨迹输出,用于记录表达式值、电源输入和电源输出
- * @param input_power 进入当前表达式的电源状态
- * @param value 输出表达式最终值,不能为空
- * @return 成功结果,或包含失败节点 ID 的扫描错误
- */
- LogicScanResult evaluateExpression(
- const std::string &logic_id,
- const ConditionExpression &expression,
- RegisterRepository &repository,
- LogicTraceValues *trace,
- bool input_power,
- bool *value);
- /**
- * @brief 在网络结果驱动下执行单个输出节点
- * @param logic_id 所属控制逻辑 ID,用于隔离计数器等跨扫描状态
- * @param node 待读取配置并执行的输出节点
- * @param rung_value 当前网络的逻辑结果,决定输出是否动作
- * @param repository 提供输出读写所需的 M/D 寄存器仓库
- * @param now 本轮扫描的单调时钟时间点,TON 使用它计算累计时间
- * @param trace 可选的轨迹输出,用于记录 TON、计数器和字操作结果
- * @param output_value 输出节点最终逻辑值,不能为空
- * @return 成功结果,或包含节点 ID 的逻辑/寄存器读写失败结果
- */
- LogicScanResult executeOutput(
- const std::string &logic_id,
- const LogicNode &node,
- bool rung_value,
- RegisterRepository &repository,
- TimePoint now,
- LogicTraceValues *trace,
- bool *output_value);
- /**
- * @brief 解析并读取一个字操作数
- * @param operand 常量或 D 寄存器操作数配置
- * @param repository 读取寄存器操作数时使用的仓库
- * @param value 输出解析后的 int16 数值,不能为空
- * @param node_id 关联节点 ID,用于构造可定位的失败结果
- * @return 常量读取成功,或 D 寄存器读取/操作数类型校验失败
- */
- LogicScanResult readWordOperand(
- const WordOperand &operand,
- RegisterRepository &repository,
- std::int16_t *value,
- const std::string &node_id) const;
-
- // 按逻辑 ID 和节点 ID 保存上一轮边沿输入
- std::map<std::pair<std::string, std::string>, bool> previous_edge_inputs_;
- // 按逻辑 ID 和节点 ID 保存上一轮计数输入
- std::map<std::pair<std::string, std::string>, bool> previous_counter_inputs_;
- // 按定时器编号保存跨扫描状态
- std::unordered_map<int, TonRuntimeState> ton_states_;
- // 单个计数器跨扫描保存的完成状态
- struct CounterRuntimeState
- {
- bool done = false; // 是否已经达到预置值
- };
- std::unordered_map<int, CounterRuntimeState> counter_states_; // 按计数器编号保存状态
- };
|