|
- #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; });
- }
-
- bool containsLineBreak(const std::string &value)
- {
- return value.find('\r') != std::string::npos
- || value.find('\n') != std::string::npos;
- }
-
- 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;
- }
-
- const ConditionExpression *findParentExpression(
- const ConditionExpression &expression, const std::string &child_id)
- {
- for (const ConditionExpression &child : expression.children)
- {
- if (child.id == child_id)
- {
- return &expression;
- }
- if (const 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 addParallelForWireCellRange(
- ConditionExpression *expression,
- const std::string &wire_id,
- int first_column,
- int selected_columns,
- ConditionExpression *branch,
- const std::string ¶llel_id,
- const std::string &series_id,
- const std::string &leading_wire_id,
- const std::string &trailing_wire_id)
- {
- if (expression == nullptr)
- {
- return false;
- }
- if (expression->kind == ConditionExpressionKind::Wire
- && expression->id == wire_id)
- {
- const int total_columns = expression->wire->columnSpan;
- if (first_column < 0 || selected_columns <= 0
- || first_column + selected_columns > total_columns)
- {
- return false;
- }
- if (first_column == 0 && selected_columns == total_columns)
- {
- addParallelSibling(expression, std::move(*branch), parallel_id);
- return true;
- }
-
- ConditionExpression replacement;
- replacement.id = series_id;
- replacement.kind = ConditionExpressionKind::Series;
- if (first_column > 0)
- {
- replacement.children.push_back(ConditionExpression::fromWire(
- leading_wire_id, first_column));
- }
- ConditionExpression selected_wire = ConditionExpression::fromWire(
- wire_id, selected_columns);
- replacement.children.push_back(makeContainer(
- parallel_id,
- ConditionExpressionKind::Parallel,
- std::move(selected_wire),
- std::move(*branch)));
- const int trailing_columns =
- total_columns - first_column - selected_columns;
- if (trailing_columns > 0)
- {
- replacement.children.push_back(ConditionExpression::fromWire(
- trailing_wire_id, trailing_columns));
- }
- *expression = std::move(replacement);
- return true;
- }
- if (expression->kind == ConditionExpressionKind::Node
- || expression->kind == ConditionExpressionKind::Wire)
- {
- return false;
- }
- for (ConditionExpression &child : expression->children)
- {
- if (addParallelForWireCellRange(
- &child,
- wire_id,
- first_column,
- selected_columns,
- branch,
- parallel_id,
- series_id,
- leading_wire_id,
- trailing_wire_id))
- {
- return true;
- }
- }
- return false;
- }
-
- struct WireCellLocation
- {
- const ConditionExpression *series = nullptr;
- std::size_t child_index = 0U;
- int child_start = 0;
- };
-
- bool findWireCellLocation(
- const ConditionExpression &expression,
- const std::string &wire_id,
- WireCellLocation *location)
- {
- if (expression.kind == ConditionExpressionKind::Node
- || expression.kind == ConditionExpressionKind::Wire)
- {
- return false;
- }
-
- if (expression.kind == ConditionExpressionKind::Series)
- {
- int child_start = 0;
- for (std::size_t index = 0; index < expression.children.size(); ++index)
- {
- const ConditionExpression &child = expression.children[index];
- if (child.kind == ConditionExpressionKind::Wire
- && child.id == wire_id)
- {
- if (location != nullptr)
- {
- *location = {&expression, index, child_start};
- }
- return true;
- }
- if (findWireCellLocation(child, wire_id, location))
- {
- return true;
- }
- child_start += expressionColumns(child);
- }
- return false;
- }
-
- for (const ConditionExpression &child : expression.children)
- {
- if (findWireCellLocation(child, wire_id, location))
- {
- return true;
- }
- }
- return false;
- }
-
- bool addParallelForWireCellSeriesRange(
- ConditionExpression *root,
- const std::string &series_id,
- std::size_t first_child,
- std::size_t last_child,
- int leading_columns,
- int selected_columns,
- int trailing_columns,
- ConditionExpression *branch,
- const std::string &selected_wire_id,
- const std::string ¶llel_id,
- const std::string &leading_wire_id,
- const std::string &trailing_wire_id)
- {
- if (root == nullptr || branch == nullptr)
- {
- return false;
- }
- ConditionExpression *series = findConditionExpression(*root, series_id);
- if (series == nullptr || series->kind != ConditionExpressionKind::Series
- || first_child > last_child || last_child >= series->children.size()
- || leading_columns < 0 || selected_columns <= 0 || trailing_columns < 0)
- {
- return false;
- }
- for (std::size_t index = first_child; index <= last_child; ++index)
- {
- if (series->children[index].kind != ConditionExpressionKind::Wire)
- {
- return false;
- }
- }
-
- ConditionExpression selected_wire = ConditionExpression::fromWire(
- selected_wire_id, selected_columns);
- ConditionExpression parallel = makeContainer(
- parallel_id,
- ConditionExpressionKind::Parallel,
- std::move(selected_wire),
- std::move(*branch));
- std::vector<ConditionExpression> replacement;
- replacement.reserve(
- series->children.size() - (last_child - first_child) + 3U);
- for (std::size_t index = 0; index < first_child; ++index)
- {
- replacement.push_back(std::move(series->children[index]));
- }
- if (leading_columns > 0)
- {
- replacement.push_back(ConditionExpression::fromWire(
- leading_wire_id, leading_columns));
- }
- replacement.push_back(std::move(parallel));
- if (trailing_columns > 0)
- {
- replacement.push_back(ConditionExpression::fromWire(
- trailing_wire_id, trailing_columns));
- }
- for (std::size_t index = last_child + 1U;
- index < series->children.size();
- ++index)
- {
- replacement.push_back(std::move(series->children[index]));
- }
- series->children = std::move(replacement);
- 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);
- }
-
- void LogicEditorService::rollbackEdit(
- HistoryState before, bool modified_before)
- {
- project_service_.editProject().controlLogics = std::move(before.logics);
- project_service_.restoreModifiedState(modified_before);
- }
-
- 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";
- 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;
- 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, "未找到梯形图网络");
- }
- 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 (containsLineBreak(comment))
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "梯形图网络注释只能使用单行文本");
- }
- if (comment.size() > ProjectLimits::kMaximumRungCommentBytes)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "梯形图网络注释不能超过 128 个 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)
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- const bool create_rung = rung_id.empty();
- if (!create_rung && findRung(logic_id, rung_id) == nullptr)
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
- }
- if (create_rung
- && logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
- {
- return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
- }
- if (!isConditionConfig(config))
- {
- return failure(LogicEditorError::InvalidNode, "梯形图条件不能使用线圈节点");
- }
- // 先生成稳定节点 ID,再把新节点接到已有表达式的串联末尾
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- const std::string target_rung_id = create_rung
- ? makeUniqueRungId(*logic) : rung_id;
- ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
- HistoryState before = captureState();
- const bool modified_before = project_service_.isModified();
- Project &project = project_service_.editProject();
- auto editable_logic = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- if (create_rung)
- {
- editable_logic->rungs.push_back({
- target_rung_id,
- "网络 " + std::to_string(editable_logic->rungs.size() + 1U),
- {},
- std::nullopt,
- std::nullopt});
- }
- LadderRung *rung = findEditableRung(project, logic_id, target_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))
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::insertConditionAtColumn(
- const std::string &logic_id,
- const std::string &rung_id,
- int column,
- const LogicNodeConfig &config)
- {
- const ControlLogic *logic = findLogic(logic_id);
- const LadderRung *existing_rung = findRung(logic_id, rung_id);
- if (logic == nullptr || existing_rung == nullptr)
- {
- return failure(
- logic == nullptr ? LogicEditorError::LogicNotFound
- : LogicEditorError::RungNotFound,
- logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
- }
- if (!isConditionConfig(config))
- {
- return failure(LogicEditorError::InvalidNode, "梯形图条件不能使用输出节点");
- }
- if (column < 0 || column >= ProjectLimits::kMaximumConditionColumns)
- {
- return failure(LogicEditorError::InvalidOperation, "条件插入列必须位于第 1~10 列");
- }
-
- const int occupied_columns = existing_rung->condition.has_value()
- ? expressionColumns(*existing_rung->condition) : 0;
- if (column < occupied_columns)
- {
- return failure(LogicEditorError::InvalidOperation, "所选网格已被条件或横线占用");
- }
-
- // 点击远端空网格时,用持久化横线填充前置空档,再放入真实条件节点
- const int leading_wire_columns = column - occupied_columns;
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
- HistoryState before = captureState();
- const bool modified_before = project_service_.isModified();
- Project &project = project_service_.editProject();
- LadderRung *rung = findEditableRung(project, logic_id, rung_id);
-
- if (leading_wire_columns > 0)
- {
- ConditionExpression wire = ConditionExpression::fromWire(
- makeUniqueWireId(*logic), leading_wire_columns);
- if (!rung->condition.has_value())
- {
- rung->condition = makeContainer(
- makeUniqueExpressionId(*logic),
- ConditionExpressionKind::Series,
- std::move(wire),
- std::move(leaf));
- }
- else if (rung->condition->kind == ConditionExpressionKind::Series)
- {
- rung->condition->children.push_back(std::move(wire));
- rung->condition->children.push_back(std::move(leaf));
- }
- else
- {
- ConditionExpression series;
- series.id = makeUniqueExpressionId(*logic);
- series.kind = ConditionExpressionKind::Series;
- series.children.push_back(std::move(*rung->condition));
- series.children.push_back(std::move(wire));
- series.children.push_back(std::move(leaf));
- rung->condition = std::move(series);
- }
- }
- else 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))
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::insertConditionInBranchAtColumn(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &branch_expression_id,
- int column,
- const LogicNodeConfig &config)
- {
- 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(
- logic == nullptr ? LogicEditorError::LogicNotFound
- : LogicEditorError::RungNotFound,
- logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
- }
- if (!isConditionConfig(config))
- {
- return failure(LogicEditorError::InvalidNode, "并联空网格只能插入条件节点");
- }
-
- const ConditionExpression *branch = findExpression(
- logic_id, rung_id, branch_expression_id);
- const ConditionExpression *branch_parent = findParentExpression(
- *existing_rung->condition, branch_expression_id);
- if (branch == nullptr || branch_parent == nullptr
- || branch_parent->kind != ConditionExpressionKind::Parallel)
- {
- return failure(
- LogicEditorError::ExpressionNotFound,
- "未找到并联分支空网格");
- }
-
- const int branch_columns = expressionColumns(*branch);
- const int parallel_columns = expressionColumns(*branch_parent);
- if (column < branch_columns || column >= parallel_columns)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "所选位置不是并联分支中的可用空网格");
- }
-
- const int leading_wire_columns = column - branch_columns;
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- const std::string wire_id = leading_wire_columns > 0
- ? makeUniqueWireId(*logic) : std::string{};
- const std::string series_id = makeUniqueExpressionId(*logic);
- ConditionExpression leaf = ConditionExpression::fromNode(
- makeNode(node_id, config));
- HistoryState before = captureState();
- const bool modified_before = project_service_.isModified();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- ConditionExpression *editable_branch = findConditionExpression(
- *rung->condition, branch_expression_id);
-
- if (editable_branch->kind == ConditionExpressionKind::Series)
- {
- if (leading_wire_columns > 0)
- {
- editable_branch->children.push_back(ConditionExpression::fromWire(
- wire_id, leading_wire_columns));
- }
- editable_branch->children.push_back(std::move(leaf));
- }
- else
- {
- ConditionExpression series;
- series.id = series_id;
- series.kind = ConditionExpressionKind::Series;
- series.children.push_back(std::move(*editable_branch));
- if (leading_wire_columns > 0)
- {
- series.children.push_back(ConditionExpression::fromWire(
- wire_id, leading_wire_columns));
- }
- series.children.push_back(std::move(leaf));
- *editable_branch = std::move(series);
- }
-
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- rollbackEdit(std::move(before), modified_before);
- 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)
- {
- return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
- }
- WireSegment wire{column_span};
- std::string error;
- if (!wire.validate(&error))
- {
- return failure(LogicEditorError::InvalidOperation, error);
- }
- const LadderRung *existing_rung = rung_id.empty()
- ? nullptr : findRung(logic_id, rung_id);
- if (!rung_id.empty() && existing_rung == nullptr)
- {
- return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
- }
- bool create_rung = rung_id.empty();
- if (!create_rung && column_span == 1 && existing_rung->condition.has_value()
- && expressionColumns(*existing_rung->condition)
- >= ProjectLimits::kMaximumConditionColumns)
- {
- // 无选中目标的连续追加达到十列后,原子切换到下一个网络
- create_rung = true;
- }
- if (create_rung
- && logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
- {
- return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
- }
- const std::string target_rung_id = create_rung
- ? makeUniqueRungId(*logic) : rung_id;
- const std::string wire_id = makeUniqueWireId(*logic);
- ConditionExpression leaf = ConditionExpression::fromWire(wire_id, column_span);
- HistoryState before = captureState();
- const bool modified_before = project_service_.isModified();
- Project &project = project_service_.editProject();
- auto editable_logic = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- if (create_rung)
- {
- editable_logic->rungs.push_back({
- target_rung_id,
- "网络 " + std::to_string(editable_logic->rungs.size() + 1U),
- {},
- std::nullopt,
- std::nullopt});
- }
- LadderRung *rung = findEditableRung(project, logic_id, target_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))
- {
- rollbackEdit(std::move(before), modified_before);
- 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();
- const bool modified_before = project_service_.isModified();
- 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;
- });
- const auto insertion_position = target_iterator + 1;
- if (insertion_position != parent->children.end()
- && insertion_position->kind == ConditionExpressionKind::Wire)
- {
- // 横线代表已占用的网格位置,连续插入时先消费一格而不是扩展网络
- if (insertion_position->wire->columnSpan == 1)
- {
- *insertion_position = std::move(leaf);
- }
- else
- {
- --insertion_position->wire->columnSpan;
- parent->children.insert(insertion_position, std::move(leaf));
- }
- }
- else
- {
- parent->children.insert(insertion_position, 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))
- {
- rollbackEdit(std::move(before), modified_before);
- 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();
- const bool modified_before = project_service_.isModified();
- 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))
- {
- rollbackEdit(std::move(before), modified_before);
- 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();
- const bool modified_before = project_service_.isModified();
- 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))
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(LogicEditorError::InvalidNode, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, node_id};
- }
-
- LogicEditorResult LogicEditorService::replaceWireColumnWithCondition(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::string &wire_expression_id,
- int column_offset,
- 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, "横线只能替换为条件节点");
- }
- if (column_offset < 0 || column_offset >= wire->wire->columnSpan)
- {
- return failure(LogicEditorError::InvalidOperation, "横线网格偏移超出有效范围");
- }
- if (wire->wire->columnSpan == 1)
- {
- return replaceWireWithCondition(
- logic_id, rung_id, wire_expression_id, config);
- }
-
- const int leading_columns = column_offset;
- const int trailing_columns = wire->wire->columnSpan - column_offset - 1;
- const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
- ConditionExpression replacement;
- replacement.id = makeUniqueExpressionId(*logic);
- replacement.kind = ConditionExpressionKind::Series;
- if (leading_columns > 0)
- {
- replacement.children.push_back(ConditionExpression::fromWire(
- wire_expression_id, leading_columns));
- }
- replacement.children.push_back(
- ConditionExpression::fromNode(makeNode(node_id, config)));
- if (trailing_columns > 0)
- {
- replacement.children.push_back(ConditionExpression::fromWire(
- leading_columns > 0 ? makeUniqueWireId(*logic) : wire_expression_id,
- trailing_columns));
- }
-
- HistoryState before = captureState();
- const bool modified_before = project_service_.isModified();
- Project &project = project_service_.editProject();
- LadderRung *rung = findEditableRung(project, logic_id, rung_id);
- ConditionExpression *editable = findConditionExpression(
- *rung->condition, wire_expression_id);
- *editable = std::move(replacement);
- normalizeConditionExpression(&rung->condition);
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- rollbackEdit(std::move(before), modified_before);
- 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();
- const bool modified_before = project_service_.isModified();
- Project &project = project_service_.editProject();
- LadderRung *rung = findEditableRung(project, logic_id, rung_id);
- if (!addParallelForSelection(
- &*rung->condition,
- selected_ids,
- &leaf,
- parallel_id,
- series_id))
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(
- LogicEditorError::InvalidOperation,
- "并联选择必须是一个连续的逻辑范围");
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- rollbackEdit(std::move(before), modified_before);
- 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();
- const bool modified_before = project_service_.isModified();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- if (!addParallelForSelection(
- &*rung->condition,
- selected_ids,
- &wire,
- parallel_id,
- series_id))
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线连接只能围绕同一支路中的连续逻辑范围");
- }
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, wire_id};
- }
-
- LogicEditorResult LogicEditorService::addParallelWireBranchAtCells(
- const std::string &logic_id,
- const std::string &rung_id,
- const std::vector<std::pair<std::string, int>> &selected_wire_cells)
- {
- 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_wire_cells.empty())
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "建立竖线连接前必须选择横线网格");
- }
-
- struct SelectedWireCell
- {
- std::string wire_id;
- int offset = 0;
- int absolute_column = 0;
- };
- std::vector<SelectedWireCell> cells;
- cells.reserve(selected_wire_cells.size());
- for (const auto &cell : selected_wire_cells)
- {
- if (cell.first.empty())
- {
- return failure(
- LogicEditorError::ExpressionNotFound,
- "未找到选中的横线");
- }
- const ConditionExpression *wire = findExpression(
- logic_id, rung_id, cell.first);
- if (wire == nullptr || wire->kind != ConditionExpressionKind::Wire
- || !wire->wire.has_value()
- || cell.second < 0 || cell.second >= wire->wire->columnSpan)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线网格列偏移超出横线范围");
- }
- cells.push_back({cell.first, cell.second, 0});
- }
-
- const bool same_wire = std::all_of(
- cells.cbegin(), cells.cend(),
- [&cells](const SelectedWireCell &cell)
- {
- return cell.wire_id == cells.front().wire_id;
- });
- const std::string wire_expression_id = cells.front().wire_id;
- const ConditionExpression *selected_wire = findExpression(
- logic_id, rung_id, wire_expression_id);
- if (selected_wire == nullptr || !selected_wire->wire.has_value())
- {
- return failure(LogicEditorError::ExpressionNotFound, "未找到选中的横线");
- }
- int leading_columns = 0;
- int trailing_columns = 0;
- std::string selected_series_id;
- std::size_t selected_first_child = 0U;
- std::size_t selected_last_child = 0U;
- if (same_wire)
- {
- std::vector<int> offsets;
- offsets.reserve(cells.size());
- for (const SelectedWireCell &cell : cells)
- {
- offsets.push_back(cell.offset);
- }
- std::sort(offsets.begin(), offsets.end());
- if (std::adjacent_find(offsets.cbegin(), offsets.cend())
- != offsets.cend()
- || offsets.back() - offsets.front() + 1
- != static_cast<int>(offsets.size()))
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线网格必须是连续列");
- }
- for (SelectedWireCell &cell : cells)
- {
- cell.absolute_column = cell.offset;
- }
- leading_columns = offsets.front();
- trailing_columns = selected_wire->wire->columnSpan
- - offsets.front() - static_cast<int>(offsets.size());
- std::sort(
- cells.begin(), cells.end(),
- [](const SelectedWireCell &left, const SelectedWireCell &right)
- {
- return left.absolute_column < right.absolute_column;
- });
- }
- else
- {
- const ConditionExpression *source_series = nullptr;
- for (SelectedWireCell &cell : cells)
- {
- WireCellLocation location;
- if (!findWireCellLocation(
- *existing_rung->condition, cell.wire_id, &location)
- || location.series == nullptr)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线网格必须位于同一条连续串联路径");
- }
- if (source_series == nullptr)
- {
- source_series = location.series;
- }
- else if (source_series != location.series)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线网格必须位于同一条连续串联路径");
- }
- cell.absolute_column = location.child_start + cell.offset;
- }
- std::sort(
- cells.begin(), cells.end(),
- [](const SelectedWireCell &left, const SelectedWireCell &right)
- {
- return left.absolute_column < right.absolute_column;
- });
- if (std::adjacent_find(
- cells.cbegin(), cells.cend(),
- [](const SelectedWireCell &left, const SelectedWireCell &right)
- {
- return left.absolute_column == right.absolute_column;
- }) != cells.cend()
- || cells.back().absolute_column - cells.front().absolute_column + 1
- != static_cast<int>(cells.size()))
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线网格必须是连续列");
- }
-
- int child_start = 0;
- std::size_t first_child = source_series->children.size();
- std::size_t last_child = source_series->children.size();
- const int range_begin = cells.front().absolute_column;
- const int range_end = cells.back().absolute_column + 1;
- for (std::size_t index = 0; index < source_series->children.size(); ++index)
- {
- const ConditionExpression &child = source_series->children[index];
- const int child_end = child_start + expressionColumns(child);
- if (child_end > range_begin && child_start < range_end)
- {
- if (child.kind != ConditionExpressionKind::Wire)
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线网格不能跨越触点或并联支路");
- }
- if (first_child == source_series->children.size())
- {
- first_child = index;
- }
- last_child = index;
- }
- child_start = child_end;
- }
- if (first_child == source_series->children.size())
- {
- return failure(
- LogicEditorError::InvalidOperation,
- "未找到连续的横线网格范围");
- }
- selected_series_id = source_series->id;
- selected_first_child = first_child;
- selected_last_child = last_child;
- int layout_start = 0;
- for (std::size_t index = 0; index < first_child; ++index)
- {
- layout_start += expressionColumns(source_series->children[index]);
- }
- int selected_end = layout_start;
- for (std::size_t index = first_child;
- index <= last_child;
- ++index)
- {
- selected_end += expressionColumns(source_series->children[index]);
- }
- leading_columns = cells.front().absolute_column - layout_start;
- trailing_columns = selected_end - (cells.back().absolute_column + 1);
- }
-
- const int first_column = cells.front().absolute_column;
- const int selected_columns = static_cast<int>(cells.size());
-
- std::unordered_set<std::string> generated_wire_ids;
- const auto makeGeneratedWireId = [&]()
- {
- std::string candidate = makeUniqueWireId(*logic);
- while (generated_wire_ids.count(candidate) != 0U
- || findExpression(logic_id, rung_id, candidate) != nullptr)
- {
- candidate += "-split";
- }
- generated_wire_ids.insert(candidate);
- return candidate;
- };
-
- const std::string branch_wire_id = makeGeneratedWireId();
- const std::string selected_wire_id = same_wire
- ? wire_expression_id : cells.front().wire_id;
- const std::string leading_wire_id = leading_columns > 0
- ? makeGeneratedWireId() : std::string{};
- const std::string trailing_wire_id = trailing_columns > 0
- ? makeGeneratedWireId() : std::string{};
- ConditionExpression branch = ConditionExpression::fromWire(
- branch_wire_id, selected_columns);
- 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();
- const bool modified_before = project_service_.isModified();
- LadderRung *rung = findEditableRung(
- project_service_.editProject(), logic_id, rung_id);
- bool inserted = false;
- if (same_wire)
- {
- inserted = addParallelForWireCellRange(
- &*rung->condition,
- wire_expression_id,
- first_column,
- selected_columns,
- &branch,
- parallel_id,
- series_id,
- leading_wire_id,
- trailing_wire_id);
- }
- else
- {
- inserted = addParallelForWireCellSeriesRange(
- &*rung->condition,
- selected_series_id,
- selected_first_child,
- selected_last_child,
- leading_columns,
- selected_columns,
- trailing_columns,
- &branch,
- selected_wire_id,
- parallel_id,
- leading_wire_id,
- trailing_wire_id);
- }
- if (!inserted)
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(
- LogicEditorError::InvalidOperation,
- "竖线连接只能围绕同一条横线中的连续网格建立");
- }
- normalizeConditionExpression(&rung->condition);
- std::string validation_error;
- if (!rung->validate(&validation_error))
- {
- rollbackEdit(std::move(before), modified_before);
- return failure(LogicEditorError::InvalidOperation, validation_error);
- }
- recordHistory(std::move(before));
- return {true, LogicEditorError::None, {}, branch_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 = rung_id.empty()
- ? nullptr : findRung(logic_id, rung_id);
- if (logic == nullptr || (!rung_id.empty() && existing_rung == nullptr)
- || !isOutputConfig(config))
- {
- return failure(
- logic == nullptr ? LogicEditorError::LogicNotFound
- : existing_rung == nullptr && !rung_id.empty()
- ? LogicEditorError::RungNotFound
- : LogicEditorError::InvalidNode,
- logic == nullptr ? "未找到控制逻辑"
- : existing_rung == nullptr && !rung_id.empty()
- ? "未找到梯形图网络"
- : "梯形图输出必须使用有效的输出指令");
- }
- if (rung_id.empty()
- && logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
- {
- return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
- }
- const bool create_rung = rung_id.empty();
- const std::string target_rung_id = create_rung
- ? makeUniqueRungId(*logic) : rung_id;
- const std::string node_id = existing_rung != nullptr
- && 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 != nullptr && existing_rung->output.has_value()
- && nodesEqual(*existing_rung->output, node))
- {
- return {true, LogicEditorError::None, {}, node_id};
- }
- HistoryState before = captureState();
- Project &project = project_service_.editProject();
- auto editable_logic = std::find_if(
- project.controlLogics.begin(), project.controlLogics.end(),
- [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
- if (create_rung)
- {
- editable_logic->rungs.push_back({
- target_rung_id,
- "网络 " + std::to_string(editable_logic->rungs.size() + 1U),
- {},
- std::nullopt,
- std::nullopt});
- }
- findEditableRung(project, logic_id, target_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();
- const bool modified_before = project_service_.isModified();
- 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};
- }
- }
- }
- }
- project_service_.restoreModifiedState(modified_before);
- 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();
- const bool modified_before = project_service_.isModified();
- 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))
- {
- rollbackEdit(std::move(before), modified_before);
- 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, {}};
- }
|