|
- #include "control_logic_model.h"
-
- #include <algorithm>
-
- namespace {
-
- void setError(std::string *error, const std::string &message)
- {
- if (error != nullptr)
- {
- *error = message;
- }
- }
-
- bool validateConfig(const ContactNodeConfig &config, std::string *error)
- {
- if (!config.address.isValid() || config.address.area() != RegisterArea::M)
- {
- setError(error, "contact node requires a valid M address");
- return false;
- }
- if (config.mode != ContactMode::NormallyOpen
- && config.mode != ContactMode::NormallyClosed)
- {
- setError(error, "contact node has an unsupported mode");
- return false;
- }
- return true;
- }
-
- bool validateConfig(const CoilNodeConfig &config, std::string *error)
- {
- if (!config.address.isValid() || config.address.area() != RegisterArea::M)
- {
- setError(error, "coil node requires a valid M address");
- return false;
- }
- if (config.mode != CoilMode::Normal
- && config.mode != CoilMode::Set
- && config.mode != CoilMode::Reset)
- {
- setError(error, "coil node has an unsupported mode");
- return false;
- }
- return true;
- }
-
- bool validateConfig(const CompareNodeConfig &config, std::string *error)
- {
- if (!config.address.isValid() || config.address.area() != RegisterArea::D)
- {
- setError(error, "comparison node requires a valid D address");
- return false;
- }
- if (config.comparison != ComparisonOperator::Equal
- && config.comparison != ComparisonOperator::NotEqual
- && config.comparison != ComparisonOperator::LessThan
- && config.comparison != ComparisonOperator::LessThanOrEqual
- && config.comparison != ComparisonOperator::GreaterThan
- && config.comparison != ComparisonOperator::GreaterThanOrEqual)
- {
- setError(error, "comparison node has an unsupported operator");
- return false;
- }
- return true;
- }
-
- template<typename TItem>
- bool hasDuplicateId(const std::vector<TItem> &items)
- {
- for (auto current = items.cbegin(); current != items.cend(); ++current)
- {
- if (std::find_if(
- current + 1,
- items.cend(),
- [¤t](const TItem &candidate)
- {
- return candidate.id == current->id;
- }) != items.cend())
- {
- return true;
- }
- }
- return false;
- }
-
- } // namespace
-
- bool LogicNode::validate(std::string *error) const
- {
- if (id.empty())
- {
- setError(error, "logic node id must not be empty");
- return false;
- }
- return std::visit(
- [error](const auto &config)
- {
- return validateConfig(config, error);
- },
- config);
- }
-
- bool LogicNode::isConfigured() const
- {
- return configured;
- }
-
- bool LogicNode::isCondition() const
- {
- return !std::holds_alternative<CoilNodeConfig>(config);
- }
-
- bool LogicNode::isOutput() const
- {
- return std::holds_alternative<CoilNodeConfig>(config);
- }
-
- bool LadderStage::validate(std::string *error) const
- {
- if (id.empty() || branches.empty())
- {
- setError(error, "ladder stage id and branches must not be empty");
- return false;
- }
- if (hasDuplicateId(branches))
- {
- setError(error, "parallel branch node ids must be unique");
- return false;
- }
- for (const LogicNode &node : branches)
- {
- if (!node.validate(error))
- {
- return false;
- }
- if (!node.isCondition())
- {
- setError(error, "ladder stage may contain condition nodes only");
- return false;
- }
- }
- return true;
- }
-
- bool LadderStage::validateForRunning(std::string *error) const
- {
- if (!validate(error))
- {
- return false;
- }
- for (const LogicNode &node : branches)
- {
- if (!node.isConfigured())
- {
- setError(error, "ladder condition " + node.id + " is not configured");
- return false;
- }
- }
- return true;
- }
-
- bool LadderRung::validate(std::string *error) const
- {
- return validateStructure(error);
- }
-
- bool LadderRung::validateForRunning(std::string *error) const
- {
- if (!validateStructure(error))
- {
- return false;
- }
- if (stages.empty() && !output.has_value())
- {
- return true;
- }
- if (stages.empty() || !output.has_value())
- {
- setError(error, "incomplete ladder network requires conditions and an output coil");
- return false;
- }
- for (const LadderStage &stage : stages)
- {
- if (!stage.validateForRunning(error))
- {
- return false;
- }
- }
- if (!output->isConfigured())
- {
- setError(error, "output coil " + output->id + " is not configured");
- return false;
- }
- return true;
- }
-
- bool LadderRung::validateStructure(std::string *error) const
- {
- if (id.empty() || name.empty())
- {
- setError(error, "ladder rung id and name must not be empty");
- return false;
- }
- if (hasDuplicateId(stages))
- {
- setError(error, "ladder stage ids must be unique within a rung");
- return false;
- }
- std::vector<std::string> node_ids;
- for (const LadderStage &stage : stages)
- {
- if (!stage.validate(error))
- {
- return false;
- }
- for (const LogicNode &node : stage.branches)
- {
- node_ids.push_back(node.id);
- }
- }
- if (output.has_value())
- {
- if (!output->validate(error))
- {
- return false;
- }
- if (!output->isOutput())
- {
- setError(error, "ladder rung output must be a coil node");
- return false;
- }
- node_ids.push_back(output->id);
- }
- std::sort(node_ids.begin(), node_ids.end());
- if (std::adjacent_find(node_ids.cbegin(), node_ids.cend()) != node_ids.cend())
- {
- setError(error, "logic node ids must be unique within a rung");
- return false;
- }
- return true;
- }
-
- bool ControlLogic::validate(std::string *error) const
- {
- if (!validateStructure(error))
- {
- return false;
- }
- for (const LadderRung &rung : rungs)
- {
- if (!rung.validate(error))
- {
- return false;
- }
- }
- return true;
- }
-
- bool ControlLogic::validateStructure(std::string *error) const
- {
- if (id.empty() || name.empty())
- {
- setError(error, "control logic id and name must not be empty");
- return false;
- }
- if (hasDuplicateId(rungs))
- {
- setError(error, "ladder rung ids must be unique within a logic");
- return false;
- }
- std::vector<std::string> node_ids;
- for (const LadderRung &rung : rungs)
- {
- if (!rung.validateStructure(error))
- {
- return false;
- }
- for (const LadderStage &stage : rung.stages)
- {
- for (const LogicNode &node : stage.branches)
- {
- node_ids.push_back(node.id);
- }
- }
- if (rung.output.has_value())
- {
- node_ids.push_back(rung.output->id);
- }
- }
- std::sort(node_ids.begin(), node_ids.end());
- if (std::adjacent_find(node_ids.cbegin(), node_ids.cend()) != node_ids.cend())
- {
- setError(error, "logic node ids must be unique within a logic");
- return false;
- }
- return true;
- }
-
- bool ControlLogic::validateForRunning(std::string *error) const
- {
- if (!validateStructure(error))
- {
- return false;
- }
- for (const LadderRung &rung : rungs)
- {
- if (!rung.validateForRunning(error))
- {
- return false;
- }
- }
- return true;
- }
|