소스 검색

fix: 修复并联支路批量删除残留

main
suyu 1 개월 전
부모
커밋
2468cc6d36
4개의 변경된 파일152개의 추가작업 그리고 0개의 파일을 삭제
  1. +6
    -0
      app/src/domain/control_logic_model.cpp
  2. +9
    -0
      app/src/services/logic_editor_service.cpp
  3. +55
    -0
      app/tests/logic_editor_service_tests.cpp
  4. +82
    -0
      app/tests/main_window_tests.cpp

+ 6
- 0
app/src/domain/control_logic_model.cpp 파일 보기

@@ -251,6 +251,12 @@ void normalizeExpression(ConditionExpression *expression)
std::vector<ConditionExpression> flattened;
for (ConditionExpression &child : expression->children)
{
if ((child.kind == ConditionExpressionKind::Series
|| child.kind == ConditionExpressionKind::Parallel)
&& child.children.empty())
{
continue;
}
if (child.kind == expression->kind)
{
for (ConditionExpression &grandchild : child.children)


+ 9
- 0
app/src/services/logic_editor_service.cpp 파일 보기

@@ -1430,6 +1430,15 @@ LogicEditorResult LogicEditorService::removeNodes(
}
}
}
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()};
}


+ 55
- 0
app/tests/logic_editor_service_tests.cpp 파일 보기

@@ -192,6 +192,60 @@ void testStructuredWireEditing()
"wire branch deletion must participate in ladder undo history");
}

void testBatchDeleteAllNodesInParallelBranch()
{
TestProjectStorage storage;
ProjectService project_service(storage);
LogicEditorService service(project_service);
const std::string logic_id = service.ensureDefaultLogic().id;
const std::string rung_id = service.firstRungId(logic_id);

const LogicEditorResult first = service.appendCondition(
logic_id, rung_id, contact(0));
const LogicEditorResult second = service.appendCondition(
logic_id, rung_id, contact(1));
const LogicEditorResult third = service.appendCondition(
logic_id, rung_id, contact(2));
const LogicEditorResult fourth = service.appendCondition(
logic_id, rung_id, contact(3));
require(first.succeeded && second.succeeded && third.succeeded
&& fourth.succeeded,
"parallel batch deletion setup contacts must be created");

const LogicEditorResult branch = service.addParallelBranch(
logic_id, rung_id,
{second.id, third.id, fourth.id},
contact(10));
require(branch.succeeded,
"parallel batch deletion setup branch must be created");

require(service.removeNodes(
logic_id, {second.id, third.id, fourth.id})
.succeeded,
"deleting every node in a parallel branch as one batch must succeed");
const LadderRung *rung = service.findRung(logic_id, rung_id);
require(rung != nullptr && rung->condition.has_value()
&& rung->validate(),
"batch deletion must leave a valid normalized ladder expression");
require(rung->condition->kind == ConditionExpressionKind::Series
&& rung->condition->children.size() == 2U
&& rung->condition->children.at(0).kind
== ConditionExpressionKind::Node
&& rung->condition->children.at(1).kind
== ConditionExpressionKind::Node,
"an empty parallel branch must collapse into the remaining branch");
require(service.findNode(logic_id, second.id) == nullptr
&& service.findNode(logic_id, third.id) == nullptr
&& service.findNode(logic_id, fourth.id) == nullptr,
"all selected parallel branch nodes must be removed");
require(service.undo().succeeded,
"parallel batch deletion must be undoable");
require(service.findNode(logic_id, second.id) != nullptr
&& service.findNode(logic_id, third.id) != nullptr
&& service.findNode(logic_id, fourth.id) != nullptr,
"undo must restore every deleted parallel branch node");
}

void testConditionColumnLimit()
{
TestProjectStorage storage;
@@ -415,6 +469,7 @@ int main()
testStructuredEditingAndNormalization();
testRangeParallelInsertion();
testStructuredWireEditing();
testBatchDeleteAllNodesInParallelBranch();
testConditionColumnLimit();
testUnconditionalOutputEditing();
testLogicLifecycleAndOrdering();


+ 82
- 0
app/tests/main_window_tests.cpp 파일 보기

@@ -43,9 +43,11 @@
#include <QToolButton>
#include <QTreeWidget>

#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

namespace {

@@ -285,6 +287,85 @@ QColor renderedColorAt(HmiEditorWidget &view, const QPoint &viewport_position)
return image.pixelColor(viewport_position);
}

ContactNodeConfig logicContact(int address)
{
return {RegisterAddress{RegisterArea::M, address}, ContactMode::NormallyOpen};
}

void testLogicEditorBatchDeletesWholeParallelRow()
{
TestProjectStorage storage;
ProjectService project_service(storage);
LogicEditorService service(project_service);
const std::string logic_id = service.ensureDefaultLogic().id;
const std::string rung_id = service.firstRungId(logic_id);

std::vector<std::string> top_ids;
for (int address = 0; address < 4; ++address)
{
const LogicEditorResult result = service.appendCondition(
logic_id, rung_id, logicContact(address));
require(result.succeeded,
"logic UI batch deletion setup contacts must be created");
top_ids.push_back(result.id);
}
const LogicEditorResult lower_first = service.addParallelBranch(
logic_id, rung_id, top_ids, logicContact(10));
require(lower_first.succeeded,
"logic UI batch deletion setup branch must be created");
std::string lower_tail = lower_first.id;
for (int address = 11; address < 14; ++address)
{
const LogicEditorResult result = service.insertConditionAfter(
logic_id, rung_id, lower_tail, logicContact(address));
require(result.succeeded,
"logic UI batch deletion lower branch contacts must be created");
lower_tail = result.id;
}

LogicEditorWidget view(service);
view.setLogicId(logic_id);
qreal lower_row_y = 0.0;
for (QGraphicsItem *item : view.scene()->items())
{
if (qFuzzyCompare(item->zValue(), 3.0))
{
lower_row_y = std::max(lower_row_y, item->scenePos().y());
}
}
int selected_count = 0;
for (QGraphicsItem *item : view.scene()->items())
{
if (qFuzzyCompare(item->zValue(), 3.0)
&& qFuzzyCompare(item->scenePos().y(), lower_row_y))
{
item->setSelected(true);
++selected_count;
}
}
require(selected_count == 4,
"logic UI batch deletion must select every contact in the lower branch");
require(view.deleteSelected().succeeded,
"logic UI must batch delete a complete parallel row");

const LadderRung *rung = service.findRung(logic_id, rung_id);
require(rung != nullptr && rung->condition.has_value() && rung->validate(),
"logic UI batch deletion must leave a valid ladder expression");
require(rung->condition->kind == ConditionExpressionKind::Series
&& rung->condition->children.size() == 4U,
"logic UI batch deletion must preserve only the original series row");
const QList<QGraphicsItem *> rendered_items = view.scene()->items();
const bool has_vertical_connector = std::any_of(
rendered_items.cbegin(),
rendered_items.cend(),
[](const QGraphicsItem *item)
{
return qFuzzyCompare(item->zValue(), 2.5);
});
require(!has_vertical_connector,
"logic UI must not render a vertical connector after removing the branch");
}

void testRuntimeButtonMouseInteraction()
{
TestProjectStorage storage;
@@ -1645,6 +1726,7 @@ int main(int argc, char *argv[])

try
{
testLogicEditorBatchDeletesWholeParallelRow();
testRuntimeButtonMouseInteraction();
testRuntimeProgressBarRendering();
testRuntimePageJumpDoesNotRequireRegisterWritePermission();


불러오는 중...
취소
저장