#include "software_logic_executor.h" #include #include #include #include namespace { LogicScanResult success() { return {true, LogicScanError::None, {}, {}, {}, {}}; } LogicScanResult failure( LogicScanError error, const std::string &message, const std::string &logic_id = {}, const std::string &rung_id = {}, const std::string &node_id = {}) { return {false, error, message, logic_id, rung_id, node_id}; } bool compareWord( std::int16_t actual, ComparisonOperator comparison, std::int16_t expected) { switch (comparison) { case ComparisonOperator::Equal: { return actual == expected; } case ComparisonOperator::NotEqual: { return actual != expected; } case ComparisonOperator::LessThan: { return actual < expected; } case ComparisonOperator::LessThanOrEqual: { return actual <= expected; } case ComparisonOperator::GreaterThan: { return actual > expected; } case ComparisonOperator::GreaterThanOrEqual: { return actual >= expected; } default: { return false; } } } bool coilModesAreCompatible(CoilMode existing, CoilMode current) { if (existing == current) { return true; } return existing != CoilMode::Normal && current != CoilMode::Normal; } } // namespace void LogicTraceValues::clear() { nodeValues.clear(); expressionValues.clear(); rungValues.clear(); tonValues.clear(); counterValues.clear(); wordValues.clear(); } void LogicTraceSnapshot::clear() { LogicTraceValues::clear(); logicValues.clear(); } LogicTraceSnapshot LogicTraceSnapshot::forLogic( const std::string &logic_id) const { LogicTraceSnapshot projection; const auto values = logicValues.find(logic_id); if (values != logicValues.cend()) { projection.nodeValues = values->second.nodeValues; projection.expressionValues = values->second.expressionValues; projection.rungValues = values->second.rungValues; projection.tonValues = values->second.tonValues; projection.counterValues = values->second.counterValues; projection.wordValues = values->second.wordValues; } return projection; } LogicScanResult SoftwareLogicExecutor::validate( const std::vector &logics) const { std::map output_modes; for (const ControlLogic &logic : logics) { std::string validation_error; if (!logic.validateStructure(&validation_error)) { return failure( LogicScanError::InvalidLogic, validation_error, logic.id); } if (!logic.enabled) { continue; } if (!logic.validateForRunning(&validation_error)) { return failure( LogicScanError::InvalidLogic, validation_error, logic.id); } for (const LadderRung &rung : logic.rungs) { if (!rung.output.has_value()) { continue; } 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) || std::holds_alternative( rung.output->config)) { continue; } return failure( LogicScanError::InvalidLogic, "梯形图输出不是有效的输出指令", logic.id, rung.id, rung.output->id); } const auto existing = output_modes.find(output->address.index()); if (existing != output_modes.end() && !coilModesAreCompatible(existing->second, output->mode)) { return failure( LogicScanError::ConflictingOutput, "同一地址使用了不同线圈模式:" + output->address.toString(), logic.id, rung.id, rung.output->id); } 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) { return validation; } if (trace != nullptr) { trace->clear(); } for (const ControlLogic &logic : logics) { if (!logic.enabled) { continue; } LogicTraceValues *logic_trace = trace == nullptr ? nullptr : &trace->logicValues[logic.id]; for (const LadderRung &rung : logic.rungs) { if (!rung.condition.has_value() && !rung.output.has_value()) { continue; } bool rung_value = false; LogicScanResult result = evaluateExpression( logic.id, *rung.condition, repository, logic_trace, &rung_value); if (!result.succeeded) { result.logicId = logic.id; result.rungId = rung.id; return result; } if (logic_trace != nullptr) { logic_trace->rungValues[rung.id] = rung_value; logic_trace->nodeValues[rung.output->id] = rung_value; } bool output_value = false; result = executeOutput( logic.id, *rung.output, rung_value, repository, now, logic_trace, &output_value); if (!result.succeeded) { result.logicId = logic.id; result.rungId = rung.id; return result; } if (logic_trace != nullptr) { logic_trace->nodeValues[rung.output->id] = output_value; } } } if (trace != nullptr) { const auto first_enabled = std::find_if( logics.cbegin(), logics.cend(), [](const ControlLogic &logic) { return logic.enabled; }); if (first_enabled != logics.cend()) { const auto values = trace->logicValues.find(first_enabled->id); if (values != trace->logicValues.cend()) { trace->nodeValues = values->second.nodeValues; trace->expressionValues = values->second.expressionValues; trace->rungValues = values->second.rungValues; trace->tonValues = values->second.tonValues; trace->counterValues = values->second.counterValues; trace->wordValues = values->second.wordValues; } } } return success(); } LogicScanResult SoftwareLogicExecutor::evaluateExpression( const std::string &logic_id, const ConditionExpression &expression, RegisterRepository &repository, LogicTraceValues *trace, bool *value) { if (value == nullptr) { return failure(LogicScanError::InvalidLogic, "缺少表达式结果接收对象"); } if (expression.kind == ConditionExpressionKind::Node) { LogicScanResult result = evaluateCondition( logic_id, *expression.node, repository, value); if (result.succeeded && trace != nullptr) { trace->nodeValues[expression.node->id] = *value; trace->expressionValues[expression.id] = *value; } return result; } bool accumulated = expression.kind == ConditionExpressionKind::Series; for (const ConditionExpression &child : expression.children) { bool child_value = false; LogicScanResult result = evaluateExpression( logic_id, child, repository, trace, &child_value); if (!result.succeeded) { return result; } accumulated = expression.kind == ConditionExpressionKind::Series ? accumulated && child_value : accumulated || child_value; } *value = accumulated; if (trace != nullptr) { trace->expressionValues[expression.id] = accumulated; } return success(); } LogicScanResult SoftwareLogicExecutor::evaluateCondition( const std::string &logic_id, const LogicNode &node, RegisterRepository &repository, bool *value) { if (value == nullptr) { return failure( LogicScanError::InvalidLogic, "缺少条件结果接收对象", {}, {}, node.id); } return std::visit( [this, &logic_id, &repository, value, &node]( const auto &config) -> LogicScanResult { using Config = std::decay_t; if constexpr (std::is_same_v) { const BitReadResult read = repository.readBit(config.address); if (!read.succeeded) { return failure( LogicScanError::RegisterReadFailed, "读取寄存器失败:" + config.address.toString(), {}, {}, node.id); } *value = config.mode == ContactMode::NormallyOpen ? read.value : !read.value; return success(); } else if constexpr (std::is_same_v) { const BitReadResult read = repository.readBit(config.address); if (!read.succeeded) { return failure( LogicScanError::RegisterReadFailed, "读取寄存器失败:" + config.address.toString(), {}, {}, node.id); } const auto runtime_key = std::make_pair(logic_id, node.id); const bool previous = previous_edge_inputs_[runtime_key]; *value = config.mode == EdgeMode::Rising ? read.value && !previous : !read.value && previous; previous_edge_inputs_[runtime_key] = read.value; return success(); } else if constexpr (std::is_same_v) { 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) { 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) { const WordReadResult read = repository.readWord(config.address); if (!read.succeeded) { return failure( LogicScanError::RegisterReadFailed, "读取寄存器失败:" + config.address.toString(), {}, {}, node.id); } *value = compareWord(read.value, config.comparison, config.value); return success(); } else { return failure( LogicScanError::InvalidLogic, "条件节点不能包含输出节点", {}, {}, node.id); } }, node.config); } LogicScanResult SoftwareLogicExecutor::readWordOperand( const WordOperand &operand, RegisterRepository &repository, std::int16_t *value, const std::string &node_id) const { if (value == nullptr) { return failure( LogicScanError::InvalidLogic, "缺少字操作数结果接收对象", {}, {}, node_id); } if (operand.kind == WordOperandKind::Constant) { *value = operand.constant; return success(); } if (operand.kind != WordOperandKind::Register) { return failure( LogicScanError::InvalidLogic, "字操作数类型无效", {}, {}, node_id); } const WordReadResult read = repository.readWord(operand.address); if (!read.succeeded) { return failure( LogicScanError::RegisterReadFailed, "读取寄存器失败:" + operand.address.toString(), {}, {}, node_id); } *value = read.value; return success(); } LogicScanResult SoftwareLogicExecutor::executeOutput( const std::string &logic_id, const LogicNode &node, bool rung_value, RegisterRepository &repository, TimePoint now, LogicTraceValues *trace, bool *output_value) { if (output_value == nullptr) { return failure( LogicScanError::InvalidLogic, "缺少输出指令结果接收对象", {}, {}, 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) { *output_value = false; return success(); } std::int16_t source = 0; LogicScanResult result = readWordOperand( move->source, repository, &source, node.id); if (!result.succeeded) { return result; } const RegisterWriteResult write = repository.writeWord( move->destination, source); if (!write.succeeded) { return failure( LogicScanError::RegisterWriteFailed, "MOVE 写入寄存器失败:" + move->destination.toString(), {}, {}, node.id); } *output_value = true; if (trace != nullptr) { trace->wordValues[node.id] = {source, false}; } return success(); } if (const auto *arithmetic = std::get_if(&node.config)) { if (!rung_value) { *output_value = false; return success(); } std::int16_t left = 0; std::int16_t right = 0; LogicScanResult result = readWordOperand( arithmetic->left, repository, &left, node.id); if (!result.succeeded) { return result; } result = readWordOperand(arithmetic->right, repository, &right, node.id); if (!result.succeeded) { return result; } const std::int32_t raw = arithmetic->operation == ArithmeticOperation::Add ? static_cast(left) + right : static_cast(left) - right; const bool overflow = raw < std::numeric_limits::min() || raw > std::numeric_limits::max(); const std::int32_t bounded = std::max( std::numeric_limits::min(), std::min(raw, std::numeric_limits::max())); const std::int16_t result_value = static_cast(bounded); const RegisterWriteResult write = repository.writeWord( arithmetic->destination, result_value); if (!write.succeeded) { return failure( LogicScanError::RegisterWriteFailed, "算术指令写入寄存器失败:" + arithmetic->destination.toString(), {}, {}, node.id); } *output_value = true; if (trace != nullptr) { trace->wordValues[node.id] = {result_value, overflow}; } return success(); } const auto *config = std::get_if(&node.config); if (config == nullptr) { return failure( LogicScanError::InvalidLogic, "梯形图输出不是有效的输出指令", {}, {}, node.id); } bool should_write = true; bool register_value = rung_value; switch (config->mode) { case CoilMode::Normal: { break; } case CoilMode::Set: { should_write = rung_value; register_value = true; break; } case CoilMode::Reset: { should_write = rung_value; register_value = false; break; } default: { return failure( LogicScanError::InvalidLogic, "不支持的线圈模式", {}, {}, node.id); } } if (!should_write) { *output_value = rung_value; return success(); } const RegisterWriteResult write = repository.writeBit( config->address, register_value); if (!write.succeeded) { return failure( LogicScanError::RegisterWriteFailed, "写入寄存器失败:" + config->address.toString(), {}, {}, node.id); } *output_value = rung_value; return success(); }