|
- #include "software_logic_executor.h"
-
- #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;
- }
- }
- }
-
- } // namespace
-
- 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.validateForRunning(&validation_error))
- {
- return failure(
- LogicScanError::InvalidLogic,
- validation_error,
- logic.id);
- }
- if (!logic.enabled)
- {
- continue;
- }
- 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)
- {
- return failure(
- LogicScanError::InvalidLogic,
- "ladder output is not a coil",
- logic.id,
- rung.id,
- rung.output->id);
- }
- const auto existing = output_modes.find(output->address.index());
- if (existing != output_modes.end() && existing->second != output->mode)
- {
- return failure(
- LogicScanError::ConflictingOutput,
- "mixed coil modes target " + output->address.toString(),
- logic.id,
- rung.id,
- rung.output->id);
- }
- output_modes[output->address.index()] = output->mode;
- }
- }
- return success();
- }
-
- LogicScanResult SoftwareLogicExecutor::executeScan(
- const std::vector<ControlLogic> &logics,
- RegisterRepository &repository) const
- {
- const LogicScanResult validation = validate(logics);
- if (!validation.succeeded)
- {
- return validation;
- }
-
- for (const ControlLogic &logic : logics)
- {
- if (!logic.enabled)
- {
- continue;
- }
- for (const LadderRung &rung : logic.rungs)
- {
- if (rung.stages.empty() && !rung.output.has_value())
- {
- continue;
- }
-
- bool rung_value = true;
- for (const LadderStage &stage : rung.stages)
- {
- bool stage_value = false;
- for (const LogicNode &node : stage.branches)
- {
- bool condition_value = false;
- LogicScanResult result = evaluateCondition(
- node, repository, &condition_value);
- if (!result.succeeded)
- {
- result.logicId = logic.id;
- result.rungId = rung.id;
- return result;
- }
- stage_value = stage_value || condition_value;
- }
- rung_value = rung_value && stage_value;
- }
-
- LogicScanResult result = writeOutput(
- *rung.output, rung_value, repository);
- if (!result.succeeded)
- {
- result.logicId = logic.id;
- result.rungId = rung.id;
- return result;
- }
- }
- }
- return success();
- }
-
- LogicScanResult SoftwareLogicExecutor::evaluateCondition(
- const LogicNode &node,
- RegisterRepository &repository,
- bool *value) const
- {
- if (value == nullptr)
- {
- return failure(
- LogicScanError::InvalidLogic,
- "condition result target is missing",
- {},
- {},
- node.id);
- }
-
- return std::visit(
- [&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,
- "failed to read " + config.address.toString(),
- {},
- {},
- node.id);
- }
- *value = config.mode == ContactMode::NormallyOpen
- ? read.value : !read.value;
- 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,
- "failed to read " + config.address.toString(),
- {},
- {},
- node.id);
- }
- *value = compareWord(read.value, config.comparison, config.value);
- return success();
- }
- else
- {
- return failure(
- LogicScanError::InvalidLogic,
- "condition node contains an output coil",
- {},
- {},
- node.id);
- }
- },
- node.config);
- }
-
- LogicScanResult SoftwareLogicExecutor::writeOutput(
- const LogicNode &node,
- bool rung_value,
- RegisterRepository &repository) const
- {
- const auto *config = std::get_if<CoilNodeConfig>(&node.config);
- if (config == nullptr)
- {
- return failure(
- LogicScanError::InvalidLogic,
- "ladder output is not a coil",
- {},
- {},
- node.id);
- }
-
- bool should_write = true;
- bool output_value = rung_value;
- switch (config->mode)
- {
- case CoilMode::Normal:
- {
- break;
- }
- case CoilMode::Set:
- {
- should_write = rung_value;
- output_value = true;
- break;
- }
- case CoilMode::Reset:
- {
- should_write = rung_value;
- output_value = false;
- break;
- }
- default:
- {
- return failure(
- LogicScanError::InvalidLogic,
- "unsupported coil mode",
- {},
- {},
- node.id);
- }
- }
-
- if (!should_write)
- {
- return success();
- }
- const RegisterWriteResult write = repository.writeBit(
- config->address, output_value);
- if (!write.succeeded)
- {
- return failure(
- LogicScanError::RegisterWriteFailed,
- "failed to write " + config->address.toString(),
- {},
- {},
- node.id);
- }
- return success();
- }
|