|
- #include "logic_editor_service.h"
-
- #include "project_service.h"
- #include "domain/project_limits.h"
-
- #include <algorithm>
- #include <cctype>
- #include <cstddef>
- #include <iterator>
- #include <unordered_set>
- #include <type_traits>
- #include <utility>
-
- namespace {
-
- bool isBlank(const std::string &value)
- {
- return value.empty()
- || std::all_of(
- value.cbegin(), value.cend(),
- [](unsigned char character) { return std::isspace(character) != 0; });
- }
-
- std::string makeUniqueLogicId(const Project &project)
- {
- int suffix = 1;
- while (true)
- {
- const std::string candidate = "logic-" + std::to_string(suffix);
- const bool found = std::any_of(
- project.controlLogics.cbegin(), project.controlLogics.cend(),
- [&candidate](const ControlLogic &logic) { return logic.id == candidate; });
- if (!found)
- {
- return candidate;
- }
- ++suffix;
- }
- }
-
- LogicNode makeNode(const std::string &id, const LogicNodeConfig &config)
- {
- return {id, config, false};
- }
-
- ConditionExpression makeContainer(
- const std::string &id,
- ConditionExpressionKind kind,
- ConditionExpression first,
- ConditionExpression second)
- {
- ConditionExpression expression;
- expression.id = id;
- expression.kind = kind;
- expression.children.push_back(std::move(first));
- expression.children.push_back(std::move(second));
- return expression;
- }
-
- ConditionExpression *findParentExpression(
- ConditionExpression &expression, const std::string &child_id)
- {
- for (ConditionExpression &child : expression.children)
- {
- if (child.id == child_id)
- {
- return &expression;
- }
- if (ConditionExpression *parent = findParentExpression(child, child_id))
- {
- return parent;
- }
- }
- return nullptr;
- }
-
- using NodeIdSet = std::unordered_set<std::string>;
-
- NodeIdSet conditionLeafIds(const ConditionExpression &expression)
- {
- NodeIdSet ids;
- if (expression.kind == ConditionExpressionKind::Node
- || expression.kind == ConditionExpressionKind::Wire)
- {
- ids.insert(expression.id);
- return ids;
- }
- for (const ConditionExpression &child : expression.children)
- {
- NodeIdSet child_ids = conditionLeafIds(child);
- ids.insert(child_ids.cbegin(), child_ids.cend());
- }
- return ids;
- }
-
- int expressionColumns(const ConditionExpression &expression)
- {
- if (expression.kind == ConditionExpressionKind::Node)
- {
- return 1;
- }
- if (expression.kind == ConditionExpressionKind::Wire)
- {
- return expression.wire->columnSpan;
- }
- int columns = expression.kind == ConditionExpressionKind::Series ? 0 : 1;
- for (const ConditionExpression &child : expression.children)
- {
- const int child_columns = expressionColumns(child);
- columns = expression.kind == ConditionExpressionKind::Series
- ? columns + child_columns : std::max(columns, child_columns);
- }
- return columns;
- }
-
- bool isSubset(const NodeIdSet &subset, const NodeIdSet &values)
- {
- return std::all_of(
- subset.cbegin(), subset.cend(),
- [&values](const std::string &value) { return values.count(value) != 0U; });
- }
-
- bool sameValues(const NodeIdSet &left, const NodeIdSet &right)
- {
- return left.size() == right.size() && isSubset(left, right);
- }
-
- void addParallelSibling(
- ConditionExpression *target,
- ConditionExpression branch,
- const std::string &container_id)
- {
- if (target->kind == ConditionExpressionKind::Parallel)
- {
- target->children.push_back(std::move(branch));
- return;
- }
- ConditionExpression original = std::move(*target);
- *target = makeContainer(
- container_id,
- ConditionExpressionKind::Parallel,
- std::move(original),
- std::move(branch));
- }
-
- bool addParallelForSelection(
- ConditionExpression *expression,
- const NodeIdSet &selected_ids,
- ConditionExpression *branch,
- const std::string ¶llel_id,
- const std::string &series_id)
- {
- const NodeIdSet expression_ids = conditionLeafIds(*expression);
- if (sameValues(expression_ids, selected_ids))
- {
- addParallelSibling(expression, std::move(*branch), parallel_id);
- return true;
- }
-
- std::vector<NodeIdSet> child_ids;
- child_ids.reserve(expression->children.size());
- std::vector<std::size_t> matching_children;
- for (std::size_t index = 0; index < expression->children.size(); ++index)
- {
- child_ids.push_back(conditionLeafIds(expression->children[index]));
- const bool intersects = std::any_of(
- child_ids.back().cbegin(), child_ids.back().cend(),
- [&selected_ids](const std::string &id)
- {
- return selected_ids.count(id) != 0U;
- });
- if (intersects)
- {
- matching_children.push_back(index);
- }
- }
- if (matching_children.empty())
- {
- return false;
- }
- if (matching_children.size() == 1U)
- {
- const std::size_t child_index = matching_children.front();
- if (expression->kind == ConditionExpressionKind::Parallel
- && sameValues(child_ids[child_index], selected_ids))
- {
- expression->children.push_back(std::move(*branch));
- return true;
- }
- return addParallelForSelection(
- &expression->children[child_index],
- selected_ids,
- branch,
- parallel_id,
- series_id);
- }
- if (expression->kind != ConditionExpressionKind::Series)
- {
- return false;
- }
-
- const std::size_t first = matching_children.front();
- const std::size_t last = matching_children.back();
- if (last - first + 1U != matching_children.size())
- {
- return false;
- }
- NodeIdSet range_ids;
- for (std::size_t index = first; index <= last; ++index)
- {
- range_ids.insert(child_ids[index].cbegin(), child_ids[index].cend());
- }
- if (!sameValues(range_ids, selected_ids))
- {
- return false;
- }
-
- ConditionExpression range;
- range.id = series_id;
- range.kind = ConditionExpressionKind::Series;
- auto range_begin = expression->children.begin() + static_cast<std::ptrdiff_t>(first);
- auto range_end = expression->children.begin() + static_cast<std::ptrdiff_t>(last + 1U);
- range.children.insert(
- range.children.end(),
- std::make_move_iterator(range_begin),
- std::make_move_iterator(range_end));
- range_begin = expression->children.erase(range_begin, range_end);
-
- ConditionExpression parallel = makeContainer(
- parallel_id,
- ConditionExpressionKind::Parallel,
- std::move(range),
- std::move(*branch));
- expression->children.insert(range_begin, std::move(parallel));
- return true;
- }
-
- bool removeExpressionRecursive(
- ConditionExpression *expression, const std::string &expression_id)
- {
- if (expression == nullptr
- || expression->kind == ConditionExpressionKind::Node
- || expression->kind == ConditionExpressionKind::Wire)
- {
- return false;
- }
- const auto removable = std::find_if(
- expression->children.begin(),
- expression->children.end(),
- [&expression_id](const ConditionExpression &child)
- {
- return child.id == expression_id;
- });
- if (removable != expression->children.end())
- {
- expression->children.erase(removable);
- return true;
- }
- for (ConditionExpression &child : expression->children)
- {
- if (removeExpressionRecursive(&child, expression_id))
- {
- return true;
- }
- }
- return false;
- }
-
- std::optional<int> selectedExpressionColumns(
- const ConditionExpression &expression,
- const NodeIdSet &selected_ids)
- {
- const NodeIdSet expression_ids = conditionLeafIds(expression);
- if (sameValues(expression_ids, selected_ids))
- {
- return expressionColumns(expression);
- }
- if (expression.kind == ConditionExpressionKind::Node
- || expression.kind == ConditionExpressionKind::Wire)
- {
- return std::nullopt;
- }
-
- std::vector<NodeIdSet> child_ids;
- std::vector<std::size_t> matching_children;
- child_ids.reserve(expression.children.size());
- for (std::size_t index = 0; index < expression.children.size(); ++index)
- {
- child_ids.push_back(conditionLeafIds(expression.children[index]));
- const bool intersects = std::any_of(
- child_ids.back().cbegin(), child_ids.back().cend(),
- [&selected_ids](const std::string &id)
- {
- return selected_ids.count(id) != 0U;
- });
- if (intersects)
- {
- matching_children.push_back(index);
- }
- }
- if (matching_children.size() == 1U)
- {
- return selectedExpressionColumns(
- expression.children[matching_children.front()], selected_ids);
- }
- if (matching_children.empty()
- || expression.kind != ConditionExpressionKind::Series)
- {
- return std::nullopt;
- }
- const std::size_t first = matching_children.front();
- const std::size_t last = matching_children.back();
- if (last - first + 1U != matching_children.size())
- {
- return std::nullopt;
- }
- NodeIdSet range_ids;
- int columns = 0;
- for (std::size_t index = first; index <= last; ++index)
- {
- range_ids.insert(child_ids[index].cbegin(), child_ids[index].cend());
- columns += expressionColumns(expression.children[index]);
- }
- return sameValues(range_ids, selected_ids)
- ? std::optional<int>{columns} : std::nullopt;
- }
-
- LadderRung *findEditableRung(
- Project &project,
- const std::string &logic_id,
- const std::string &rung_id)
- {
- const auto logic = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- if (logic == project.controlLogics.end())
- {
- return nullptr;
- }
- const auto rung = std::find_if(
- logic->rungs.begin(), logic->rungs.end(),
- [&rung_id](const LadderRung &candidate) { return candidate.id == rung_id; });
- return rung == logic->rungs.end() ? nullptr : &*rung;
- }
-
- } // namespace
-
- LogicEditorService::LogicEditorService(ProjectService &project_service)
- : project_service_(project_service)
- {
- }
-
- LogicEditorService::HistoryState LogicEditorService::captureState() const
- {
- return {project_service_.project().controlLogics};
- }
-
- void LogicEditorService::recordHistory(HistoryState before)
- {
- const HistoryState after = captureState();
- history_.record(std::move(before), after, &LogicEditorService::statesEqual);
- }
-
- bool LogicEditorService::statesEqual(
- const HistoryState &left, const HistoryState &right)
- {
- if (left.logics.size() != right.logics.size())
- {
- return false;
- }
- for (std::size_t index = 0; index < left.logics.size(); ++index)
- {
- if (!logicsEqual(left.logics[index], right.logics[index]))
- {
- return false;
- }
- }
- return true;
- }
-
- bool LogicEditorService::logicsEqual(
- const ControlLogic &left, const ControlLogic &right)
- {
- if (left.id != right.id || left.name != right.name
- || left.enabled != right.enabled || left.rungs.size() != right.rungs.size())
- {
- return false;
- }
- for (std::size_t index = 0; index < left.rungs.size(); ++index)
- {
- if (!rungsEqual(left.rungs[index], right.rungs[index]))
- {
- return false;
- }
- }
- return true;
- }
-
- bool LogicEditorService::rungsEqual(
- const LadderRung &left, const LadderRung &right)
- {
- if (left.id != right.id || left.name != right.name
- || left.comment != right.comment
- || left.condition.has_value() != right.condition.has_value()
- || left.output.has_value() != right.output.has_value())
- {
- return false;
- }
- return (!left.condition.has_value()
- || expressionsEqual(*left.condition, *right.condition))
- && (!left.output.has_value() || nodesEqual(*left.output, *right.output));
- }
-
- bool LogicEditorService::expressionsEqual(
- const ConditionExpression &left, const ConditionExpression &right)
- {
- if (left.id != right.id || left.kind != right.kind
- || left.node.has_value() != right.node.has_value()
- || left.wire.has_value() != right.wire.has_value()
- || left.children.size() != right.children.size())
- {
- return false;
- }
- if (left.node.has_value() && !nodesEqual(*left.node, *right.node))
- {
- return false;
- }
- if (left.wire.has_value()
- && left.wire->columnSpan != right.wire->columnSpan)
- {
- return false;
- }
- for (std::size_t index = 0; index < left.children.size(); ++index)
- {
- if (!expressionsEqual(left.children[index], right.children[index]))
- {
- return false;
- }
- }
- return true;
- }
-
- bool LogicEditorService::nodesEqual(
- const LogicNode &left, const LogicNode &right)
- {
- return left.id == right.id && left.config.index() == right.config.index()
- && left.configured == right.configured && configsEqual(left.config, right.config);
- }
-
- bool LogicEditorService::configsEqual(
- const LogicNodeConfig &left, const LogicNodeConfig &right)
- {
- return std::visit(
- [](const auto &left_config, const auto &right_config)
- {
- using Left = std::decay_t<decltype(left_config)>;
- using Right = std::decay_t<decltype(right_config)>;
- if constexpr (!std::is_same_v<Left, Right>)
- {
- return false;
- }
- else if constexpr (std::is_same_v<Left, ContactNodeConfig>)
- {
- return left_config.address == right_config.address
- && left_config.mode == right_config.mode;
- }
- else if constexpr (std::is_same_v<Left, EdgeContactNodeConfig>)
- {
- return left_config.address == right_config.address
- && left_config.mode == right_config.mode;
- }
- else if constexpr (std::is_same_v<Left, TimerContactNodeConfig>)
- {
- return left_config.address == right_config.address
- && left_config.mode == right_config.mode;
- }
- else if constexpr (std::is_same_v<Left, CounterContactNodeConfig>)
- {
- return left_config.address == right_config.address
- && left_config.mode == right_config.mode;
- }
- else if constexpr (std::is_same_v<Left, CoilNodeConfig>)
- {
- return left_config.address == right_config.address
- && left_config.mode == right_config.mode;
- }
- else if constexpr (std::is_same_v<Left, CompareNodeConfig>)
- {
- return left_config.address == right_config.address
- && left_config.comparison == right_config.comparison
- && left_config.value == right_config.value;
- }
- else if constexpr (std::is_same_v<Left, TonNodeConfig>)
- {
- return left_config.address == right_config.address
- && left_config.presetMs == right_config.presetMs;
- }
- else if constexpr (std::is_same_v<Left, CounterNodeConfig>)
- {
- 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<Left, MoveNodeConfig>)
- {
- return left_config.source.kind == right_config.source.kind
- && left_config.source.address == right_config.source.address
- && left_config.source.constant == right_config.source.constant
- && left_config.destination == right_config.destination;
- }
- else
- {
- return left_config.operation == right_config.operation
- && left_config.left.kind == right_config.left.kind
- && left_config.left.address == right_config.left.address
- && left_config.left.constant == right_config.left.constant
- && left_config.right.kind == right_config.right.kind
- && left_config.right.address == right_config.right.address
- && left_config.right.constant == right_config.right.constant
- && left_config.destination == right_config.destination;
- }
- },
- left,
- right);
- }
-
- LogicEditorResult LogicEditorService::historyFailure(const std::string &message)
- {
- return {false, LogicEditorError::InvalidOperation, message, {}};
- }
-
- const ControlLogic *LogicEditorService::findLogic(const std::string &logic_id) const
- {
- const auto &logics = project_service_.project().controlLogics;
- const auto logic = std::find_if(
- logics.cbegin(), logics.cend(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- return logic == logics.cend() ? nullptr : &*logic;
- }
-
- const LadderRung *LogicEditorService::findRung(
- const std::string &logic_id, const std::string &rung_id) const
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr)
- {
- return nullptr;
- }
- const auto rung = std::find_if(
- logic->rungs.cbegin(), logic->rungs.cend(),
- [&rung_id](const LadderRung &candidate) { return candidate.id == rung_id; });
- return rung == logic->rungs.cend() ? nullptr : &*rung;
- }
-
- const LogicNode *LogicEditorService::findNode(
- const std::string &logic_id, const std::string &node_id) const
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr)
- {
- return nullptr;
- }
- for (const LadderRung &rung : logic->rungs)
- {
- if (rung.output.has_value() && rung.output->id == node_id)
- {
- return &*rung.output;
- }
- if (rung.condition.has_value())
- {
- if (const LogicNode *node = findConditionNode(*rung.condition, node_id))
- {
- return node;
- }
- }
- }
- return nullptr;
- }
-
- const ConditionExpression *LogicEditorService::findExpression(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &expression_id) const
- {
- const LadderRung *rung = findRung(logic_id, rung_id);
- return rung == nullptr || !rung->condition.has_value()
- ? nullptr : findConditionExpression(*rung->condition, expression_id);
- }
-
- std::string LogicEditorService::firstLogicId() const
- {
- const auto &logics = project_service_.project().controlLogics;
- return logics.empty() ? std::string{} : logics.front().id;
- }
-
- std::string LogicEditorService::firstRungId(const std::string &logic_id) const
- {
- const ControlLogic *logic = findLogic(logic_id);
- return logic == nullptr || logic->rungs.empty() ? std::string{} : logic->rungs.front().id;
- }
-
- std::string LogicEditorService::rungIdForNode(
- const std::string &logic_id, const std::string &node_id) const
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr)
- {
- return {};
- }
- for (const LadderRung &rung : logic->rungs)
- {
- if ((rung.output.has_value() && rung.output->id == node_id)
- || (rung.condition.has_value()
- && findConditionNode(*rung.condition, node_id) != nullptr))
- {
- return rung.id;
- }
- }
- return {};
- }
-
- std::string LogicEditorService::registerCommentFor(
- const RegisterAddress &address) const
- {
- const RegisterComment *comment = project_service_.project().findRegisterComment(address);
- return comment == nullptr ? std::string{} : comment->text;
- }
-
- LogicEditorResult LogicEditorService::ensureDefaultLogic()
- {
- if (!project_service_.project().controlLogics.empty())
- {
- return {true, LogicEditorError::None, {}, firstLogicId()};
- }
- ControlLogic logic;
- logic.id = "logic-1";
- logic.name = "控制逻辑 1";
- logic.rungs.push_back({"rung-1", "网络 1", {}, std::nullopt, std::nullopt});
- Project &project = project_service_.editProject();
- project.controlLogics.push_back(std::move(logic));
- return {true, LogicEditorError::None, {}, project.controlLogics.back().id};
- }
-
- LogicEditorResult LogicEditorService::addLogic(const std::string &name)
- {
- if (isBlank(name))
- {
- return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能为空");
- }
- const Project ¤t = project_service_.project();
- if (current.controlLogics.size() >= ProjectLimits::kMaximumControlLogics)
- {
- return failure(LogicEditorError::InvalidOperation, "单个工程最多包含 128 组控制逻辑");
- }
- if (name.size() > ProjectLimits::kMaximumTextBytes)
- {
- return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能超过 4096 个 UTF-8 字节");
- }
- const bool duplicate = std::any_of(
- current.controlLogics.cbegin(), current.controlLogics.cend(),
- [&name](const ControlLogic &logic) { return logic.name == name; });
- if (duplicate)
- {
- return failure(LogicEditorError::DuplicateName, "控制逻辑名称必须唯一");
- }
-
- ControlLogic logic;
- logic.id = makeUniqueLogicId(current);
- logic.name = name;
- logic.rungs.push_back({"rung-1", "网络 1", {}, std::nullopt, std::nullopt});
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- project.controlLogics.push_back(std::move(logic));
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, project.controlLogics.back().id};
- }
-
- LogicEditorResult LogicEditorService::renameLogic(
- const std::string &logic_id, const std::string &name)
- {
- if (isBlank(name))
- {
- return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能为空");
- }
- if (name.size() > ProjectLimits::kMaximumTextBytes)
- {
- return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能超过 4096 个 UTF-8 字节");
- }
- const Project ¤t = project_service_.project();
- const auto logic = std::find_if(
- current.controlLogics.cbegin(), current.controlLogics.cend(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- if (logic == current.controlLogics.cend())
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- const bool duplicate = std::any_of(
- current.controlLogics.cbegin(), current.controlLogics.cend(),
- [&logic_id, &name](const ControlLogic &candidate)
- {
- return candidate.id != logic_id && candidate.name == name;
- });
- if (duplicate)
- {
- return failure(LogicEditorError::DuplicateName, "控制逻辑名称必须唯一");
- }
-
- if (logic->name == name)
- {
- return {true, LogicEditorError::None, {}, logic_id};
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- auto editable = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- editable->name = name;
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, logic_id};
- }
-
- LogicEditorResult LogicEditorService::removeLogic(const std::string &logic_id)
- {
- const Project ¤t = project_service_.project();
- const auto logic = std::find_if(
- current.controlLogics.cbegin(), current.controlLogics.cend(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- if (logic == current.controlLogics.cend())
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- if (current.controlLogics.size() <= 1)
- {
- return failure(LogicEditorError::LastLogicRequired, "工程至少需要保留一个控制逻辑");
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- project.controlLogics.erase(
- std::remove_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; }),
- project.controlLogics.end());
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, logic_id};
- }
-
- LogicEditorResult LogicEditorService::moveLogic(const std::string &logic_id, int offset)
- {
- if (offset != -1 && offset != 1)
- {
- return failure(LogicEditorError::InvalidOperation, "控制逻辑每次只能上移或下移一位");
- }
- const Project ¤t = project_service_.project();
- const auto logic = std::find_if(
- current.controlLogics.cbegin(), current.controlLogics.cend(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- if (logic == current.controlLogics.cend())
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- const auto index = std::distance(current.controlLogics.cbegin(), logic);
- const std::ptrdiff_t target_index = index + offset;
- if (target_index < 0
- || target_index
- >= static_cast<std::ptrdiff_t>(current.controlLogics.size()))
- {
- return failure(LogicEditorError::InvalidOperation, "控制逻辑已经位于目标边界");
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- std::iter_swap(
- project.controlLogics.begin() + index,
- project.controlLogics.begin() + target_index);
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, logic_id};
- }
-
- LogicEditorResult LogicEditorService::setLogicEnabled(
- const std::string &logic_id, bool enabled)
- {
- if (findLogic(logic_id) == nullptr)
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- const ControlLogic *existing = findLogic(logic_id);
- if (existing->enabled == enabled)
- {
- return {true, LogicEditorError::None, {}, logic_id};
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- auto logic = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- logic->enabled = enabled;
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, logic_id};
- }
-
- LogicEditorResult LogicEditorService::addRung(const std::string &logic_id)
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr)
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- if (logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
- {
- return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
- }
- LadderRung rung;
- rung.id = makeUniqueRungId(*logic);
- rung.name = "网络 " + std::to_string(logic->rungs.size() + 1U);
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- auto target = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- target->rungs.push_back(std::move(rung));
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, target->rungs.back().id};
- }
-
- LogicEditorResult LogicEditorService::removeRung(
- const std::string &logic_id, const std::string &rung_id)
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr)
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- if (findRung(logic_id, rung_id) == nullptr)
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
- }
- if (logic->rungs.size() == 1U)
- {
- return failure(LogicEditorError::InvalidOperation, "控制逻辑至少需要保留一个网络");
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- auto target = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- target->rungs.erase(
- std::remove_if(
- target->rungs.begin(), target->rungs.end(),
- [&rung_id](const LadderRung &rung) { return rung.id == rung_id; }),
- target->rungs.end());
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, rung_id};
- }
-
- LogicEditorResult LogicEditorService::updateRungComment(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &comment)
- {
- const LadderRung *rung = findRung(logic_id, rung_id);
- if (rung == nullptr)
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
- }
- if (comment.size() > ProjectLimits::kMaximumTextBytes)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "梯形图网络注释不能超过 4096 个 UTF-8 字节");
- }
- if (rung->comment == comment)
- {
- return {true, LogicEditorError::None, {}, rung_id};
- }
- HistoryState before = captureState();
- LadderRung &editable = *findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- editable.comment = comment;
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, rung_id};
- }
-
- LogicEditorResult LogicEditorService::appendCondition(
- const std::string &logic_id,
- const std::string &rung_id,
- const LogicNodeConfig &config)
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
- {
- return failure(
- logic == nullptr ? LogicEditorError::LogicNotFound : LogicEditorError::RungNotFound,
- logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
- }
- if (!isConditionConfig(config))
- {
- return failure(LogicEditorError::InvalidNode, "梯形图条件不能使用线圈节点");
- }
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- LadderRung *rung = findEditableRung(project, logic_id, rung_id);
- if (!rung->condition.has_value())
- {
- rung->condition = std::move(leaf);
- }
- else if (rung->condition->kind == ConditionExpressionKind::Series)
- {
- rung->condition->children.push_back(std::move(leaf));
- }
- else
- {
- rung->condition = makeContainer(
- makeUniqueExpressionId(*logic),
- ConditionExpressionKind::Series,
- std::move(*rung->condition),
- std::move(leaf));
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- project.controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::appendWire(
- const std::string &logic_id,
- const std::string &rung_id,
- int column_span)
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
- {
- return failure(
- logic == nullptr ? LogicEditorError::LogicNotFound
- : LogicEditorError::RungNotFound,
- logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
- }
- WireSegment wire{column_span};
- std::string error;
- if (!wire.validate(&error))
- {
- return failure(LogicEditorError::InvalidOperation, error);
- }
- const std::string wire_id = makeUniqueWireId(*logic);
- ConditionExpression leaf = ConditionExpression::fromWire(wire_id, column_span);
- HistoryState before = captureState();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- if (!rung->condition.has_value())
- {
- rung->condition = std::move(leaf);
- }
- else if (rung->condition->kind == ConditionExpressionKind::Series)
- {
- rung->condition->children.push_back(std::move(leaf));
- }
- else
- {
- rung->condition = makeContainer(
- makeUniqueExpressionId(*logic),
- ConditionExpressionKind::Series,
- std::move(*rung->condition),
- std::move(leaf));
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- project_service_.editProject().controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, wire_id};
- }
-
- LogicEditorResult LogicEditorService::insertConditionAfter(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &target_node_id,
- const LogicNodeConfig &config)
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
- }
- const LadderRung *existing_rung = findRung(logic_id, rung_id);
- if (!isConditionConfig(config)
- || existing_rung == nullptr
- || !existing_rung->condition.has_value()
- || findConditionNode(*existing_rung->condition, target_node_id) == nullptr)
- {
- return failure(LogicEditorError::NodeNotFound, "未找到串联插入目标");
- }
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- LadderRung *rung = findEditableRung(project, logic_id, rung_id);
- ConditionExpression *target = findConditionExpression(*rung->condition, target_node_id);
- ConditionExpression *parent = findParentExpression(*rung->condition, target_node_id);
- if (parent != nullptr && parent->kind == ConditionExpressionKind::Series)
- {
- const auto target_iterator = std::find_if(
- parent->children.begin(), parent->children.end(),
- [&target_node_id](const ConditionExpression &child)
- {
- return child.id == target_node_id;
- });
- parent->children.insert(target_iterator + 1, std::move(leaf));
- }
- else
- {
- ConditionExpression original = std::move(*target);
- *target = makeContainer(
- makeUniqueExpressionId(*logic),
- ConditionExpressionKind::Series,
- std::move(original),
- std::move(leaf));
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- project.controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::insertWireAfter(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &target_expression_id,
- int column_span)
- {
- const ControlLogic *logic = findLogic(logic_id);
- const LadderRung *existing_rung = findRung(logic_id, rung_id);
- const ConditionExpression *target = findExpression(
- logic_id, rung_id, target_expression_id);
- WireSegment wire{column_span};
- std::string error;
- if (logic == nullptr || existing_rung == nullptr || target == nullptr)
- {
- return failure(LogicEditorError::ExpressionNotFound, "未找到横线插入位置");
- }
- if ((target->kind != ConditionExpressionKind::Node
- && target->kind != ConditionExpressionKind::Wire)
- || !wire.validate(&error))
- {
- return failure(
- LogicEditorError::InvalidOperation,
- error.empty() ? "只能在触点或横线后插入横线" : error);
- }
- const std::string wire_id = makeUniqueWireId(*logic);
- ConditionExpression leaf = ConditionExpression::fromWire(wire_id, column_span);
- HistoryState before = captureState();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- ConditionExpression *editable_target = findConditionExpression(
- *rung->condition, target_expression_id);
- ConditionExpression *parent = findParentExpression(
- *rung->condition, target_expression_id);
- if (parent != nullptr && parent->kind == ConditionExpressionKind::Series)
- {
- const auto target_iterator = std::find_if(
- parent->children.begin(), parent->children.end(),
- [&target_expression_id](const ConditionExpression &child)
- {
- return child.id == target_expression_id;
- });
- parent->children.insert(target_iterator + 1, std::move(leaf));
- }
- else
- {
- ConditionExpression original = std::move(*editable_target);
- *editable_target = makeContainer(
- makeUniqueExpressionId(*logic),
- ConditionExpressionKind::Series,
- std::move(original),
- std::move(leaf));
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- project_service_.editProject().controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, wire_id};
- }
-
- LogicEditorResult LogicEditorService::replaceWireWithCondition(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &wire_expression_id,
- const LogicNodeConfig &config)
- {
- const ControlLogic *logic = findLogic(logic_id);
- const ConditionExpression *wire = findExpression(
- logic_id, rung_id, wire_expression_id);
- if (logic == nullptr || wire == nullptr
- || wire->kind != ConditionExpressionKind::Wire)
- {
- return failure(LogicEditorError::ExpressionNotFound, "未找到要替换的横线");
- }
- if (!isConditionConfig(config))
- {
- return failure(LogicEditorError::InvalidNode, "横线只能替换为条件节点");
- }
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- HistoryState before = captureState();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- ConditionExpression *editable = findConditionExpression(
- *rung->condition, wire_expression_id);
- *editable = ConditionExpression::fromNode(makeNode(node_id, config));
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- project_service_.editProject().controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidNode, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::addParallelBranch(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::vector<std::string> &selected_node_ids,
- const LogicNodeConfig &config)
- {
- const ControlLogic *logic = findLogic(logic_id);
- if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
- }
- if (!isConditionConfig(config) || selected_node_ids.empty())
- {
- return failure(LogicEditorError::InvalidOperation, "建立并联支路前必须选择节点");
- }
- const LadderRung *existing_rung = findRung(logic_id, rung_id);
- if (!existing_rung->condition.has_value())
- {
- return failure(LogicEditorError::InvalidOperation, "梯形图网络尚无条件节点");
- }
- NodeIdSet selected_ids(selected_node_ids.cbegin(), selected_node_ids.cend());
- if (selected_ids.size() != selected_node_ids.size())
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "并联选择中存在重复节点");
- }
- const NodeIdSet available_ids = conditionLeafIds(*existing_rung->condition);
- if (!isSubset(selected_ids, available_ids))
- {
- return failure(
- LogicEditorError::NodeNotFound,
- "并联选择中包含未知节点");
- }
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
- const std::string parallel_id = makeUniqueExpressionId(*logic);
- std::string series_id = parallel_id + "-range";
- while (findExpression(logic_id, rung_id, series_id) != nullptr)
- {
- series_id += "-range";
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- LadderRung *rung = findEditableRung(project, logic_id, rung_id);
- if (!addParallelForSelection(
- &*rung->condition,
- selected_ids,
- &leaf,
- parallel_id,
- series_id))
- {
- project.controlLogics = before.logics;
- return failure(
- LogicEditorError::InvalidOperation,
- "并联选择必须是一个连续的逻辑范围");
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- project.controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::addParallelWireBranch(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::vector<std::string> &selected_expression_ids)
- {
- const ControlLogic *logic = findLogic(logic_id);
- const LadderRung *existing_rung = findRung(logic_id, rung_id);
- if (logic == nullptr || existing_rung == nullptr
- || !existing_rung->condition.has_value())
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图条件网络");
- }
- if (selected_expression_ids.empty())
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "建立竖线连接前必须选择连续的触点或横线");
- }
- NodeIdSet selected_ids(
- selected_expression_ids.cbegin(), selected_expression_ids.cend());
- if (selected_ids.size() != selected_expression_ids.size())
- {
- return failure(LogicEditorError::InvalidOperation, "并联选择中存在重复对象");
- }
- const NodeIdSet available_ids = conditionLeafIds(*existing_rung->condition);
- if (!isSubset(selected_ids, available_ids))
- {
- return failure(LogicEditorError::ExpressionNotFound, "并联选择中包含未知对象");
- }
- const std::optional<int> column_span = selectedExpressionColumns(
- *existing_rung->condition, selected_ids);
- if (!column_span.has_value()
- || *column_span > WireSegment::kMaximumColumnSpan)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线连接只能围绕同一支路中的连续逻辑范围");
- }
-
- const std::string wire_id = makeUniqueWireId(*logic);
- ConditionExpression wire = ConditionExpression::fromWire(
- wire_id, *column_span);
- const std::string parallel_id = makeUniqueExpressionId(*logic);
- std::string series_id = parallel_id + "-range";
- while (findExpression(logic_id, rung_id, series_id) != nullptr)
- {
- series_id += "-range";
- }
- HistoryState before = captureState();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- if (!addParallelForSelection(
- &*rung->condition,
- selected_ids,
- &wire,
- parallel_id,
- series_id))
- {
- project_service_.editProject().controlLogics = before.logics;
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线连接只能围绕同一支路中的连续逻辑范围");
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- project_service_.editProject().controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, wire_id};
- }
-
- LogicEditorResult LogicEditorService::setOutput(
- const std::string &logic_id,
- const std::string &rung_id,
- const LogicNodeConfig &config,
- bool configured)
- {
- const ControlLogic *logic = findLogic(logic_id);
- const LadderRung *existing_rung = findRung(logic_id, rung_id);
- if (logic == nullptr || existing_rung == nullptr || !isOutputConfig(config))
- {
- return failure(
- LogicEditorError::InvalidNode,
- "梯形图输出必须使用有效的输出指令");
- }
- const std::string node_id = existing_rung->output.has_value()
- ? existing_rung->output->id : makeUniqueNodeId(*logic, nodePrefix(config));
- LogicNode node = makeNode(node_id, config);
- node.configured = configured;
- std::string validation_error;
- if (!node.validate(&validation_error))
- {
- return failure(LogicEditorError::InvalidNode, validation_error);
- }
- if (existing_rung->output.has_value()
- && nodesEqual(*existing_rung->output, node))
- {
- return {true, LogicEditorError::None, {}, node_id};
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- findEditableRung(project, logic_id, rung_id)->output = std::move(node);
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::updateNodeConfig(
- const std::string &logic_id,
- const std::string &node_id,
- const LogicNodeConfig &config)
- {
- const LogicNode *node = findNode(logic_id, node_id);
- if (node == nullptr)
- {
- return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
- }
- if (node->config.index() != config.index())
- {
- return failure(
- LogicEditorError::UnsupportedNodeChange,
- "节点创建后不能修改节点类别");
- }
- LogicNode candidate{node_id, config, true};
- std::string error;
- if (!candidate.validate(&error))
- {
- return failure(LogicEditorError::InvalidNode, error);
- }
- if (nodesEqual(*node, candidate))
- {
- return {true, LogicEditorError::None, {}, node_id};
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- for (ControlLogic &logic : project.controlLogics)
- {
- if (logic.id != logic_id)
- {
- continue;
- }
- for (LadderRung &rung : logic.rungs)
- {
- if (rung.output.has_value() && rung.output->id == node_id)
- {
- *rung.output = candidate;
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
- if (rung.condition.has_value())
- {
- if (LogicNode *editable = findConditionNode(*rung.condition, node_id))
- {
- *editable = candidate;
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
- }
- }
- }
- return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
- }
-
- LogicEditorResult LogicEditorService::removeNode(
- const std::string &logic_id, const std::string &node_id)
- {
- return removeNodes(logic_id, {node_id});
- }
-
- LogicEditorResult LogicEditorService::removeNodes(
- const std::string &logic_id,
- const std::vector<std::string> &node_ids)
- {
- if (findLogic(logic_id) == nullptr)
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- if (node_ids.empty())
- {
- return failure(LogicEditorError::InvalidOperation, "请先选择要删除的逻辑节点");
- }
- std::unordered_set<std::string> selected_ids;
- for (const std::string &node_id : node_ids)
- {
- if (!selected_ids.insert(node_id).second)
- {
- return failure(LogicEditorError::InvalidOperation, "删除列表中存在重复节点");
- }
- if (findNode(logic_id, node_id) == nullptr)
- {
- return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
- }
- }
-
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- ControlLogic *editable_logic = nullptr;
- for (ControlLogic &candidate : project.controlLogics)
- {
- if (candidate.id == logic_id)
- {
- editable_logic = &candidate;
- break;
- }
- }
- for (LadderRung &rung : editable_logic->rungs)
- {
- if (rung.output.has_value()
- && selected_ids.find(rung.output->id) != selected_ids.end())
- {
- rung.output.reset();
- }
- if (rung.condition.has_value())
- {
- for (const std::string &node_id : node_ids)
- {
- if (rung.condition->kind == ConditionExpressionKind::Node
- && rung.condition->id == node_id)
- {
- rung.condition.reset();
- break;
- }
- removeExpressionRecursive(&*rung.condition, node_id);
- }
- if (rung.condition.has_value())
- {
- normalizeConditionExpression(&rung.condition);
- }
- }
- }
- for (const LadderRung &rung : editable_logic->rungs)
- {
- std::string validation_error;
- if (!rung.validate(&validation_error))
- {
- project.controlLogics = std::move(before.logics);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_ids.front()};
- }
-
- LogicEditorResult LogicEditorService::removeExpression(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &expression_id)
- {
- if (findExpression(logic_id, rung_id, expression_id) == nullptr)
- {
- return failure(LogicEditorError::ExpressionNotFound, "未找到条件支路");
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- LadderRung *rung = findEditableRung(project, logic_id, rung_id);
- if (rung->condition->id == expression_id)
- {
- rung->condition.reset();
- }
- else
- {
- removeExpressionRecursive(&*rung->condition, expression_id);
- normalizeConditionExpression(&rung->condition);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, expression_id};
- }
-
- LogicEditorResult LogicEditorService::removeExpressions(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::vector<std::string> &expression_ids)
- {
- const LadderRung *existing_rung = findRung(logic_id, rung_id);
- if (existing_rung == nullptr || !existing_rung->condition.has_value())
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图条件网络");
- }
- if (expression_ids.empty())
- {
- return failure(LogicEditorError::InvalidOperation, "请先选择要删除的条件或横线");
- }
- NodeIdSet unique_ids;
- for (const std::string &expression_id : expression_ids)
- {
- const ConditionExpression *expression = findExpression(
- logic_id, rung_id, expression_id);
- if (!unique_ids.insert(expression_id).second)
- {
- return failure(LogicEditorError::InvalidOperation, "删除列表中存在重复对象");
- }
- if (expression == nullptr)
- {
- return failure(
- LogicEditorError::ExpressionNotFound,
- "删除列表中包含未知条件表达式");
- }
- }
-
- HistoryState before = captureState();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- for (const std::string &expression_id : expression_ids)
- {
- if (!rung->condition.has_value())
- {
- break;
- }
- if (rung->condition->id == expression_id)
- {
- rung->condition.reset();
- break;
- }
- removeExpressionRecursive(&*rung->condition, expression_id);
- }
- normalizeConditionExpression(&rung->condition);
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, expression_ids.front()};
- }
-
- bool LogicEditorService::canUndo() const
- {
- return history_.canUndo();
- }
-
- bool LogicEditorService::canRedo() const
- {
- return history_.canRedo();
- }
-
- LogicEditorResult LogicEditorService::undo()
- {
- const std::optional<HistoryState> target = history_.undo(captureState());
- if (!target.has_value())
- {
- return historyFailure("没有可撤销的梯形图编辑操作");
- }
- project_service_.editProject().controlLogics = target->logics;
- return {true, LogicEditorError::None, {}, {}};
- }
-
- LogicEditorResult LogicEditorService::redo()
- {
- const std::optional<HistoryState> target = history_.redo(captureState());
- if (!target.has_value())
- {
- return historyFailure("没有可重做的梯形图编辑操作");
- }
- project_service_.editProject().controlLogics = target->logics;
- return {true, LogicEditorError::None, {}, {}};
- }
-
- void LogicEditorService::clearHistory()
- {
- history_.clear();
- }
-
- bool LogicEditorService::isConditionConfig(const LogicNodeConfig &config)
- {
- return std::holds_alternative<ContactNodeConfig>(config)
- || std::holds_alternative<EdgeContactNodeConfig>(config)
- || std::holds_alternative<TimerContactNodeConfig>(config)
- || std::holds_alternative<CounterContactNodeConfig>(config)
- || std::holds_alternative<CompareNodeConfig>(config);
- }
-
- bool LogicEditorService::isOutputConfig(const LogicNodeConfig &config)
- {
- return std::holds_alternative<CoilNodeConfig>(config)
- || std::holds_alternative<TonNodeConfig>(config)
- || std::holds_alternative<CounterNodeConfig>(config)
- || std::holds_alternative<MoveNodeConfig>(config)
- || std::holds_alternative<ArithmeticNodeConfig>(config);
- }
-
- std::string LogicEditorService::nodePrefix(const LogicNodeConfig &config)
- {
- return std::visit(
- [](const auto &value) -> std::string
- {
- using Config = std::decay_t<decltype(value)>;
- if constexpr (std::is_same_v<Config, ContactNodeConfig>)
- {
- return "contact";
- }
- else if constexpr (std::is_same_v<Config, EdgeContactNodeConfig>)
- {
- return "edge";
- }
- else if constexpr (std::is_same_v<Config, TimerContactNodeConfig>)
- {
- return "timer-contact";
- }
- else if constexpr (std::is_same_v<Config, CounterContactNodeConfig>)
- {
- return "counter-contact";
- }
- else if constexpr (std::is_same_v<Config, CoilNodeConfig>)
- {
- return "coil";
- }
- else if constexpr (std::is_same_v<Config, TonNodeConfig>)
- {
- return "ton";
- }
- else if constexpr (std::is_same_v<Config, CounterNodeConfig>)
- {
- return "counter";
- }
- else if constexpr (std::is_same_v<Config, MoveNodeConfig>)
- {
- return "move";
- }
- else if constexpr (std::is_same_v<Config, ArithmeticNodeConfig>)
- {
- return "arithmetic";
- }
- else
- {
- return "compare";
- }
- },
- config);
- }
-
- std::string LogicEditorService::makeUniqueNodeId(
- const ControlLogic &logic, const std::string &prefix)
- {
- for (std::size_t index = 1;; ++index)
- {
- const std::string candidate = prefix + '-' + std::to_string(index);
- bool found = false;
- for (const LadderRung &rung : logic.rungs)
- {
- found = found || (rung.output.has_value() && rung.output->id == candidate)
- || (rung.condition.has_value()
- && findConditionNode(*rung.condition, candidate) != nullptr);
- }
- if (!found)
- {
- return candidate;
- }
- }
- }
-
- std::string LogicEditorService::makeUniqueWireId(const ControlLogic &logic)
- {
- for (std::size_t index = 1;; ++index)
- {
- const std::string candidate = "wire-" + std::to_string(index);
- const bool found = std::any_of(
- logic.rungs.cbegin(), logic.rungs.cend(),
- [&candidate](const LadderRung &rung)
- {
- return rung.condition.has_value()
- && findConditionExpression(*rung.condition, candidate) != nullptr;
- });
- if (!found)
- {
- return candidate;
- }
- }
- }
-
- std::string LogicEditorService::makeUniqueExpressionId(const ControlLogic &logic)
- {
- for (std::size_t index = 1;; ++index)
- {
- const std::string candidate = "expression-" + std::to_string(index);
- bool found = false;
- for (const LadderRung &rung : logic.rungs)
- {
- found = found || (rung.condition.has_value()
- && findConditionExpression(*rung.condition, candidate) != nullptr);
- }
- if (!found)
- {
- return candidate;
- }
- }
- }
-
- std::string LogicEditorService::makeUniqueRungId(const ControlLogic &logic)
- {
- for (std::size_t index = 1;; ++index)
- {
- const std::string candidate = "rung-" + std::to_string(index);
- if (std::none_of(
- logic.rungs.cbegin(), logic.rungs.cend(),
- [&candidate](const LadderRung &rung) { return rung.id == candidate; }))
- {
- return candidate;
- }
- }
- }
-
- LogicEditorResult LogicEditorService::failure(
- LogicEditorError error, const std::string &message)
- {
- return {false, error, message, {}};
- }
|