diff --git a/app/src/domain/control_logic_model.cpp b/app/src/domain/control_logic_model.cpp index b678da4..8e48e46 100644 --- a/app/src/domain/control_logic_model.cpp +++ b/app/src/domain/control_logic_model.cpp @@ -3,7 +3,6 @@ #include "project_limits.h" #include -#include #include #include @@ -52,22 +51,6 @@ bool validateWordAddress( return true; } -bool validateConfig(const CounterContactNodeConfig &config, std::string *error) -{ - if (!config.address.isValid()) - { - setError(error, "计数器触点必须使用有效的 C 地址"); - return false; - } - if (config.mode != ContactMode::NormallyOpen - && config.mode != ContactMode::NormallyClosed) - { - setError(error, "计数器触点使用了不支持的模式"); - return false; - } - return true; -} - bool validateConfig(const EdgeContactNodeConfig &config, std::string *error) { if (!config.address.isValid() || config.address.area() != RegisterArea::M) @@ -120,70 +103,6 @@ bool validateConfig(const CompareNodeConfig &config, std::string *error) return true; } -bool validateConfig(const TimerContactNodeConfig &config, std::string *error) -{ - if (!config.address.isValid()) - { - setError(error, "定时器触点必须使用有效的 T 地址"); - return false; - } - if (config.mode != ContactMode::NormallyOpen - && config.mode != ContactMode::NormallyClosed) - { - setError(error, "定时器触点使用了不支持的模式"); - return false; - } - return true; -} - -bool validateConfig(const TonNodeConfig &config, std::string *error) -{ - if (!config.address.isValid()) - { - setError(error, "TON 必须使用有效的 T 地址"); - return false; - } - if (config.presetMs < TonNodeConfig::kMinimumPresetMs - || config.presetMs > TonNodeConfig::kMaximumPresetMs) - { - setError(error, "TON 预设时间必须在 1~86400000 ms 范围内"); - return false; - } - return true; -} - -bool validateConfig(const CounterNodeConfig &config, std::string *error) -{ - if (!config.address.isValid()) - { - setError(error, "计数器指令必须使用有效的 C 地址"); - return false; - } - if (config.mode != CounterMode::Up && config.mode != CounterMode::Down) - { - setError(error, "计数器指令使用了不支持的方向"); - return false; - } - if (!validateWordAddress(config.currentValueAddress, "计数当前值", error) - || !config.preset.validate(error)) - { - return false; - } - if (config.preset.kind == WordOperandKind::Constant - && config.preset.constant < 0) - { - setError(error, "计数器常量预设值必须在 0~32767 范围内"); - return false; - } - if (!config.resetAddress.isValid() - || config.resetAddress.area() != RegisterArea::M) - { - setError(error, "计数器复位输入必须使用有效的 M 区地址"); - return false; - } - return true; -} - bool validateConfig(const MoveNodeConfig &config, std::string *error) { if (!config.source.validate(error)) @@ -419,11 +338,6 @@ bool validateConditionExpression( } // namespace -TimerAddress::TimerAddress(int index) - : index_(index) -{ -} - bool WordOperand::validate(std::string *error) const { if (kind == WordOperandKind::Constant) @@ -443,61 +357,6 @@ bool WordOperand::validate(std::string *error) const return true; } -CounterAddress::CounterAddress(int index) - : index_(index) -{ -} - -int CounterAddress::index() const -{ - return index_; -} - -bool CounterAddress::isValid() const -{ - return index_ >= kMinimumIndex && index_ <= kMaximumIndex; -} - -std::string CounterAddress::toString() const -{ - return isValid() ? "C" + std::to_string(index_) : "InvalidCounterAddress"; -} - -bool CounterAddress::operator==(const CounterAddress &other) const -{ - return index_ == other.index_; -} - -bool CounterAddress::operator!=(const CounterAddress &other) const -{ - return !(*this == other); -} - -int TimerAddress::index() const -{ - return index_; -} - -bool TimerAddress::isValid() const -{ - return index_ >= kMinimumIndex && index_ <= kMaximumIndex; -} - -std::string TimerAddress::toString() const -{ - return isValid() ? "T" + std::to_string(index_) : "InvalidTimerAddress"; -} - -bool TimerAddress::operator==(const TimerAddress &other) const -{ - return index_ == other.index_; -} - -bool TimerAddress::operator!=(const TimerAddress &other) const -{ - return !(*this == other); -} - std::optional registerAddressForLogicNode( const LogicNodeConfig &config) { @@ -512,10 +371,6 @@ std::optional registerAddressForLogicNode( { return value.address; } - else if constexpr (std::is_same_v) - { - return value.currentValueAddress; - } else if constexpr (std::is_same_v || std::is_same_v) { @@ -545,15 +400,6 @@ void collectRegisterAddressesForLogicNode( { appendValidAddress(addresses, value.address); } - else if constexpr (std::is_same_v) - { - appendValidAddress(addresses, value.currentValueAddress); - appendValidAddress(addresses, value.resetAddress); - if (value.preset.kind == WordOperandKind::Register) - { - appendValidAddress(addresses, value.preset.address); - } - } else if constexpr (std::is_same_v) { appendValidAddress(addresses, value.destination); @@ -574,10 +420,6 @@ void collectRegisterAddressesForLogicNode( appendValidAddress(addresses, value.right.address); } } - else if constexpr (std::is_same_v) - { - return; - } }, config); } @@ -609,8 +451,6 @@ bool LogicNode::isCondition() const { return std::holds_alternative(config) || std::holds_alternative(config) - || std::holds_alternative(config) - || std::holds_alternative(config) || std::holds_alternative(config); } @@ -618,8 +458,6 @@ bool LogicNode::isCondition() const bool LogicNode::isOutput() const { return std::holds_alternative(config) - || std::holds_alternative(config) - || std::holds_alternative(config) || std::holds_alternative(config) || std::holds_alternative(config); } @@ -984,148 +822,3 @@ bool ControlLogic::validateForRunning(std::string *error) const } return true; } - -bool validateTimerReferencesForRunning( - const std::vector &logics, - std::string *error) -{ - std::map timer_outputs; - for (const ControlLogic &logic : logics) - { - if (!logic.enabled) - { - continue; - } - for (const LadderRung &rung : logic.rungs) - { - if (!rung.output.has_value()) - { - continue; - } - const auto *ton = std::get_if(&rung.output->config); - if (ton == nullptr) - { - continue; - } - const auto inserted = timer_outputs.emplace( - ton->address.index(), rung.output->id); - if (!inserted.second) - { - setError( - error, - "定时器 " + ton->address.toString() - + " 只能由一个已启用 TON 指令驱动"); - return false; - } - } - } - - for (const ControlLogic &logic : logics) - { - if (!logic.enabled) - { - continue; - } - for (const LadderRung &rung : logic.rungs) - { - if (!rung.condition.has_value()) - { - continue; - } - std::vector nodes; - collectConditionNodes(*rung.condition, &nodes); - for (const LogicNode *node : nodes) - { - const auto *contact = std::get_if( - &node->config); - if (contact != nullptr - && timer_outputs.count(contact->address.index()) == 0U) - { - setError( - error, - "定时器触点 " + contact->address.toString() - + " 没有对应的已启用 TON 指令"); - return false; - } - } - } - } - return true; -} - -bool validateCounterReferencesForRunning( - const std::vector &logics, - std::string *error) -{ - std::map counter_outputs; - for (const ControlLogic &logic : logics) - { - if (!logic.enabled) - { - continue; - } - for (const LadderRung &rung : logic.rungs) - { - if (!rung.output.has_value()) - { - continue; - } - const auto *counter = std::get_if( - &rung.output->config); - if (counter == nullptr) - { - continue; - } - const auto inserted = counter_outputs.emplace( - counter->address.index(), rung.output->id); - if (!inserted.second) - { - setError( - error, - "计数器 " + counter->address.toString() - + " 只能由一个已启用计数指令驱动"); - return false; - } - } - } - - for (const ControlLogic &logic : logics) - { - if (!logic.enabled) - { - continue; - } - for (const LadderRung &rung : logic.rungs) - { - if (!rung.condition.has_value()) - { - continue; - } - std::vector nodes; - collectConditionNodes(*rung.condition, &nodes); - for (const LogicNode *node : nodes) - { - const auto *contact = std::get_if( - &node->config); - if (contact != nullptr - && counter_outputs.count(contact->address.index()) == 0U) - { - setError( - error, - "计数器触点 " + contact->address.toString() - + " 没有对应的已启用计数指令"); - return false; - } - } - } - } - return true; -} - -bool validateLogicResourceReferencesForRunning( - const std::vector &logics, - std::string *error) -{ - return validateTimerReferencesForRunning(logics, error) - && validateCounterReferencesForRunning(logics, error); -} diff --git a/app/src/domain/control_logic_model.h b/app/src/domain/control_logic_model.h index bb1f9fb..a996a2b 100644 --- a/app/src/domain/control_logic_model.h +++ b/app/src/domain/control_logic_model.h @@ -49,7 +49,7 @@ enum class WordOperandKind Register // 使用 D 寄存器中的数值 }; -// MOVE、ADD/SUB、计数器预置值共用的字操作数 +// MOVE、ADD/SUB 共用的字操作数 struct WordOperand { WordOperandKind kind = WordOperandKind::Constant; // 操作数来源 @@ -94,90 +94,6 @@ struct CompareNodeConfig std::int16_t value = 0; }; -// 定时器 T 编号,内部使用,不映射 M/D 寄存器;限制编号范围,判断是否合法,可转字符串 -class TimerAddress -{ -public: - static constexpr int kMinimumIndex = 0; // 允许使用的最小 T 编号 - static constexpr int kMaximumIndex = 4000; // 允许使用的最大 T 编号 - - explicit TimerAddress(int index = 0); // 使用编号创建 T 地址 - - int index() const; // 返回地址编号 - bool isValid() const; // 判断地址编号是否在有效范围内 - std::string toString() const; // 转成 T0 这样的显示文本 - - bool operator==(const TimerAddress &other) const; // 判断两个 T 地址是否相同 - bool operator!=(const TimerAddress &other) const; // 判断两个 T 地址是否不同 - -private: - int index_ = 0; // 保存 T 地址编号 -}; - -// 读取 TON 完成状态的条件触点 -struct TimerContactNodeConfig -{ - // 要读取完成状态的定时器地址 - TimerAddress address; - // 按常开或常闭方式判断完成状态 - ContactMode mode = ContactMode::NormallyOpen; -}; - -// 离线执行器内部使用的 C 区地址,不会直接映射 PLC C 区 -class CounterAddress -{ -public: - static constexpr int kMinimumIndex = 0; // 允许使用的最小 C 编号 - static constexpr int kMaximumIndex = 4000; // 允许使用的最大 C 编号 - - explicit CounterAddress(int index = 0); // 使用编号创建 C 地址 - - int index() const; // 返回地址编号 - bool isValid() const; // 判断地址编号是否在有效范围内 - std::string toString() const; // 转成 C0 这样的显示文本 - - bool operator==(const CounterAddress &other) const; // 判断两个 C 地址是否相同 - bool operator!=(const CounterAddress &other) const; // 判断两个 C 地址是否不同 - -private: - int index_ = 0; // 保存 C 地址编号 -}; - -enum class CounterMode -{ - Up, // 加计数 - Down // 减计数 -}; - -// 读取计数器完成状态的条件触点 -struct CounterContactNodeConfig -{ - // 要读取完成状态的计数器地址 - CounterAddress address; - // 按常开或常闭方式判断完成状态 - ContactMode mode = ContactMode::NormallyOpen; -}; - -// TON 输出的定时器和预置时间,单位为毫秒 -struct TonNodeConfig -{ - static constexpr int kMinimumPresetMs = 1; - static constexpr int kMaximumPresetMs = 86400000; - - TimerAddress address; // 定时器地址 - int presetMs = 1000; // 延时时间,单位为毫秒 -}; - -// CTU/CTD 输出配置;当前值、预置值和复位信号都可引用 M/D -struct CounterNodeConfig -{ - CounterAddress address; // 计数器地址 - CounterMode mode = CounterMode::Up; // 加计数或减计数 - RegisterAddress currentValueAddress{RegisterArea::D, 0}; // 当前值保存到的 D 地址 - WordOperand preset; // 计数预置值 - RegisterAddress resetAddress{RegisterArea::M, 0}; // 复位输入使用的 M 地址 -}; - // MOVE 输出:把一个字操作数写入目标 D 地址 struct MoveNodeConfig { @@ -204,12 +120,8 @@ struct ArithmeticNodeConfig using LogicNodeConfig = std::variant< ContactNodeConfig, // 普通常开或常闭触点 EdgeContactNodeConfig, // 上升沿或下降沿触点 - TimerContactNodeConfig, // 定时器完成触点 - CounterContactNodeConfig, // 计数器完成触点 CoilNodeConfig, // 普通、置位或复位线圈 CompareNodeConfig, // D 寄存器比较节点 - TonNodeConfig, // TON 定时器输出 - CounterNodeConfig, // CTU 或 CTD 计数器输出 MoveNodeConfig, // MOVE 数据传送输出 ArithmeticNodeConfig>; // ADD 或 SUB 算术输出 @@ -333,16 +245,3 @@ struct ControlLogic // 校验运行所需资源和网络配置 bool validateForRunning(std::string *error = nullptr) const; }; - -// 检查同一 T 地址是否被多个逻辑节点以冲突方式使用 -bool validateTimerReferencesForRunning( - const std::vector &logics, - std::string *error = nullptr); -// 检查同一 C 地址是否被多个计数器配置重复占用 -bool validateCounterReferencesForRunning( - const std::vector &logics, - std::string *error = nullptr); -// 一次性执行 T/C 资源引用冲突检查 -bool validateLogicResourceReferencesForRunning( - const std::vector &logics, - std::string *error = nullptr); diff --git a/app/src/domain/project_model.cpp b/app/src/domain/project_model.cpp index b7068cc..a85f664 100644 --- a/app/src/domain/project_model.cpp +++ b/app/src/domain/project_model.cpp @@ -334,9 +334,5 @@ bool Project::validateForRunning(std::string *error) const return false; } } - if (!validateLogicResourceReferencesForRunning(controlLogics, error)) - { - return false; - } return true; } diff --git a/app/src/infrastructure/json_project_storage.cpp b/app/src/infrastructure/json_project_storage.cpp index e7bc398..037f784 100644 --- a/app/src/infrastructure/json_project_storage.cpp +++ b/app/src/infrastructure/json_project_storage.cpp @@ -270,64 +270,6 @@ bool parseAddress( return true; } -QJsonObject serializeTimerAddress(const TimerAddress &address) -{ - QJsonObject object; - object.insert(QStringLiteral("index"), address.index()); - return object; -} - -bool parseTimerAddress( - const QJsonObject &object, - const std::string &context, - TimerAddress *address, - ParseState *state) -{ - int index = 0; - if (!readInt( - object, - "index", - context, - TimerAddress::kMinimumIndex, - TimerAddress::kMaximumIndex, - &index, - state)) - { - return false; - } - *address = TimerAddress{index}; - return true; -} - -QJsonObject serializeCounterAddress(const CounterAddress &address) -{ - QJsonObject object; - object.insert(QStringLiteral("index"), address.index()); - return object; -} - -bool parseCounterAddress( - const QJsonObject &object, - const std::string &context, - CounterAddress *address, - ParseState *state) -{ - int index = 0; - if (!readInt( - object, - "index", - context, - CounterAddress::kMinimumIndex, - CounterAddress::kMaximumIndex, - &index, - state)) - { - return false; - } - *address = CounterAddress{index}; - return true; -} - QString wordOperandKindName(WordOperandKind kind) { return kind == WordOperandKind::Constant @@ -1113,24 +1055,6 @@ QJsonObject serializeNodeConfig(const EdgeContactNodeConfig &config) return object; } -QJsonObject serializeNodeConfig(const TimerContactNodeConfig &config) -{ - QJsonObject object; - object.insert(QStringLiteral("type"), QStringLiteral("timerContact")); - object.insert(QStringLiteral("timer"), serializeTimerAddress(config.address)); - object.insert(QStringLiteral("mode"), contactModeName(config.mode)); - return object; -} - -QJsonObject serializeNodeConfig(const CounterContactNodeConfig &config) -{ - QJsonObject object; - object.insert(QStringLiteral("type"), QStringLiteral("counterContact")); - object.insert(QStringLiteral("counter"), serializeCounterAddress(config.address)); - object.insert(QStringLiteral("mode"), contactModeName(config.mode)); - return object; -} - // 将线圈节点配置序列化,并写入用于反序列化分派的类型标记 QJsonObject serializeNodeConfig(const CoilNodeConfig &config) { @@ -1152,35 +1076,6 @@ QJsonObject serializeNodeConfig(const CompareNodeConfig &config) return object; } -QJsonObject serializeNodeConfig(const TonNodeConfig &config) -{ - QJsonObject object; - object.insert(QStringLiteral("type"), QStringLiteral("ton")); - object.insert(QStringLiteral("timer"), serializeTimerAddress(config.address)); - object.insert(QStringLiteral("presetMs"), config.presetMs); - return object; -} - -QString counterModeName(CounterMode mode) -{ - return mode == CounterMode::Up - ? QStringLiteral("up") : QStringLiteral("down"); -} - -QJsonObject serializeNodeConfig(const CounterNodeConfig &config) -{ - QJsonObject object; - object.insert(QStringLiteral("type"), QStringLiteral("counter")); - object.insert(QStringLiteral("counter"), serializeCounterAddress(config.address)); - object.insert(QStringLiteral("mode"), counterModeName(config.mode)); - object.insert( - QStringLiteral("currentValueAddress"), - serializeAddress(config.currentValueAddress)); - object.insert(QStringLiteral("preset"), serializeWordOperand(config.preset)); - object.insert(QStringLiteral("resetAddress"), serializeAddress(config.resetAddress)); - return object; -} - QJsonObject serializeNodeConfig(const MoveNodeConfig &config) { QJsonObject object; @@ -1238,113 +1133,6 @@ bool parseNodeConfig( return false; } - if (type == "timerContact" || type == "ton" - || type == "counterContact" || type == "counter") - { - const char *resource_field = - type == "timerContact" || type == "ton" ? "timer" : "counter"; - QJsonObject resource_object; - if (!readObject(object, resource_field, context, &resource_object, state)) - { - return false; - } - if (type == "counterContact" || type == "counter") - { - CounterAddress address; - if (!parseCounterAddress( - resource_object, context + ".counter", &address, state)) - { - return false; - } - if (type == "counterContact") - { - std::string mode_text; - ContactMode mode = ContactMode::NormallyOpen; - if (!readString(object, "mode", context, &mode_text, state) - || !parseContactMode(mode_text, &mode, state)) - { - return false; - } - *config = CounterContactNodeConfig{address, mode}; - return true; - } - std::string mode_text; - CounterMode mode = CounterMode::Up; - if (!readString(object, "mode", context, &mode_text, state)) - { - return false; - } - if (mode_text == "up") - { - mode = CounterMode::Up; - } - else if (mode_text == "down") - { - mode = CounterMode::Down; - } - else - { - return state->fail( - ProjectStorageError::InvalidField, - "不支持的计数器方向:" + mode_text); - } - QJsonObject current_object; - QJsonObject preset_object; - QJsonObject reset_object; - RegisterAddress current{RegisterArea::D, 0}; - RegisterAddress reset{RegisterArea::M, 0}; - WordOperand preset; - if (!readObject( - object, "currentValueAddress", context, ¤t_object, state) - || !parseAddress( - current_object, - context + ".currentValueAddress", - ¤t, - state) - || !readObject(object, "preset", context, &preset_object, state) - || !parseWordOperand(preset_object, context + ".preset", &preset, state) - || !readObject(object, "resetAddress", context, &reset_object, state) - || !parseAddress( - reset_object, context + ".resetAddress", &reset, state)) - { - return false; - } - *config = CounterNodeConfig{address, mode, current, preset, reset}; - return true; - } - TimerAddress address; - if (!parseTimerAddress(resource_object, context + ".timer", &address, state)) - { - return false; - } - if (type == "timerContact") - { - std::string mode_text; - ContactMode mode = ContactMode::NormallyOpen; - if (!readString(object, "mode", context, &mode_text, state) - || !parseContactMode(mode_text, &mode, state)) - { - return false; - } - *config = TimerContactNodeConfig{address, mode}; - return true; - } - int preset_ms = 0; - if (!readInt( - object, - "presetMs", - context, - TonNodeConfig::kMinimumPresetMs, - TonNodeConfig::kMaximumPresetMs, - &preset_ms, - state)) - { - return false; - } - *config = TonNodeConfig{address, preset_ms}; - return true; - } - if (type == "move") { QJsonObject source_object; @@ -1410,6 +1198,14 @@ bool parseNodeConfig( return true; } + if (type != "contact" && type != "edgeContact" + && type != "coil" && type != "compare") + { + return state->fail( + ProjectStorageError::InvalidField, + "不支持的逻辑节点类型:" + type); + } + QJsonObject address_object; if (!readObject(object, "address", context, &address_object, state)) { @@ -1481,11 +1277,7 @@ bool parseNodeConfig( address, comparison, static_cast(value)}; return true; } - - - return state->fail( - ProjectStorageError::InvalidField, - "不支持的逻辑节点类型:" + type); + return state->fail(ProjectStorageError::InvalidField, "逻辑节点类型解析失败"); } // 解析逻辑节点标识及其多态配置 diff --git a/app/src/services/logic_editor_service.cpp b/app/src/services/logic_editor_service.cpp index 11e9dfb..fbc9266 100644 --- a/app/src/services/logic_editor_service.cpp +++ b/app/src/services/logic_editor_service.cpp @@ -699,16 +699,6 @@ bool LogicEditorService::configsEqual( return left_config.address == right_config.address && left_config.mode == right_config.mode; } - else if constexpr (std::is_same_v) - { - return left_config.address == right_config.address - && left_config.mode == right_config.mode; - } - else if constexpr (std::is_same_v) - { - return left_config.address == right_config.address - && left_config.mode == right_config.mode; - } else if constexpr (std::is_same_v) { return left_config.address == right_config.address @@ -720,21 +710,6 @@ bool LogicEditorService::configsEqual( && left_config.comparison == right_config.comparison && left_config.value == right_config.value; } - else if constexpr (std::is_same_v) - { - return left_config.address == right_config.address - && left_config.presetMs == right_config.presetMs; - } - else if constexpr (std::is_same_v) - { - return left_config.address == right_config.address - && left_config.mode == right_config.mode - && left_config.currentValueAddress == right_config.currentValueAddress - && left_config.preset.kind == right_config.preset.kind - && left_config.preset.address == right_config.preset.address - && left_config.preset.constant == right_config.preset.constant - && left_config.resetAddress == right_config.resetAddress; - } else if constexpr (std::is_same_v) { return left_config.source.kind == right_config.source.kind @@ -2434,16 +2409,12 @@ bool LogicEditorService::isConditionConfig(const LogicNodeConfig &config) { return std::holds_alternative(config) || std::holds_alternative(config) - || std::holds_alternative(config) - || std::holds_alternative(config) || std::holds_alternative(config); } bool LogicEditorService::isOutputConfig(const LogicNodeConfig &config) { return std::holds_alternative(config) - || std::holds_alternative(config) - || std::holds_alternative(config) || std::holds_alternative(config) || std::holds_alternative(config); } @@ -2462,26 +2433,10 @@ std::string LogicEditorService::nodePrefix(const LogicNodeConfig &config) { return "edge"; } - else if constexpr (std::is_same_v) - { - return "timer-contact"; - } - else if constexpr (std::is_same_v) - { - return "counter-contact"; - } else if constexpr (std::is_same_v) { return "coil"; } - else if constexpr (std::is_same_v) - { - return "ton"; - } - else if constexpr (std::is_same_v) - { - return "counter"; - } else if constexpr (std::is_same_v) { return "move"; diff --git a/app/src/services/offline_simulation_service.cpp b/app/src/services/offline_simulation_service.cpp index a1b59de..d3c0371 100644 --- a/app/src/services/offline_simulation_service.cpp +++ b/app/src/services/offline_simulation_service.cpp @@ -31,7 +31,7 @@ SimulationStartResult OfflineSimulationService::start( return {false, SimulationStartError::InvalidLogic, validation}; } - // 重启会话时清空 T/C/沿触发状态和虚拟 M/D,避免继承上次运行现场 + // 重启会话时清空沿触发状态和虚拟 M/D,避免继承上次运行现场 timer_.stop(); executor_.resetRuntime(); repository_.clear(); diff --git a/app/src/services/software_logic_executor.cpp b/app/src/services/software_logic_executor.cpp index 6d1d98c..4b015ab 100644 --- a/app/src/services/software_logic_executor.cpp +++ b/app/src/services/software_logic_executor.cpp @@ -79,8 +79,6 @@ void LogicTraceValues::clear() expressionInputValues.clear(); expressionPowerValues.clear(); rungValues.clear(); - tonValues.clear(); - counterValues.clear(); wordValues.clear(); } @@ -103,8 +101,6 @@ LogicTraceSnapshot LogicTraceSnapshot::forLogic( projection.expressionInputValues = values->second.expressionInputValues; projection.expressionPowerValues = values->second.expressionPowerValues; projection.rungValues = values->second.rungValues; - projection.tonValues = values->second.tonValues; - projection.counterValues = values->second.counterValues; projection.wordValues = values->second.wordValues; } return projection; @@ -113,7 +109,7 @@ LogicTraceSnapshot LogicTraceSnapshot::forLogic( LogicScanResult SoftwareLogicExecutor::validate( const std::vector &logics) const { - // 先检查每个逻辑自身,再检查跨网络共享线圈和 T/C 资源的冲突 + // 先检查每个逻辑自身,再检查跨网络共享线圈的冲突 std::map output_modes; for (const ControlLogic &logic : logics) { @@ -145,9 +141,7 @@ LogicScanResult SoftwareLogicExecutor::validate( const auto *output = std::get_if(&rung.output->config); if (output == nullptr) { - if (std::holds_alternative(rung.output->config) - || std::holds_alternative(rung.output->config) - || std::holds_alternative(rung.output->config) + if (std::holds_alternative(rung.output->config) || std::holds_alternative( rung.output->config)) { @@ -174,35 +168,18 @@ LogicScanResult SoftwareLogicExecutor::validate( output_modes[output->address.index()] = output->mode; } } - std::string resource_error; - if (!validateLogicResourceReferencesForRunning(logics, &resource_error)) - { - return failure(LogicScanError::InvalidLogic, resource_error); - } return success(); } void SoftwareLogicExecutor::resetRuntime() { previous_edge_inputs_.clear(); - previous_counter_inputs_.clear(); - ton_states_.clear(); - counter_states_.clear(); } LogicScanResult SoftwareLogicExecutor::executeScan( const std::vector &logics, RegisterRepository &repository, LogicTraceSnapshot *trace) -{ - return executeScanAt(logics, repository, Clock::now(), trace); -} - -LogicScanResult SoftwareLogicExecutor::executeScanAt( - const std::vector &logics, - RegisterRepository &repository, - TimePoint now, - LogicTraceSnapshot *trace) { const LogicScanResult validation = validate(logics); if (!validation.succeeded) @@ -258,11 +235,9 @@ LogicScanResult SoftwareLogicExecutor::executeScanAt( } bool output_value = false; result = executeOutput( - logic.id, *rung.output, rung_value, repository, - now, logic_trace, &output_value); if (!result.succeeded) @@ -294,8 +269,6 @@ LogicScanResult SoftwareLogicExecutor::executeScanAt( trace->expressionInputValues = values->second.expressionInputValues; trace->expressionPowerValues = values->second.expressionPowerValues; trace->rungValues = values->second.rungValues; - trace->tonValues = values->second.tonValues; - trace->counterValues = values->second.counterValues; trace->wordValues = values->second.wordValues; } } @@ -383,7 +356,7 @@ LogicScanResult SoftwareLogicExecutor::evaluateCondition( RegisterRepository &repository, bool *value) { - // 根据条件节点的具体配置读取 M/D 或内部 T/C 状态,并计算触点结果 + // 根据条件节点的具体配置读取 M/D 状态,并计算触点结果 // 边沿触点使用 logic_id 和 node.id 组成运行时状态键,避免同名节点互相影响 if (value == nullptr) { @@ -437,20 +410,6 @@ LogicScanResult SoftwareLogicExecutor::evaluateCondition( previous_edge_inputs_[runtime_key] = read.value; return success(); } - else if constexpr (std::is_same_v) - { - // 定时器触点不读 PLC 地址,而是读取本地 TON 的完成状态 - const bool done = ton_states_[config.address.index()].done; - *value = config.mode == ContactMode::NormallyOpen ? done : !done; - return success(); - } - else if constexpr (std::is_same_v) - { - // 计数器触点读取本地 CTU/CTD 的完成状态 - const bool done = counter_states_[config.address.index()].done; - *value = config.mode == ContactMode::NormallyOpen ? done : !done; - return success(); - } else if constexpr (std::is_same_v) { // 字比较触点读取 D 字,再按配置的比较运算符与目标值比较 @@ -525,11 +484,9 @@ LogicScanResult SoftwareLogicExecutor::readWordOperand( } LogicScanResult SoftwareLogicExecutor::executeOutput( - const std::string &logic_id, const LogicNode &node, bool rung_value, RegisterRepository &repository, - TimePoint now, LogicTraceValues *trace, bool *output_value) { @@ -542,136 +499,6 @@ LogicScanResult SoftwareLogicExecutor::executeOutput( {}, node.id); } - if (const auto *ton = std::get_if(&node.config)) - { - TonRuntimeState &state = ton_states_[ton->address.index()]; - if (!rung_value) - { - state = TonRuntimeState{}; - } - else if (!state.timing) - { - state.timing = true; - state.startedAt = now; - state.elapsed = std::chrono::milliseconds{0}; - state.done = false; - } - else - { - state.elapsed = std::chrono::duration_cast( - now >= state.startedAt ? now - state.startedAt : TimePoint::duration::zero()); - state.done = state.elapsed.count() >= ton->presetMs; - } - *output_value = state.done; - if (trace != nullptr) - { - trace->tonValues[node.id] = { - rung_value, - state.done, - state.elapsed.count(), - ton->presetMs}; - } - return success(); - } - - if (const auto *counter = std::get_if(&node.config)) - { - const BitReadResult reset_read = repository.readBit(counter->resetAddress); - if (!reset_read.succeeded) - { - return failure( - LogicScanError::RegisterReadFailed, - "读取计数器复位输入失败:" + counter->resetAddress.toString(), - {}, - {}, - node.id); - } - std::int16_t preset = 0; - LogicScanResult result = readWordOperand( - counter->preset, repository, &preset, node.id); - if (!result.succeeded) - { - return result; - } - const std::int32_t bounded_preset = std::max(0, preset); - preset = static_cast( - std::min(bounded_preset, std::numeric_limits::max())); - const WordReadResult current_read = repository.readWord( - counter->currentValueAddress); - if (!current_read.succeeded) - { - return failure( - LogicScanError::RegisterReadFailed, - "读取计数当前值失败:" + counter->currentValueAddress.toString(), - {}, - {}, - node.id); - } - std::int32_t current = std::max(0, current_read.value); - current = std::min(current, std::numeric_limits::max()); - const auto runtime_key = std::make_pair(logic_id, node.id); - const bool previous = previous_counter_inputs_[runtime_key]; - const bool rising = rung_value && !previous; - previous_counter_inputs_[runtime_key] = rung_value; - bool done = counter_states_[counter->address.index()].done; - if (reset_read.value) - { - current = counter->mode == CounterMode::Up ? 0 : preset; - done = counter->mode == CounterMode::Down && current == 0; - } - else if (rising) - { - if (counter->mode == CounterMode::Up) - { - if (current < preset) - { - ++current; - } - done = current >= preset; - } - else - { - if (current > 0) - { - --current; - } - done = current <= 0; - } - } - if (counter->mode == CounterMode::Up && current >= preset) - { - done = true; - } - if (counter->mode == CounterMode::Down && current <= 0) - { - done = true; - } - const std::int16_t current_value = static_cast(current); - const RegisterWriteResult write = repository.writeWord( - counter->currentValueAddress, current_value); - if (!write.succeeded) - { - return failure( - LogicScanError::RegisterWriteFailed, - "写入计数当前值失败:" + counter->currentValueAddress.toString(), - {}, - {}, - node.id); - } - counter_states_[counter->address.index()].done = done; - *output_value = done; - if (trace != nullptr) - { - trace->counterValues[node.id] = { - rung_value, - reset_read.value, - done, - current_value, - preset}; - } - return success(); - } - if (const auto *move = std::get_if(&node.config)) { if (!rung_value) diff --git a/app/src/services/software_logic_executor.h b/app/src/services/software_logic_executor.h index d51369f..809fea3 100644 --- a/app/src/services/software_logic_executor.h +++ b/app/src/services/software_logic_executor.h @@ -3,7 +3,6 @@ #include "domain/control_logic_model.h" #include "domain/register_repository.h" -#include #include #include #include @@ -32,25 +31,6 @@ struct LogicScanResult 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 { @@ -67,8 +47,6 @@ struct LogicTraceValues std::unordered_map expressionInputValues; // 表达式输入状态 std::unordered_map expressionPowerValues; // 表达式输出电源状态 std::unordered_map rungValues; // 网络最终逻辑值 - std::unordered_map tonValues; // TON 节点状态 - std::unordered_map counterValues; // CTU/CTD 节点状态 std::unordered_map wordValues; // MOVE/ADD/SUB 节点状态 // 清除当前逻辑或网络的全部轨迹 @@ -90,22 +68,19 @@ struct LogicTraceSnapshot : LogicTraceValues 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 &logics) const; /** * @brief 清除所有跨扫描运行状态 * - * 重置边沿触发、TON 定时器和 CTU/CTD 计数器;不修改寄存器仓库和轨迹对象 + * 重置边沿触发状态;不修改寄存器仓库和轨迹对象 */ void resetRuntime(); @@ -120,35 +95,12 @@ public: const std::vector &logics, RegisterRepository &repository, LogicTraceSnapshot *trace = nullptr); - /** - * @brief 使用指定时间执行一轮扫描 - * @param logics 按工程顺序扫描的控制逻辑集合 - * @param repository 扫描读取和写入的寄存器仓库 - * @param now 本轮扫描使用的单调时钟时间点 - * @param trace 可选的轨迹输出;适合测试 TON 等时间相关逻辑 - * @return 扫描结果 - */ - LogicScanResult executeScanAt( - const std::vector &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 repository 提供 M/D 值的寄存器仓库 * @param value 输出节点导通结果,不能为空 * @return 成功结果,或包含节点 ID 的逻辑/寄存器读取失败结果 */ @@ -176,21 +128,17 @@ private: bool *value); /** * @brief 在网络结果驱动下执行单个输出节点 - * @param logic_id 所属控制逻辑 ID,用于隔离计数器等跨扫描状态 * @param node 待读取配置并执行的输出节点 * @param rung_value 当前网络的逻辑结果,决定输出是否动作 * @param repository 提供输出读写所需的 M/D 寄存器仓库 - * @param now 本轮扫描的单调时钟时间点,TON 使用它计算累计时间 - * @param trace 可选的轨迹输出,用于记录 TON、计数器和字操作结果 + * @param trace 可选的轨迹输出,用于记录字操作结果 * @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); /** @@ -209,14 +157,4 @@ private: // 按逻辑 ID 和节点 ID 保存上一轮边沿输入 std::map, bool> previous_edge_inputs_; - // 按逻辑 ID 和节点 ID 保存上一轮计数输入 - std::map, bool> previous_counter_inputs_; - // 按定时器编号保存跨扫描状态 - std::unordered_map ton_states_; - // 单个计数器跨扫描保存的完成状态 - struct CounterRuntimeState - { - bool done = false; // 是否已经达到预置值 - }; - std::unordered_map counter_states_; // 按计数器编号保存状态 }; diff --git a/app/src/ui/logic_editor_widget.cpp b/app/src/ui/logic_editor_widget.cpp index fc2aa42..7fafc2c 100644 --- a/app/src/ui/logic_editor_widget.cpp +++ b/app/src/ui/logic_editor_widget.cpp @@ -48,16 +48,6 @@ QString registerAddressText(const RegisterAddress &address) return QString::fromStdString(address.toString()); } -QString timerAddressText(const TimerAddress &address) -{ - return QString::fromStdString(address.toString()); -} - -QString counterAddressText(const CounterAddress &address) -{ - return QString::fromStdString(address.toString()); -} - QString wordOperandText(const WordOperand &operand) { return operand.kind == WordOperandKind::Register @@ -119,16 +109,6 @@ QString nodeToolTip(const LogicNodeConfig &config) : LogicEditorWidget::tr("下降")) .arg(registerAddressText(value.address)); } - else if constexpr (std::is_same_v) - { - return LogicEditorWidget::tr("T 触点:%1") - .arg(timerAddressText(value.address)); - } - else if constexpr (std::is_same_v) - { - return LogicEditorWidget::tr("C 触点:%1") - .arg(counterAddressText(value.address)); - } else if constexpr (std::is_same_v) { return LogicEditorWidget::tr("输出线圈:%1") @@ -141,22 +121,6 @@ QString nodeToolTip(const LogicNodeConfig &config) .arg(comparisonText(value.comparison)) .arg(value.value); } - else if constexpr (std::is_same_v) - { - return LogicEditorWidget::tr("TON:%1,预设 %2 ms") - .arg(timerAddressText(value.address)) - .arg(value.presetMs); - } - else if constexpr (std::is_same_v) - { - return LogicEditorWidget::tr("%1:%2,CV %3,PV %4,复位 %5") - .arg(value.mode == CounterMode::Up - ? QStringLiteral("CTU") : QStringLiteral("CTD")) - .arg(counterAddressText(value.address)) - .arg(registerAddressText(value.currentValueAddress)) - .arg(wordOperandText(value.preset)) - .arg(registerAddressText(value.resetAddress)); - } else if constexpr (std::is_same_v) { return LogicEditorWidget::tr("MOVE:%1 -> %2") @@ -358,41 +322,6 @@ public: configured_ ? registerAddressText(edge->address) : tr("< M 地址 >")); drawRegisterComment(painter, register_comment_); } - else if (const auto *timer_contact = - std::get_if(&config_)) - { - painter->fillRect(QRectF(-25, -22, 50, 44), Qt::white); - painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-18, 0)); - painter->drawLine(QPointF(18, 0), QPointF(kNodeTerminalX, 0)); - painter->drawLine(QPointF(-18, -15), QPointF(-18, 15)); - painter->drawLine(QPointF(18, -15), QPointF(18, 15)); - if (timer_contact->mode == ContactMode::NormallyClosed) - { - painter->drawLine(QPointF(-23, 18), QPointF(23, -18)); - } - painter->drawText( - QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), - Qt::AlignCenter, - configured_ ? timerAddressText(timer_contact->address) : tr("< T 地址 >")); - } - else if (const auto *counter_contact = - std::get_if(&config_)) - { - painter->fillRect(QRectF(-25, -22, 50, 44), Qt::white); - painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-18, 0)); - painter->drawLine(QPointF(18, 0), QPointF(kNodeTerminalX, 0)); - painter->drawLine(QPointF(-18, -15), QPointF(-18, 15)); - painter->drawLine(QPointF(18, -15), QPointF(18, 15)); - if (counter_contact->mode == ContactMode::NormallyClosed) - { - painter->drawLine(QPointF(-23, 18), QPointF(23, -18)); - } - painter->drawText( - QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), - Qt::AlignCenter, - configured_ ? counterAddressText(counter_contact->address) - : tr("< C 地址 >")); - } else if (const auto *coil = std::get_if(&config_)) { painter->fillRect(QRectF(-35, -23, 70, 46), Qt::white); @@ -439,54 +368,6 @@ public: configured_ ? QString::number(comparison->value) : tr("< 常量 >")); drawRegisterComment(painter, register_comment_, 40.0); } - else if (const auto *ton = std::get_if(&config_)) - { - const QRectF box(-47, -18, 94, 36); - painter->fillRect(box.adjusted(-1, -1, 1, 1), Qt::white); - painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-47, 0)); - painter->drawLine(QPointF(47, 0), QPointF(kNodeTerminalX, 0)); - painter->drawRect(box); - painter->drawText(box, Qt::AlignCenter, QStringLiteral("TON")); - painter->drawText( - QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), - Qt::AlignCenter, - configured_ ? timerAddressText(ton->address) : tr("< T 地址 >")); - painter->drawText( - QRectF(-kCellWidth / 2.0, 22, kCellWidth, 18), - Qt::AlignCenter, - configured_ ? QStringLiteral("PT %1 ms").arg(ton->presetMs) - : tr("< 预设时间 >")); - } - else if (const auto *counter = std::get_if(&config_)) - { - const QRectF box(-47, -18, 94, 36); - painter->fillRect(box.adjusted(-1, -1, 1, 1), Qt::white); - painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-47, 0)); - painter->drawLine(QPointF(47, 0), QPointF(kNodeTerminalX, 0)); - painter->drawRect(box); - painter->drawText( - box, - Qt::AlignCenter, - counter->mode == CounterMode::Up - ? QStringLiteral("CTU") : QStringLiteral("CTD")); - painter->drawText( - QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), - Qt::AlignCenter, - configured_ ? counterAddressText(counter->address) - : tr("< C 地址 >")); - painter->drawText( - QRectF(-kCellWidth / 2.0 + 4.0, 20, kCellWidth - 8.0, 18), - Qt::AlignCenter | Qt::TextSingleLine, - configured_ ? QStringLiteral("CV %1") - .arg(registerAddressText(counter->currentValueAddress)) - : tr("< CV >")); - painter->drawText( - QRectF(-kCellWidth / 2.0 + 4.0, 38, kCellWidth - 8.0, 18), - Qt::AlignCenter | Qt::TextSingleLine, - configured_ ? QStringLiteral("PV %1") - .arg(wordOperandText(counter->preset)) - : tr("< PV >")); - } else if (const auto *move = std::get_if(&config_)) { const QRectF box(-47, -18, 94, 36); @@ -1228,17 +1109,7 @@ void LogicEditorWidget::reloadLogic() editor_service_.registerCommentFor(*address)) : QString{}; }(), - [&rung, this, rung_active] - { - return (std::holds_alternative(rung.output->config) - || std::holds_alternative( - rung.output->config)) - ? traceValue( - trace_, - &LogicTraceSnapshot::nodeValues, - rung.output->id) - : rung_active; - }(), + rung_active, rung.output->id == fault_node_id_, false)); } diff --git a/app/src/ui/logic_instruction_dialog.cpp b/app/src/ui/logic_instruction_dialog.cpp index 7073f83..ff7518d 100644 --- a/app/src/ui/logic_instruction_dialog.cpp +++ b/app/src/ui/logic_instruction_dialog.cpp @@ -31,14 +31,12 @@ LogicInstructionDialog::LogicInstructionDialog( original_config_(config) { ui_->setupUi(this); - configureOperandCombo(ui_->counterPresetKindComboBox); configureOperandCombo(ui_->leftOperandKindComboBox); configureOperandCombo(ui_->rightOperandKindComboBox); connect(ui_->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(ui_->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); for (QComboBox *combo : { - ui_->counterPresetKindComboBox, ui_->leftOperandKindComboBox, ui_->rightOperandKindComboBox}) { @@ -49,11 +47,7 @@ LogicInstructionDialog::LogicInstructionDialog( &LogicInstructionDialog::updateOperandRanges); } - if (const auto *counter = std::get_if(&config)) - { - configureCounter(*counter); - } - else if (const auto *move = std::get_if(&config)) + if (const auto *move = std::get_if(&config)) { configureMove(*move); } @@ -68,21 +62,6 @@ LogicInstructionDialog::~LogicInstructionDialog() = default; LogicNodeConfig LogicInstructionDialog::config() const { - if (std::holds_alternative(original_config_)) - { - return CounterNodeConfig{ - CounterAddress{ui_->counterAddressSpinBox->value()}, - static_cast(ui_->counterModeComboBox->currentData().toInt()), - RegisterAddress{ - RegisterArea::D, - ui_->counterCurrentAddressSpinBox->value()}, - operandFrom( - ui_->counterPresetKindComboBox, - ui_->counterPresetValueSpinBox), - RegisterAddress{ - RegisterArea::M, - ui_->counterResetAddressSpinBox->value()}}; - } if (std::holds_alternative(original_config_)) { return MoveNodeConfig{ @@ -101,25 +80,6 @@ LogicNodeConfig LogicInstructionDialog::config() const ui_->destinationAddressSpinBox->value()}}; } -void LogicInstructionDialog::configureCounter(const CounterNodeConfig &config) -{ - setWindowTitle(tr("配置计数器")); - ui_->instructionStackedWidget->setCurrentWidget(ui_->counterPage); - ui_->counterModeComboBox->clear(); - ui_->counterModeComboBox->addItem(tr("CTU 加计数"), static_cast(CounterMode::Up)); - ui_->counterModeComboBox->addItem( - tr("CTD 减计数"), static_cast(CounterMode::Down)); - ui_->counterModeComboBox->setCurrentIndex( - ui_->counterModeComboBox->findData(static_cast(config.mode))); - ui_->counterAddressSpinBox->setValue(config.address.index()); - ui_->counterCurrentAddressSpinBox->setValue(config.currentValueAddress.index()); - ui_->counterResetAddressSpinBox->setValue(config.resetAddress.index()); - setOperand( - ui_->counterPresetKindComboBox, - ui_->counterPresetValueSpinBox, - config.preset); -} - void LogicInstructionDialog::configureMove(const MoveNodeConfig &config) { setWindowTitle(tr("配置 MOVE")); @@ -165,13 +125,11 @@ void LogicInstructionDialog::updateOperandRanges() spin->setPrefix(register_operand ? QStringLiteral("D") : QString{}); spin->setMinimum(register_operand ? RegisterAddress::kMinimumIndex - : (combo->objectName() == QStringLiteral("counterPresetKindComboBox") - ? 0 : std::numeric_limits::min())); + : std::numeric_limits::min()); spin->setMaximum(register_operand ? RegisterAddress::kMaximumIndex : std::numeric_limits::max()); }; - update(ui_->counterPresetKindComboBox, ui_->counterPresetValueSpinBox); update(ui_->leftOperandKindComboBox, ui_->leftOperandValueSpinBox); update(ui_->rightOperandKindComboBox, ui_->rightOperandValueSpinBox); } diff --git a/app/src/ui/logic_instruction_dialog.h b/app/src/ui/logic_instruction_dialog.h index 05c982b..0034173 100644 --- a/app/src/ui/logic_instruction_dialog.h +++ b/app/src/ui/logic_instruction_dialog.h @@ -38,8 +38,6 @@ public: LogicNodeConfig config() const; private: - /** 根据计数器配置初始化计数器输入项 */ - void configureCounter(const CounterNodeConfig &config); /** 根据传送配置初始化传送输入项 */ void configureMove(const MoveNodeConfig &config); /** 根据算术配置初始化运算输入项 */ diff --git a/app/src/ui/logic_instruction_dialog.ui b/app/src/ui/logic_instruction_dialog.ui index 54fc185..246cb9e 100644 --- a/app/src/ui/logic_instruction_dialog.ui +++ b/app/src/ui/logic_instruction_dialog.ui @@ -9,27 +9,6 @@ - - - 计数方式 - - 计数器资源 - C4000 - 当前值 CV - D4000 - 预设值 PV - - - 0000 - - 3276710 - - - - 复位输入 - M4000 - - 指令 diff --git a/app/src/ui/main_window.cpp b/app/src/ui/main_window.cpp index c3c1d36..c804e62 100644 --- a/app/src/ui/main_window.cpp +++ b/app/src/ui/main_window.cpp @@ -423,8 +423,6 @@ void MainWindow::configureActions() QAction *parallel_closed = parallel_menu->addAction(tr("并联常闭触点")); QAction *parallel_rising = parallel_menu->addAction(tr("并联上升沿触点")); QAction *parallel_falling = parallel_menu->addAction(tr("并联下降沿触点")); - QAction *parallel_timer = parallel_menu->addAction(tr("并联 T 触点")); - QAction *parallel_counter = parallel_menu->addAction(tr("并联 C 触点")); QAction *parallel_compare = parallel_menu->addAction(tr("并联比较条件")); connect(parallel_open, &QAction::triggered, this, @@ -456,20 +454,6 @@ void MainWindow::configureActions() addLogicParallelBranch(EdgeContactNodeConfig{ RegisterAddress{RegisterArea::M, 0}, EdgeMode::Falling}); }); - connect(parallel_timer, &QAction::triggered, - this, - [this] - { - addLogicParallelBranch(TimerContactNodeConfig{ - TimerAddress{0}, ContactMode::NormallyOpen}); - }); - connect(parallel_counter, &QAction::triggered, - this, - [this] - { - addLogicParallelBranch(CounterContactNodeConfig{ - CounterAddress{0}, ContactMode::NormallyOpen}); - }); connect(parallel_compare, &QAction::triggered, this, [this] @@ -515,20 +499,6 @@ void MainWindow::configureActions() addLogicCondition(EdgeContactNodeConfig{ RegisterAddress{RegisterArea::M, 0}, EdgeMode::Falling}); }); - connect(ui_->addTimerContactAction, &QAction::triggered, - this, - [this] - { - addLogicCondition(TimerContactNodeConfig{ - TimerAddress{0}, ContactMode::NormallyOpen}); - }); - connect(ui_->addCounterContactAction, &QAction::triggered, - this, - [this] - { - addLogicCondition(CounterContactNodeConfig{ - CounterAddress{0}, ContactMode::NormallyOpen}); - }); connect(ui_->addNormalCoilAction, &QAction::triggered, this, [this] @@ -553,40 +523,6 @@ void MainWindow::configureActions() RegisterAddress{RegisterArea::M, 0}, CoilMode::Reset}); }); - connect(ui_->addTonAction, &QAction::triggered, - this, - [this] - { - setLogicOutput(TonNodeConfig{TimerAddress{0}, 1000}); - }); - connect(ui_->addCtuAction, &QAction::triggered, - this, - [this] - { - configureAndSetLogicOutput(CounterNodeConfig{ - CounterAddress{0}, - CounterMode::Up, - RegisterAddress{RegisterArea::D, 0}, - WordOperand{ - WordOperandKind::Constant, - RegisterAddress{RegisterArea::D, 0}, - 10}, - RegisterAddress{RegisterArea::M, 0}}); - }); - connect(ui_->addCtdAction, &QAction::triggered, - this, - [this] - { - configureAndSetLogicOutput(CounterNodeConfig{ - CounterAddress{0}, - CounterMode::Down, - RegisterAddress{RegisterArea::D, 0}, - WordOperand{ - WordOperandKind::Constant, - RegisterAddress{RegisterArea::D, 0}, - 10}, - RegisterAddress{RegisterArea::M, 0}}); - }); connect(ui_->addMoveAction, &QAction::triggered, this, [this] @@ -650,21 +586,13 @@ void MainWindow::configureActions() makeUiIcon(UiIcon::NormallyOpenContact), QList{ ui_->addRisingEdgeAction, - ui_->addFallingEdgeAction, - ui_->addTimerContactAction, - ui_->addCounterContactAction}); + ui_->addFallingEdgeAction}); addToolbarMenu( ui_->logicToolBar, tr("更多输出"), QStringLiteral("logicOutputMenuButton"), makeUiIcon(UiIcon::Coil), QList{ui_->addSetCoilAction, ui_->addResetCoilAction}); - addToolbarMenu( - ui_->logicToolBar, - tr("定时/计数"), - QStringLiteral("logicTimerCounterMenuButton"), - makeUiIcon(UiIcon::Ton), - QList{ui_->addTonAction, ui_->addCtuAction, ui_->addCtdAction}); addToolbarMenu( ui_->logicToolBar, tr("数据运算"), @@ -749,14 +677,9 @@ void MainWindow::configureAppearance() ui_->addNormallyClosedAction->setIcon(makeUiIcon(UiIcon::NormallyClosedContact)); ui_->addRisingEdgeAction->setIcon(makeUiIcon(UiIcon::RisingEdgeContact)); ui_->addFallingEdgeAction->setIcon(makeUiIcon(UiIcon::FallingEdgeContact)); - ui_->addTimerContactAction->setIcon(makeUiIcon(UiIcon::TimerContact)); - ui_->addCounterContactAction->setIcon(makeUiIcon(UiIcon::CounterContact)); ui_->addNormalCoilAction->setIcon(makeUiIcon(UiIcon::Coil)); ui_->addSetCoilAction->setIcon(makeUiIcon(UiIcon::SetCoil)); ui_->addResetCoilAction->setIcon(makeUiIcon(UiIcon::ResetCoil)); - ui_->addTonAction->setIcon(makeUiIcon(UiIcon::Ton)); - ui_->addCtuAction->setIcon(makeUiIcon(UiIcon::Ctu)); - ui_->addCtdAction->setIcon(makeUiIcon(UiIcon::Ctd)); ui_->addMoveAction->setIcon(makeUiIcon(UiIcon::Move)); ui_->addAddAction->setIcon(makeUiIcon(UiIcon::Add)); ui_->addSubAction->setIcon(makeUiIcon(UiIcon::Subtract)); @@ -1601,14 +1524,9 @@ void MainWindow::updateModeUi(const QString &message) ui_->addNormallyClosedAction->setEnabled(policy.allowsProjectEditing); ui_->addRisingEdgeAction->setEnabled(policy.allowsProjectEditing); ui_->addFallingEdgeAction->setEnabled(policy.allowsProjectEditing); - ui_->addTimerContactAction->setEnabled(policy.allowsProjectEditing); - ui_->addCounterContactAction->setEnabled(policy.allowsProjectEditing); ui_->addNormalCoilAction->setEnabled(policy.allowsProjectEditing); ui_->addSetCoilAction->setEnabled(policy.allowsProjectEditing); ui_->addResetCoilAction->setEnabled(policy.allowsProjectEditing); - ui_->addTonAction->setEnabled(policy.allowsProjectEditing); - ui_->addCtuAction->setEnabled(policy.allowsProjectEditing); - ui_->addCtdAction->setEnabled(policy.allowsProjectEditing); ui_->addMoveAction->setEnabled(policy.allowsProjectEditing); ui_->addAddAction->setEnabled(policy.allowsProjectEditing); ui_->addSubAction->setEnabled(policy.allowsProjectEditing); diff --git a/app/src/ui/main_window.ui b/app/src/ui/main_window.ui index 838776b..7817a20 100644 --- a/app/src/ui/main_window.ui +++ b/app/src/ui/main_window.ui @@ -922,27 +922,7 @@ - - - - 预设时间(ms) - - - - - - - 1 - - - 86400000 - - - 1000 - - - - + 应用属性 @@ -1252,18 +1232,6 @@ 在选中的空网格或横线处添加一个扫描周期的下降沿触点 - - - T 触点 - - - 在选中的空网格或横线处添加离线仿真定时器触点 - - - - C 触点 - 在选中的空网格或横线处添加离线仿真计数器完成触点 - 线圈 @@ -1288,22 +1256,6 @@ 设置当前网络的复位线圈 - - - TON - - - 设置非保持接通延时定时器输出 - - - - CTU - 设置按上升沿执行的加计数器 - - - CTD - 设置按上升沿执行的减计数器 - MOVE 设置 D 数据传送指令 diff --git a/app/src/ui/property_panel_controller.cpp b/app/src/ui/property_panel_controller.cpp index 4fa1fac..3552e3b 100644 --- a/app/src/ui/property_panel_controller.cpp +++ b/app/src/ui/property_panel_controller.cpp @@ -378,7 +378,6 @@ void PropertyPanelController::showLogicNodeProperties(const std::string &node_id static_cast(ui_.logicModeComboBox), static_cast(ui_.logicComparisonComboBox), static_cast(ui_.logicValueSpinBox), - static_cast(ui_.logicPresetSpinBox), static_cast(ui_.applyLogicPropertiesButton)}) { widget->setEnabled(has_node); @@ -391,7 +390,6 @@ void PropertyPanelController::showLogicNodeProperties(const std::string &node_id ui_.logicNodeTypeLabel->clear(); ui_.logicAddressAreaLabel->clear(); ui_.logicModeComboBox->clear(); - ui_.logicPresetSpinBox->setValue(TonNodeConfig::kMinimumPresetMs); return; } @@ -399,7 +397,6 @@ void PropertyPanelController::showLogicNodeProperties(const std::string &node_id ui_.logicModeComboBox->clear(); ui_.logicComparisonComboBox->setEnabled(false); ui_.logicValueSpinBox->setEnabled(false); - ui_.logicPresetSpinBox->setEnabled(false); if (const auto *contact = std::get_if(&node->config)) { ui_.logicNodeTypeLabel->setText(QObject::tr("触点")); @@ -426,33 +423,6 @@ void PropertyPanelController::showLogicNodeProperties(const std::string &node_id ui_.logicModeComboBox->findData(static_cast(edge->mode))); ui_.logicModeComboBox->setEnabled(true); } - else if (const auto *timer = std::get_if(&node->config)) - { - ui_.logicNodeTypeLabel->setText(QObject::tr("T 触点")); - ui_.logicAddressAreaLabel->setText(QStringLiteral("T")); - ui_.logicAddressSpinBox->setValue(timer->address.index()); - ui_.logicModeComboBox->addItem( - QObject::tr("常开"), static_cast(ContactMode::NormallyOpen)); - ui_.logicModeComboBox->addItem( - QObject::tr("常闭"), static_cast(ContactMode::NormallyClosed)); - ui_.logicModeComboBox->setCurrentIndex( - ui_.logicModeComboBox->findData(static_cast(timer->mode))); - ui_.logicModeComboBox->setEnabled(true); - } - else if (const auto *counter_contact = - std::get_if(&node->config)) - { - ui_.logicNodeTypeLabel->setText(QObject::tr("C 触点")); - ui_.logicAddressAreaLabel->setText(QStringLiteral("C")); - ui_.logicAddressSpinBox->setValue(counter_contact->address.index()); - ui_.logicModeComboBox->addItem( - QObject::tr("常开"), static_cast(ContactMode::NormallyOpen)); - ui_.logicModeComboBox->addItem( - QObject::tr("常闭"), static_cast(ContactMode::NormallyClosed)); - ui_.logicModeComboBox->setCurrentIndex(ui_.logicModeComboBox->findData( - static_cast(counter_contact->mode))); - ui_.logicModeComboBox->setEnabled(true); - } else if (const auto *coil = std::get_if(&node->config)) { ui_.logicNodeTypeLabel->setText(QObject::tr("线圈")); @@ -482,27 +452,6 @@ void PropertyPanelController::showLogicNodeProperties(const std::string &node_id ui_.logicValueSpinBox->setEnabled(true); ui_.logicValueSpinBox->setValue(compare->value); } - else if (const auto *ton = std::get_if(&node->config)) - { - ui_.logicNodeTypeLabel->setText(QObject::tr("TON")); - ui_.logicAddressAreaLabel->setText(QStringLiteral("T")); - ui_.logicAddressSpinBox->setValue(ton->address.index()); - ui_.logicModeComboBox->addItem(QObject::tr("非保持接通延时")); - ui_.logicModeComboBox->setEnabled(false); - ui_.logicPresetSpinBox->setEnabled(true); - ui_.logicPresetSpinBox->setValue(ton->presetMs); - } - else if (const auto *counter = std::get_if(&node->config)) - { - ui_.logicNodeTypeLabel->setText( - counter->mode == CounterMode::Up ? QStringLiteral("CTU") - : QStringLiteral("CTD")); - ui_.logicAddressAreaLabel->setText(QStringLiteral("C")); - ui_.logicAddressSpinBox->setValue(counter->address.index()); - ui_.logicAddressSpinBox->setEnabled(false); - ui_.logicModeComboBox->addItem(QObject::tr("使用配置对话框编辑")); - ui_.logicModeComboBox->setEnabled(false); - } else if (const auto *move = std::get_if(&node->config)) { ui_.logicNodeTypeLabel->setText(QStringLiteral("MOVE")); @@ -726,18 +675,6 @@ void PropertyPanelController::applySelectedLogicNodeProperties() RegisterAddress{RegisterArea::M, ui_.logicAddressSpinBox->value()}, static_cast(ui_.logicModeComboBox->currentData().toInt())}; } - else if (std::holds_alternative(config)) - { - config = TimerContactNodeConfig{ - TimerAddress{ui_.logicAddressSpinBox->value()}, - static_cast(ui_.logicModeComboBox->currentData().toInt())}; - } - else if (std::holds_alternative(config)) - { - config = CounterContactNodeConfig{ - CounterAddress{ui_.logicAddressSpinBox->value()}, - static_cast(ui_.logicModeComboBox->currentData().toInt())}; - } else if (std::holds_alternative(config)) { config = CoilNodeConfig{ @@ -752,12 +689,6 @@ void PropertyPanelController::applySelectedLogicNodeProperties() ui_.logicComparisonComboBox->currentData().toInt()), static_cast(ui_.logicValueSpinBox->value())}; } - else if (std::holds_alternative(config)) - { - config = TonNodeConfig{ - TimerAddress{ui_.logicAddressSpinBox->value()}, - ui_.logicPresetSpinBox->value()}; - } else { LogicInstructionDialog dialog(node->config, &parent_); diff --git a/app/src/ui/toolbar_icon_factory.cpp b/app/src/ui/toolbar_icon_factory.cpp index c19f81e..10aacb7 100644 --- a/app/src/ui/toolbar_icon_factory.cpp +++ b/app/src/ui/toolbar_icon_factory.cpp @@ -410,16 +410,6 @@ QPixmap renderIcon(UiIcon icon, int size) drawContact(painter, QStringLiteral("N"), false); break; } - case UiIcon::TimerContact: - { - drawContact(painter, QStringLiteral("T"), false); - break; - } - case UiIcon::CounterContact: - { - drawContact(painter, QStringLiteral("C"), false); - break; - } case UiIcon::Coil: { drawCoil(painter, {}); @@ -435,21 +425,6 @@ QPixmap renderIcon(UiIcon icon, int size) drawCoil(painter, QStringLiteral("R")); break; } - case UiIcon::Ton: - { - drawInstructionBlock(painter, QStringLiteral("TON")); - break; - } - case UiIcon::Ctu: - { - drawInstructionBlock(painter, QStringLiteral("CTU")); - break; - } - case UiIcon::Ctd: - { - drawInstructionBlock(painter, QStringLiteral("CTD")); - break; - } case UiIcon::Move: { painter.drawRect(QRectF(2, 6, 6, 12)); diff --git a/app/src/ui/toolbar_icon_factory.h b/app/src/ui/toolbar_icon_factory.h index bf63ab2..ea330ec 100644 --- a/app/src/ui/toolbar_icon_factory.h +++ b/app/src/ui/toolbar_icon_factory.h @@ -44,14 +44,9 @@ enum class UiIcon NormallyClosedContact, // 常闭触点 RisingEdgeContact, // 上升沿触点 FallingEdgeContact, // 下降沿触点 - TimerContact, // 定时器触点 - CounterContact, // 计数器触点 Coil, // 普通线圈 SetCoil, // 置位线圈 ResetCoil, // 复位线圈 - Ton, // TON 定时器 - Ctu, // CTU 加计数器 - Ctd, // CTD 减计数器 Move, // MOVE 指令 Add, // ADD 指令 Subtract, // SUB 指令 diff --git a/app/tests/domain_tests.cpp b/app/tests/domain_tests.cpp index ad512a3..383e1a9 100644 --- a/app/tests/domain_tests.cpp +++ b/app/tests/domain_tests.cpp @@ -434,14 +434,6 @@ void testQuantityBoundaries() require(!excessive_depth.validate(), "an expression depth of 21 must be rejected"); - CounterNodeConfig counter_config{ - CounterAddress{0}, - CounterMode::Up, - RegisterAddress{RegisterArea::D, 0}, - WordOperand{WordOperandKind::Constant, RegisterAddress{RegisterArea::D, 0}, -1}, - RegisterAddress{RegisterArea::M, 0}}; - LogicNode counter{"counter-negative-preset", counter_config, true}; - require(!counter.validate(), "a negative constant counter preset must be rejected"); (void)leaf; } @@ -469,7 +461,7 @@ void testLogicNodeConfigurationBoundaries() require(comparison.validate(), "comparison node bound to D address must be valid"); } -void testTimerAndCommentBoundaries() +void testEdgeAndCommentBoundaries() { LogicNode edge; edge.id = "edge"; @@ -477,25 +469,6 @@ void testTimerAndCommentBoundaries() RegisterAddress{RegisterArea::M, 0}, EdgeMode::Rising}; require(edge.validate(), "a valid rising edge contact must pass validation"); - LogicNode timer_contact; - timer_contact.id = "timer-contact"; - timer_contact.config = TimerContactNodeConfig{ - TimerAddress{4000}, ContactMode::NormallyOpen}; - require(timer_contact.validate(), "T4000 must be a valid timer contact"); - timer_contact.config = TimerContactNodeConfig{ - TimerAddress{-1}, ContactMode::NormallyOpen}; - require(!timer_contact.validate(), "a negative T address must be rejected"); - - LogicNode ton; - ton.id = "ton"; - ton.config = TonNodeConfig{TimerAddress{0}, TonNodeConfig::kMinimumPresetMs}; - require(ton.validate(), "the minimum TON preset must be valid"); - ton.config = TonNodeConfig{TimerAddress{0}, 0}; - require(!ton.validate(), "a zero TON preset must be rejected"); - ton.config = TonNodeConfig{ - TimerAddress{0}, TonNodeConfig::kMaximumPresetMs + 1}; - require(!ton.validate(), "an oversized TON preset must be rejected"); - RegisterComment comment{RegisterAddress{RegisterArea::M, 0}, "启动按钮"}; require(comment.validate(), "a nonblank register comment must be valid"); comment.text.assign(ProjectLimits::kMaximumRegisterCommentBytes, 'a'); @@ -528,124 +501,8 @@ void testTimerAndCommentBoundaries() require(!project.validate(), "duplicate register comments must be rejected"); } -LadderRung makeTimerRung( - const std::string &rung_id, - const std::string &condition_id, - const std::string &output_id, - int timer_index, - int preset_ms) -{ - LogicNode condition; - condition.id = condition_id; - condition.config = ContactNodeConfig{ - RegisterAddress{RegisterArea::M, timer_index}, ContactMode::NormallyOpen}; - LogicNode output; - output.id = output_id; - output.config = TonNodeConfig{TimerAddress{timer_index}, preset_ms}; - LadderRung rung; - rung.id = rung_id; - rung.name = rung_id; - rung.condition = ConditionExpression::fromNode(condition); - rung.output = output; - return rung; -} - -void testTimerReferencesForRunning() +void testDataInstructionBoundaries() { - ControlLogic valid; - valid.id = "timer-valid"; - valid.name = "Timer valid"; - valid.rungs.push_back(makeTimerRung("rung-0", "input-0", "ton-0", 0, 100)); - require(validateTimerReferencesForRunning({valid}), - "a timer contact-free TON network must pass timer reference validation"); - - ControlLogic duplicate = valid; - duplicate.id = "timer-duplicate"; - duplicate.name = "Timer duplicate"; - duplicate.rungs.front().output->id = "ton-duplicate"; - require(!validateTimerReferencesForRunning({valid, duplicate}), - "the same T must not have two enabled TON drivers"); - - ControlLogic missing; - missing.id = "timer-missing"; - missing.name = "Timer missing"; - LogicNode contact; - contact.id = "missing-contact"; - contact.config = TimerContactNodeConfig{ - TimerAddress{7}, ContactMode::NormallyOpen}; - LogicNode output; - output.id = "missing-coil"; - output.config = CoilNodeConfig{ - RegisterAddress{RegisterArea::M, 7}, CoilMode::Normal}; - LadderRung missing_rung; - missing_rung.id = "missing-rung"; - missing_rung.name = "Missing rung"; - missing_rung.condition = ConditionExpression::fromNode(contact); - missing_rung.output = output; - missing.rungs.push_back(missing_rung); - require(!validateTimerReferencesForRunning({missing}), - "a T contact without an enabled TON driver must be rejected"); - - missing.enabled = false; - require(validateTimerReferencesForRunning({missing}), - "disabled timer drafts must not block runtime timer validation"); -} - -LadderRung makeCounterRung( - const std::string &rung_id, - const std::string &output_id, - int counter_index) -{ - LogicNode condition; - condition.id = rung_id + "-input"; - condition.config = ContactNodeConfig{ - RegisterAddress{RegisterArea::M, counter_index}, - ContactMode::NormallyOpen}; - LogicNode output; - output.id = output_id; - output.config = CounterNodeConfig{ - CounterAddress{counter_index}, - CounterMode::Up, - RegisterAddress{RegisterArea::D, counter_index}, - WordOperand{ - WordOperandKind::Constant, - RegisterAddress{RegisterArea::D, 0}, - 10}, - RegisterAddress{RegisterArea::M, counter_index + 1}}; - LadderRung rung; - rung.id = rung_id; - rung.name = rung_id; - rung.condition = ConditionExpression::fromNode(condition); - rung.output = output; - return rung; -} - -void testCounterAndDataInstructionBoundaries() -{ - require(CounterAddress{0}.isValid() && CounterAddress{4000}.isValid(), - "C0 and C4000 must be valid counter resources"); - require(!CounterAddress{-1}.isValid() && !CounterAddress{4001}.isValid(), - "counter resources outside 0 through 4000 must be rejected"); - - LogicNode counter; - counter.id = "counter"; - counter.config = CounterNodeConfig{ - CounterAddress{0}, - CounterMode::Up, - RegisterAddress{RegisterArea::D, 10}, - WordOperand{ - WordOperandKind::Register, - RegisterAddress{RegisterArea::D, 11}, - 0}, - RegisterAddress{RegisterArea::M, 12}}; - require(counter.validate(), - "a counter with C identity and external M/D addresses must be valid"); - - CounterNodeConfig invalid_counter = std::get(counter.config); - invalid_counter.currentValueAddress = RegisterAddress{RegisterArea::M, 10}; - counter.config = invalid_counter; - require(!counter.validate(), "counter CV must reject M addresses"); - LogicNode move; move.id = "move"; move.config = MoveNodeConfig{ @@ -673,39 +530,6 @@ void testCounterAndDataInstructionBoundaries() require(add.validate() && add.isOutput(), "ADD must allow the same D register as source and destination"); - ControlLogic valid; - valid.id = "counter-valid"; - valid.name = "Counter valid"; - valid.rungs.push_back(makeCounterRung("counter-rung", "ctu-0", 0)); - require(validateCounterReferencesForRunning({valid}), - "a counter output without contacts must pass reference validation"); - - ControlLogic duplicate = valid; - duplicate.id = "counter-duplicate"; - duplicate.name = "Counter duplicate"; - duplicate.rungs.front().output->id = "ctu-duplicate"; - require(!validateCounterReferencesForRunning({valid, duplicate}), - "the same C resource must not have multiple enabled drivers"); - - ControlLogic missing; - missing.id = "counter-missing"; - missing.name = "Counter missing"; - LogicNode missing_contact; - missing_contact.id = "missing-counter-contact"; - missing_contact.config = CounterContactNodeConfig{ - CounterAddress{7}, ContactMode::NormallyOpen}; - LogicNode output; - output.id = "missing-counter-coil"; - output.config = CoilNodeConfig{ - RegisterAddress{RegisterArea::M, 7}, CoilMode::Normal}; - LadderRung missing_rung; - missing_rung.id = "missing-counter-rung"; - missing_rung.name = "Missing counter rung"; - missing_rung.condition = ConditionExpression::fromNode(missing_contact); - missing_rung.output = output; - missing.rungs.push_back(missing_rung); - require(!validateCounterReferencesForRunning({missing}), - "a C contact without an enabled counter driver must be rejected"); } void testLadderLogicBoundaries() @@ -914,9 +738,8 @@ int main() testProgressBarConfigurationBoundaries(); testHmiAppearancePropertyBoundaries(); testLogicNodeConfigurationBoundaries(); - testTimerAndCommentBoundaries(); - testTimerReferencesForRunning(); - testCounterAndDataInstructionBoundaries(); + testEdgeAndCommentBoundaries(); + testDataInstructionBoundaries(); testLadderLogicBoundaries(); testModelsValidateBindingsAndIdentifiers(); testMultiPageAndLogicDomainRules(); diff --git a/app/tests/logic_editor_service_tests.cpp b/app/tests/logic_editor_service_tests.cpp index e06f2f8..90c671d 100644 --- a/app/tests/logic_editor_service_tests.cpp +++ b/app/tests/logic_editor_service_tests.cpp @@ -883,7 +883,7 @@ void testLogicLifecycleAndOrdering() "the project must retain at least one control logic module"); } -void testEdgeTimerNodesAndRungComments() +void testEdgeNodesAndRungComments() { TestProjectStorage storage; ProjectService project_service(storage); @@ -898,18 +898,6 @@ void testEdgeTimerNodesAndRungComments() RegisterAddress{RegisterArea::M, 3}, EdgeMode::Rising}); require(rising.succeeded && rising.id == "edge-1", "the editor must create rising edge nodes with a stable prefix"); - const LogicEditorResult timer = service.addParallelBranch( - logic_id, - rung_id, - {rising.id}, - TimerContactNodeConfig{TimerAddress{2}, ContactMode::NormallyOpen}); - require(timer.succeeded && timer.id == "timer-contact-1", - "the editor must insert T contacts as conditions"); - const LogicEditorResult ton = service.setOutput( - logic_id, rung_id, TonNodeConfig{TimerAddress{2}, 500}); - require(ton.succeeded && ton.id == "ton-1", - "the editor must create TON outputs with a stable prefix"); - require(service.updateNodeConfig( logic_id, rising.id, @@ -917,18 +905,6 @@ void testEdgeTimerNodesAndRungComments() RegisterAddress{RegisterArea::M, 4}, EdgeMode::Falling}) .succeeded, "the editor must apply edge mode and M address properties"); - require(service.updateNodeConfig( - logic_id, - timer.id, - TimerContactNodeConfig{TimerAddress{4}, ContactMode::NormallyClosed}) - .succeeded, - "the editor must apply T contact mode and address properties"); - require(service.updateNodeConfig( - logic_id, - ton.id, - TonNodeConfig{TimerAddress{4}, 750}) - .succeeded, - "the editor must apply TON preset properties"); require(service.updateRungComment(logic_id, rung_id, "延时启动网络").succeeded, "the editor must update a network comment by stable rung id"); require(!service.updateRungComment( @@ -942,15 +918,12 @@ void testEdgeTimerNodesAndRungComments() "the editor must reject oversized network comments"); const LadderRung *rung = service.findRung(logic_id, rung_id); + const LogicNode *edge = service.findNode(logic_id, rising.id); require(rung != nullptr && rung->comment == "延时启动网络" - && std::get( - rung->condition->children.front().node->config).mode - == EdgeMode::Falling - && std::get( - rung->condition->children.at(1).node->config).address - == TimerAddress{4} - && std::get(rung->output->config).presetMs == 750, - "edge, T contact, TON and rung comment updates must remain in the model"); + && edge != nullptr + && std::get(edge->config).mode + == EdgeMode::Falling, + "edge and rung comment updates must remain in the model"); } void testHistoryAndAtomicBatchDelete() @@ -1045,7 +1018,7 @@ int main() testWireColumnReplacement(); testSequentialConditionInsertionConsumesFollowingWire(); testLogicLifecycleAndOrdering(); - testEdgeTimerNodesAndRungComments(); + testEdgeNodesAndRungComments(); testHistoryAndAtomicBatchDelete(); } catch (const std::exception &error) diff --git a/app/tests/offline_simulation_service_tests.cpp b/app/tests/offline_simulation_service_tests.cpp index 7d1c374..092e518 100644 --- a/app/tests/offline_simulation_service_tests.cpp +++ b/app/tests/offline_simulation_service_tests.cpp @@ -32,27 +32,6 @@ LogicNode edgeContact(const std::string &id, int address, EdgeMode mode) true}; } -LogicNode timerContact( - const std::string &id, - int timer_index, - ContactMode mode = ContactMode::NormallyOpen) -{ - return {id, TimerContactNodeConfig{TimerAddress{timer_index}, mode}, true}; -} - -LogicNode counterContact( - const std::string &id, - int counter_index, - ContactMode mode = ContactMode::NormallyOpen) -{ - return {id, CounterContactNodeConfig{CounterAddress{counter_index}, mode}, true}; -} - -LogicNode ton(const std::string &id, int timer_index, int preset_ms) -{ - return {id, TonNodeConfig{TimerAddress{timer_index}, preset_ms}, true}; -} - WordOperand constantOperand(std::int16_t value) { return { @@ -69,25 +48,6 @@ WordOperand registerOperand(int address) 0}; } -LogicNode counter( - const std::string &id, - int counter_index, - CounterMode mode, - int current_address, - WordOperand preset, - int reset_address) -{ - return { - id, - CounterNodeConfig{ - CounterAddress{counter_index}, - mode, - RegisterAddress{RegisterArea::D, current_address}, - preset, - RegisterAddress{RegisterArea::M, reset_address}}, - true}; -} - LogicNode move( const std::string &id, WordOperand source, @@ -479,221 +439,6 @@ void testEdgeContactsAreOneScanPulsesAndAreLogicScoped() "a restarted runtime must treat current ON inputs as fresh rising edges"); } -void testTonUsesElapsedTimeAndResetsAsNonRetentive() -{ - VirtualRegisterRepository repository; - SoftwareLogicExecutor executor; - const ControlLogic program = logic({ - rung("ton-rung", {{contact("ton-input", 0)}}, ton("ton-0", 0, 100)), - rung("ton-feedback-rung", {{timerContact("timer-done", 0)}}, - coil("ton-output", 30))}); - const auto start = SoftwareLogicExecutor::TimePoint{}; - LogicTraceSnapshot trace; - - require(executor.executeScanAt({program}, repository, start, &trace).succeeded, - "an inactive TON must scan successfully"); - require(!readBit(repository, 30) - && trace.tonValues.at("ton-0").elapsedMs == 0 - && !trace.tonValues.at("ton-0").done, - "an inactive TON must have Q false and ET zero"); - - writeBit(repository, 0, true); - require(executor.executeScanAt({program}, repository, start, &trace).succeeded, - "the first active TON scan must start timing"); - require(!readBit(repository, 30) && trace.tonValues.at("ton-0").elapsedMs == 0, - "TON must not complete on its starting scan"); - require(executor.executeScanAt( - {program}, repository, - start + std::chrono::milliseconds{99}, &trace) - .succeeded, - "TON must use elapsed monotonic time before the preset"); - require(!readBit(repository, 30) - && trace.tonValues.at("ton-0").elapsedMs == 99, - "TON must remain false before PT"); - require(executor.executeScanAt( - {program}, repository, - start + std::chrono::milliseconds{100}, &trace) - .succeeded, - "TON must complete at its preset time"); - require(readBit(repository, 30) - && trace.tonValues.at("ton-0").done, - "the T contact must observe TON Q in a later network of the same scan"); - - writeBit(repository, 0, false); - require(executor.executeScanAt( - {program}, repository, - start + std::chrono::milliseconds{150}, &trace) - .succeeded, - "disconnecting TON input must scan successfully"); - require(!readBit(repository, 30) - && trace.tonValues.at("ton-0").elapsedMs == 0 - && !trace.tonValues.at("ton-0").done, - "a non-retentive TON must reset ET and Q when input opens"); - - writeBit(repository, 0, true); - require(executor.executeScanAt( - {program}, repository, - start + std::chrono::milliseconds{200}, &trace) - .succeeded, - "TON must restart timing after an input reset"); - require(!readBit(repository, 30), "TON restart must not retain the old done state"); - - executor.resetRuntime(); - require(executor.executeScanAt( - {program}, repository, - start + std::chrono::milliseconds{1000}, &trace) - .succeeded, - "resetRuntime must clear TON state"); - require(!readBit(repository, 30), - "a restarted runtime must start a currently active TON from zero"); -} - -void testMultipleTimersAndNetworkOrder() -{ - VirtualRegisterRepository repository; - SoftwareLogicExecutor executor; - const ControlLogic independent = logic({ - rung("ton-1-rung", {{contact("input-1", 1)}}, ton("ton-1", 1, 100)), - rung("ton-2-rung", {{contact("input-2", 2)}}, ton("ton-2", 2, 200))}); - writeBit(repository, 1, true); - writeBit(repository, 2, true); - const auto start = SoftwareLogicExecutor::TimePoint{}; - LogicTraceSnapshot trace; - require(executor.executeScanAt({independent}, repository, start, &trace).succeeded, - "multiple TON resources must start independently"); - require(executor.executeScanAt( - {independent}, repository, - start + std::chrono::milliseconds{100}, &trace) - .succeeded, - "the first independent TON must reach PT without completing the second"); - require(trace.tonValues.at("ton-1").done - && !trace.tonValues.at("ton-2").done, - "different T resources must retain independent elapsed time"); - - VirtualRegisterRepository ordered_repository; - SoftwareLogicExecutor ordered_executor; - const ControlLogic ordered = logic({ - rung("feedback-before-ton", {{timerContact("early-timer", 4)}}, - coil("early-output", 40)), - rung("ton-after-feedback", {{contact("late-input", 4)}}, ton("ton-4", 4, 100))}); - writeBit(ordered_repository, 4, true); - require(ordered_executor.executeScanAt( - {ordered}, ordered_repository, start, nullptr) - .succeeded, - "network order test must start TON after its earlier T contact"); - require(!readBit(ordered_repository, 40), - "an earlier T contact must see the previous scan state"); - require(ordered_executor.executeScanAt( - {ordered}, ordered_repository, - start + std::chrono::milliseconds{100}, nullptr) - .succeeded, - "network order test must complete TON on the next scan"); - require(!readBit(ordered_repository, 40), - "the earlier T contact must remain false on the completion scan"); - require(ordered_executor.executeScanAt( - {ordered}, ordered_repository, - start + std::chrono::milliseconds{101}, nullptr) - .succeeded, - "network order test must expose TON Q on the following scan"); - require(readBit(ordered_repository, 40), - "a later scan must observe the completed TON through T contact"); -} - -void testCountersUseRisingEdgesAndExternalDValues() -{ - VirtualRegisterRepository repository; - SoftwareLogicExecutor executor; - const ControlLogic up_program = logic({ - rung( - "ctu-rung", - {{contact("count-input", 0)}}, - counter( - "ctu-0", - 0, - CounterMode::Up, - 0, - constantOperand(3), - 1)), - rung( - "ctu-done-rung", - {{counterContact("ctu-done", 0)}}, - coil("ctu-done-output", 2))}); - LogicTraceSnapshot trace; - - require(executor.executeScan({up_program}, repository, &trace).succeeded, - "an inactive CTU must scan successfully"); - require(readWord(repository, 0) == 0 && !readBit(repository, 2), - "an inactive CTU must keep CV zero and Q false"); - - writeBit(repository, 0, true); - require(executor.executeScan({up_program}, repository, &trace).succeeded, - "the first CTU rising edge must scan successfully"); - require(readWord(repository, 0) == 1, - "CTU must increment the external D current value on a rising edge"); - require(executor.executeScan({up_program}, repository, &trace).succeeded, - "a held CTU input must scan successfully"); - require(readWord(repository, 0) == 1, - "a held CTU input must not count every scan"); - - for (int expected = 2; expected <= 3; ++expected) - { - writeBit(repository, 0, false); - require(executor.executeScan({up_program}, repository).succeeded, - "CTU falling preparation scan must succeed"); - writeBit(repository, 0, true); - require(executor.executeScan({up_program}, repository, &trace).succeeded, - "CTU repeated rising edge must succeed"); - require(readWord(repository, 0) == expected, - "CTU must increment exactly once for each rising edge"); - } - require(readBit(repository, 2) - && trace.counterValues.at("ctu-0").done, - "the C contact must observe CTU completion in a later network"); - - writeBit(repository, 1, true); - require(executor.executeScan({up_program}, repository, &trace).succeeded, - "CTU reset must scan successfully"); - require(readWord(repository, 0) == 0 && !readBit(repository, 2), - "CTU reset must clear CV and the C completion state"); - - executor.resetRuntime(); - repository.clear(); - const ControlLogic down_program = logic({ - rung( - "ctd-rung", - {{contact("down-input", 3)}}, - counter( - "ctd-1", - 1, - CounterMode::Down, - 1, - constantOperand(2), - 4)), - rung( - "ctd-done-rung", - {{counterContact("ctd-done", 1)}}, - coil("ctd-done-output", 5))}); - writeBit(repository, 4, true); - require(executor.executeScan({down_program}, repository).succeeded, - "CTD load reset must scan successfully"); - require(readWord(repository, 1) == 2, - "CTD reset must load PV into the external D current value"); - writeBit(repository, 4, false); - for (int expected = 1; expected >= 0; --expected) - { - writeBit(repository, 3, false); - require(executor.executeScan({down_program}, repository).succeeded, - "CTD falling preparation scan must succeed"); - writeBit(repository, 3, true); - require(executor.executeScan({down_program}, repository).succeeded, - "CTD rising edge must scan successfully"); - require(readWord(repository, 1) == expected, - "CTD must decrement exactly once for each rising edge"); - } - require(readBit(repository, 5), - "CTD completion contact must turn on when CV reaches zero"); -} - void testMoveAndSaturatingArithmetic() { VirtualRegisterRepository repository; @@ -924,9 +669,6 @@ int main(int argc, char *argv[]) testSetResetAndDisabledLogic(); testMultipleLogicScanOrderAndTraceIsolation(); testEdgeContactsAreOneScanPulsesAndAreLogicScoped(); - testTonUsesElapsedTimeAndResetsAsNonRetentive(); - testMultipleTimersAndNetworkOrder(); - testCountersUseRisingEdgesAndExternalDValues(); testMoveAndSaturatingArithmetic(); testSetResetPairOnSameAddress(); testConflictingCoilsAreRejected(); diff --git a/app/tests/project_management_tests.cpp b/app/tests/project_management_tests.cpp index 865391c..b75dd5d 100644 --- a/app/tests/project_management_tests.cpp +++ b/app/tests/project_management_tests.cpp @@ -159,80 +159,17 @@ Project makeExampleProject() rising_edge.id = "rising-edge"; rising_edge.config = EdgeContactNodeConfig{ RegisterAddress{RegisterArea::M, 4}, EdgeMode::Rising}; - LogicNode ton_output; - ton_output.id = "ton-output"; - ton_output.config = TonNodeConfig{TimerAddress{7}, 2500}; - LadderRung ton_rung; - ton_rung.id = "ton-rung"; - ton_rung.name = "TON network"; - ton_rung.comment = "延时启动"; - ton_rung.condition = ConditionExpression::fromNode(rising_edge); - ton_rung.output = ton_output; - logic.rungs.push_back(ton_rung); - - LogicNode falling_edge; - falling_edge.id = "falling-edge"; - falling_edge.config = EdgeContactNodeConfig{ - RegisterAddress{RegisterArea::M, 5}, EdgeMode::Falling}; - LogicNode timer_contact; - timer_contact.id = "timer-contact"; - timer_contact.config = TimerContactNodeConfig{ - TimerAddress{7}, ContactMode::NormallyOpen}; - LogicNode timer_coil; - timer_coil.id = "timer-coil"; - timer_coil.config = CoilNodeConfig{ + LogicNode edge_output; + edge_output.id = "edge-output"; + edge_output.config = CoilNodeConfig{ RegisterAddress{RegisterArea::M, 6}, CoilMode::Normal}; - LadderRung timer_rung; - timer_rung.id = "timer-rung"; - timer_rung.name = "Timer feedback"; - timer_rung.comment = "定时器完成反馈"; - ConditionExpression timer_series; - timer_series.id = "timer-series"; - timer_series.kind = ConditionExpressionKind::Series; - timer_series.children = { - ConditionExpression::fromNode(falling_edge), - ConditionExpression::fromNode(timer_contact)}; - timer_rung.condition = std::move(timer_series); - timer_rung.output = timer_coil; - logic.rungs.push_back(timer_rung); - - LogicNode counter_input; - counter_input.id = "counter-input"; - counter_input.config = ContactNodeConfig{ - RegisterAddress{RegisterArea::M, 7}, ContactMode::NormallyOpen}; - LogicNode counter_output; - counter_output.id = "counter-output"; - counter_output.config = CounterNodeConfig{ - CounterAddress{3}, - CounterMode::Up, - RegisterAddress{RegisterArea::D, 10}, - WordOperand{ - WordOperandKind::Register, - RegisterAddress{RegisterArea::D, 11}, - 0}, - RegisterAddress{RegisterArea::M, 8}}; - LadderRung counter_rung; - counter_rung.id = "counter-rung"; - counter_rung.name = "Counter network"; - counter_rung.comment = "计数器上升沿计数"; - counter_rung.condition = ConditionExpression::fromNode(counter_input); - counter_rung.output = counter_output; - logic.rungs.push_back(counter_rung); - - LogicNode counter_contact; - counter_contact.id = "counter-contact"; - counter_contact.config = CounterContactNodeConfig{ - CounterAddress{3}, ContactMode::NormallyOpen}; - LogicNode counter_coil; - counter_coil.id = "counter-coil"; - counter_coil.config = CoilNodeConfig{ - RegisterAddress{RegisterArea::M, 9}, CoilMode::Normal}; - LadderRung counter_feedback_rung; - counter_feedback_rung.id = "counter-feedback-rung"; - counter_feedback_rung.name = "Counter feedback"; - counter_feedback_rung.condition = ConditionExpression::fromNode(counter_contact); - counter_feedback_rung.output = counter_coil; - logic.rungs.push_back(counter_feedback_rung); + LadderRung edge_rung; + edge_rung.id = "edge-rung"; + edge_rung.name = "Edge network"; + edge_rung.comment = "上升沿输出"; + edge_rung.condition = ConditionExpression::fromNode(rising_edge); + edge_rung.output = edge_output; + logic.rungs.push_back(edge_rung); const auto addDataRung = [&logic]( const std::string &rung_id, @@ -452,10 +389,6 @@ void testExampleProjectRoundTrip() && saved_json.contains("\"registerComments\"") && saved_json.contains("\"text\": \"启动按钮\"") && saved_json.contains("\"type\": \"edgeContact\"") - && saved_json.contains("\"type\": \"timerContact\"") - && saved_json.contains("\"type\": \"ton\"") - && saved_json.contains("\"type\": \"counterContact\"") - && saved_json.contains("\"type\": \"counter\"") && saved_json.contains("\"type\": \"move\"") && saved_json.contains("\"type\": \"arithmetic\"") && saved_json.contains("\"operation\": \"add\"") @@ -550,56 +483,26 @@ void testExampleProjectRoundTrip() require(rung.output.has_value(), "ladder output must survive round trip"); - const LadderRung &ton_rung = project.controlLogics.front().rungs.at(1); - require(ton_rung.output.has_value() - && std::holds_alternative(ton_rung.output->config) - && std::get(ton_rung.output->config).address - == TimerAddress{7} - && std::get(ton_rung.output->config).presetMs == 2500, - "TON timer address and preset must survive round trip"); + const LadderRung &edge_rung = project.controlLogics.front().rungs.at(1); + require(edge_rung.output.has_value() + && std::holds_alternative(edge_rung.output->config) + && std::get(edge_rung.output->config).address + == RegisterAddress{RegisterArea::M, 6}, + "edge network output must survive round trip"); require(std::holds_alternative( - ton_rung.condition->node->config) - && std::get(ton_rung.condition->node->config).mode + edge_rung.condition->node->config) + && std::get(edge_rung.condition->node->config).mode == EdgeMode::Rising, "rising edge configuration must survive round trip"); - const LadderRung &timer_rung = project.controlLogics.front().rungs.at(2); - require(timer_rung.comment == "定时器完成反馈" - && std::holds_alternative( - timer_rung.condition->children.at(1).node->config) - && std::get( - timer_rung.condition->children.at(1).node->config).address - == TimerAddress{7} - && std::holds_alternative( - timer_rung.condition->children.front().node->config) - && std::get( - timer_rung.condition->children.front().node->config).mode - == EdgeMode::Falling, - "falling edge and T contact configurations must survive round trip"); - - const LadderRung &counter_rung = project.controlLogics.front().rungs.at(3); - const auto &counter = std::get(counter_rung.output->config); - require(counter.address == CounterAddress{3} - && counter.currentValueAddress == RegisterAddress{RegisterArea::D, 10} - && counter.preset.kind == WordOperandKind::Register - && counter.preset.address == RegisterAddress{RegisterArea::D, 11} - && counter.resetAddress == RegisterAddress{RegisterArea::M, 8}, - "counter resource and external M/D addresses must survive round trip"); - const LadderRung &counter_feedback = project.controlLogics.front().rungs.at(4); - require(std::holds_alternative( - counter_feedback.condition->node->config) - && std::get( - counter_feedback.condition->node->config).address - == CounterAddress{3}, - "counter contacts must survive round trip"); require(std::get( - project.controlLogics.front().rungs.at(5).output->config) + project.controlLogics.front().rungs.at(2).output->config) .source.constant == 25, "MOVE operands must survive round trip"); require(std::get( - project.controlLogics.front().rungs.at(6).output->config) + project.controlLogics.front().rungs.at(3).output->config) .operation == ArithmeticOperation::Add && std::get( - project.controlLogics.front().rungs.at(7).output->config) + project.controlLogics.front().rungs.at(4).output->config) .operation == ArithmeticOperation::Subtract, "ADD and SUB operations must survive round trip"); diff --git a/app/tests/runtime_mode_service_tests.cpp b/app/tests/runtime_mode_service_tests.cpp index 5acd504..8282fca 100644 --- a/app/tests/runtime_mode_service_tests.cpp +++ b/app/tests/runtime_mode_service_tests.cpp @@ -143,54 +143,6 @@ void testModeTransitions() comparison_rung.condition = ConditionExpression::fromNode(comparison); comparison_rung.output = comparison_coil; logic.rungs.push_back(comparison_rung); - LogicNode timer_input; - timer_input.id = "poll-timer-input"; - timer_input.config = ContactNodeConfig{ - RegisterAddress{RegisterArea::M, 23}, ContactMode::NormallyOpen}; - LogicNode ton; - ton.id = "poll-ton"; - ton.config = TonNodeConfig{TimerAddress{4}, 100}; - LadderRung ton_rung; - ton_rung.id = "poll-ton-rung"; - ton_rung.name = "Poll TON"; - ton_rung.condition = ConditionExpression::fromNode(timer_input); - ton_rung.output = ton; - logic.rungs.push_back(ton_rung); - LogicNode timer_contact; - timer_contact.id = "poll-timer-contact"; - timer_contact.config = TimerContactNodeConfig{ - TimerAddress{4}, ContactMode::NormallyOpen}; - LogicNode timer_coil; - timer_coil.id = "poll-timer-coil"; - timer_coil.config = CoilNodeConfig{ - RegisterAddress{RegisterArea::M, 24}, CoilMode::Normal}; - LadderRung timer_rung; - timer_rung.id = "poll-timer-rung"; - timer_rung.name = "Poll timer contact"; - timer_rung.condition = ConditionExpression::fromNode(timer_contact); - timer_rung.output = timer_coil; - logic.rungs.push_back(timer_rung); - LogicNode counter_input; - counter_input.id = "poll-counter-input"; - counter_input.config = ContactNodeConfig{ - RegisterAddress{RegisterArea::M, 25}, ContactMode::NormallyOpen}; - LogicNode counter_output; - counter_output.id = "poll-counter"; - counter_output.config = CounterNodeConfig{ - CounterAddress{2}, - CounterMode::Up, - RegisterAddress{RegisterArea::D, 36}, - WordOperand{ - WordOperandKind::Register, - RegisterAddress{RegisterArea::D, 37}, - 0}, - RegisterAddress{RegisterArea::M, 26}}; - LadderRung counter_rung; - counter_rung.id = "poll-counter-rung"; - counter_rung.name = "Poll counter"; - counter_rung.condition = ConditionExpression::fromNode(counter_input); - counter_rung.output = counter_output; - logic.rungs.push_back(counter_rung); LogicNode move_input; move_input.id = "poll-move-input"; move_input.config = ContactNodeConfig{ @@ -264,14 +216,11 @@ void testModeTransitions() == std::vector({ {RegisterArea::M, 12}, {RegisterArea::M, 20}, {RegisterArea::M, 21}, {RegisterArea::M, 22}, - {RegisterArea::M, 23}, {RegisterArea::M, 24}, - {RegisterArea::M, 25}, {RegisterArea::M, 26}, {RegisterArea::M, 27}, {RegisterArea::M, 28}, {RegisterArea::D, 34}, {RegisterArea::D, 35}, - {RegisterArea::D, 36}, {RegisterArea::D, 37}, {RegisterArea::D, 38}, {RegisterArea::D, 39}, {RegisterArea::D, 40}, {RegisterArea::D, 41}}), - "all instruction M/D references must be polled while T/C resources and comments stay offline-only"); + "all instruction M/D references must be polled while comments stay metadata-only"); gateway.completeInitialRead(); require(service.initialPlcReadCompleted(), "service must retain the initial PLC read state");