|
- #include "software_logic_executor.h"
-
- #include <algorithm>
- #include <limits>
- #include <map>
- #include <type_traits>
-
- 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();
- nodePowerValues.clear();
- expressionValues.clear();
- expressionInputValues.clear();
- expressionPowerValues.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.nodePowerValues = values->second.nodePowerValues;
- projection.expressionValues = values->second.expressionValues;
- 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;
- }
-
- LogicScanResult SoftwareLogicExecutor::validate(
- const std::vector<ControlLogic> &logics) const
- {
- std::map<int, CoilMode> 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<CoilNodeConfig>(&rung.output->config);
- if (output == nullptr)
- {
- if (std::holds_alternative<TonNodeConfig>(rung.output->config)
- || std::holds_alternative<CounterNodeConfig>(rung.output->config)
- || std::holds_alternative<MoveNodeConfig>(rung.output->config)
- || std::holds_alternative<ArithmeticNodeConfig>(
- 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<ControlLogic> &logics,
- RegisterRepository &repository,
- LogicTraceSnapshot *trace)
- {
- return executeScanAt(logics, repository, Clock::now(), trace);
- }
-
- LogicScanResult SoftwareLogicExecutor::executeScanAt(
- const std::vector<ControlLogic> &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,
- true,
- &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;
- logic_trace->nodePowerValues[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;
- logic_trace->nodePowerValues[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->nodePowerValues = values->second.nodePowerValues;
- trace->expressionValues = values->second.expressionValues;
- 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;
- }
- }
- }
- return success();
- }
-
- LogicScanResult SoftwareLogicExecutor::evaluateExpression(
- const std::string &logic_id,
- const ConditionExpression &expression,
- RegisterRepository &repository,
- LogicTraceValues *trace,
- bool input_power,
- bool *value)
- {
- if (value == nullptr)
- {
- return failure(LogicScanError::InvalidLogic, "缺少表达式结果接收对象");
- }
- if (trace != nullptr)
- {
- trace->expressionInputValues[expression.id] = input_power;
- }
- if (expression.kind == ConditionExpressionKind::Wire)
- {
- *value = true;
- if (trace != nullptr)
- {
- trace->expressionValues[expression.id] = true;
- trace->expressionPowerValues[expression.id] = input_power;
- }
- return success();
- }
- 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->nodePowerValues[expression.node->id] = input_power && *value;
- trace->expressionValues[expression.id] = *value;
- trace->expressionPowerValues[expression.id] = input_power && *value;
- }
- return result;
- }
-
- bool accumulated = expression.kind == ConditionExpressionKind::Series;
- bool power = input_power;
- for (const ConditionExpression &child : expression.children)
- {
- bool child_value = false;
- const bool child_input = expression.kind == ConditionExpressionKind::Series
- ? power : input_power;
- LogicScanResult result = evaluateExpression(
- logic_id,
- child,
- repository,
- trace,
- child_input,
- &child_value);
- if (!result.succeeded)
- {
- return result;
- }
- accumulated = expression.kind == ConditionExpressionKind::Series
- ? accumulated && child_value : accumulated || child_value;
- if (expression.kind == ConditionExpressionKind::Series)
- {
- power = power && child_value;
- }
- }
- *value = accumulated;
- if (trace != nullptr)
- {
- trace->expressionValues[expression.id] = accumulated;
- trace->expressionPowerValues[expression.id] = input_power && 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<decltype(config)>;
- if constexpr (std::is_same_v<Config, ContactNodeConfig>)
- {
- 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<Config, EdgeContactNodeConfig>)
- {
- 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<Config, TimerContactNodeConfig>)
- {
- 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<Config, CounterContactNodeConfig>)
- {
- 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<Config, CompareNodeConfig>)
- {
- 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<TonNodeConfig>(&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<std::chrono::milliseconds>(
- 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<CounterNodeConfig>(&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<std::int32_t>(0, preset);
- preset = static_cast<std::int16_t>(
- std::min<std::int32_t>(bounded_preset, std::numeric_limits<std::int16_t>::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<std::int32_t>(0, current_read.value);
- current = std::min<std::int32_t>(current, std::numeric_limits<std::int16_t>::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<std::int16_t>(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<MoveNodeConfig>(&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<ArithmeticNodeConfig>(&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<std::int32_t>(left) + right
- : static_cast<std::int32_t>(left) - right;
- const bool overflow = raw < std::numeric_limits<std::int16_t>::min()
- || raw > std::numeric_limits<std::int16_t>::max();
- const std::int32_t bounded = std::max<std::int32_t>(
- std::numeric_limits<std::int16_t>::min(),
- std::min<std::int32_t>(raw, std::numeric_limits<std::int16_t>::max()));
- const std::int16_t result_value = static_cast<std::int16_t>(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<CoilNodeConfig>(&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();
- }
|