ソースを参照

fix: 合并重复PLC状态通知

main
suyu 1ヶ月前
コミット
423200f8e8
3個のファイルの変更127行の追加10行の削除
  1. +25
    -10
      app/src/ui/main_window.cpp
  2. +3
    -0
      app/src/ui/main_window.h
  3. +99
    -0
      app/tests/main_window_tests.cpp

+ 25
- 10
app/src/ui/main_window.cpp ファイルの表示

@@ -176,13 +176,7 @@ MainWindow::MainWindow(
configureHmiEditor(); configureHmiEditor();
configureLogicEditor(); configureLogicEditor();
runtime_mode_service_.setPlcStatusChangedCallback( runtime_mode_service_.setPlcStatusChangedCallback(
[this]
{
QMetaObject::invokeMethod(
this,
[this] { updateModeUi(plcStatusText(runtime_mode_service_)); },
Qt::QueuedConnection);
});
[this] { schedulePlcStatusUpdate(); });
hmi_editor_service_.ensureDefaultPage(); hmi_editor_service_.ensureDefaultPage();
logic_editor_service_.ensureDefaultLogic(); logic_editor_service_.ensureDefaultLogic();
refreshProjectUi(); refreshProjectUi();
@@ -581,7 +575,23 @@ void MainWindow::connectPlc()
void MainWindow::disconnectPlc() void MainWindow::disconnectPlc()
{ {
runtime_mode_service_.disconnectPlc(); runtime_mode_service_.disconnectPlc();
updateModeUi(tr("PLC 已断开"));
}

void MainWindow::schedulePlcStatusUpdate()
{
if (plc_status_update_pending_)
{
return;
}
plc_status_update_pending_ = true;
QMetaObject::invokeMethod(
this,
[this]
{
plc_status_update_pending_ = false;
updateModeUi(plcStatusText(runtime_mode_service_));
},
Qt::QueuedConnection);
} }


// 根据控件ID加载控件属性到右侧属性面板 // 根据控件ID加载控件属性到右侧属性面板
@@ -1126,8 +1136,13 @@ void MainWindow::updateModeUi(const QString &message)
} }
updateSimulationUi(false); updateSimulationUi(false);
statusBar()->showMessage(message, 4000); statusBar()->showMessage(message, 4000);
ui_->outputList->addItem(message);
ui_->outputList->scrollToBottom();
const bool duplicate = ui_->outputList->count() > 0
&& ui_->outputList->item(ui_->outputList->count() - 1)->text() == message;
if (!duplicate)
{
ui_->outputList->addItem(message);
ui_->outputList->scrollToBottom();
}
} }


void MainWindow::updateSimulationUi(bool report_fault) void MainWindow::updateSimulationUi(bool report_fault)


+ 3
- 0
app/src/ui/main_window.h ファイルの表示

@@ -101,6 +101,8 @@ private:
void applySelectedLogicNodeProperties(); void applySelectedLogicNodeProperties();
void connectPlc(); void connectPlc();
void disconnectPlc(); void disconnectPlc();
// 合并同一事件循环内的 PLC 状态通知,避免重复刷新和重复日志
void schedulePlcStatusUpdate();
// 创建新的工程并刷新编辑界面 // 创建新的工程并刷新编辑界面
void createNewProject(); void createNewProject();
// 保存当前工程到已关联的路径 // 保存当前工程到已关联的路径
@@ -155,4 +157,5 @@ private:
PlcSerialConfiguration plc_configuration_; PlcSerialConfiguration plc_configuration_;
std::string selected_control_id_; std::string selected_control_id_;
std::string selected_logic_node_id_; std::string selected_logic_node_id_;
bool plc_status_update_pending_ = false;
}; };

+ 99
- 0
app/tests/main_window_tests.cpp ファイルの表示

@@ -1,3 +1,4 @@
#include "domain/active_register_repository.h"
#include "domain/project_storage.h" #include "domain/project_storage.h"
#include "domain/register_repository.h" #include "domain/register_repository.h"
#include "services/hmi_editor_service.h" #include "services/hmi_editor_service.h"
@@ -18,6 +19,7 @@
#include <QDockWidget> #include <QDockWidget>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QListWidget>
#include <QSpinBox> #include <QSpinBox>
#include <QTimer> #include <QTimer>


@@ -42,6 +44,60 @@ public:
} }
}; };


class RepeatedStatusGateway final : public PlcCommunicationGateway
{
public:
PlcCommunicationResult connectDevice(
const PlcSerialConfiguration &) override
{
connection_state = PlcConnectionState::Connecting;
notifyStateChanged();
connection_state = PlcConnectionState::Connected;
notifyStateChanged();
notifyStateChanged();
return {true, {}};
}

void disconnectDevice() override
{
connection_state = PlcConnectionState::Disconnected;
notifyStateChanged();
}

void setPollAddresses(const std::vector<RegisterAddress> &) override {}
PlcConnectionState state() const override { return connection_state; }
bool initialReadCompleted() const override { return false; }
const std::string &lastError() const override { return last_error; }

void setCallbacks(
std::function<void()> state_callback,
std::function<void(bool)> initial_callback,
std::function<void()> cache_callback,
std::function<void(const std::string &)> error_callback) override
{
state_changed = std::move(state_callback);
initial_read_changed = std::move(initial_callback);
cache_updated = std::move(cache_callback);
error_reported = std::move(error_callback);
}

private:
void notifyStateChanged()
{
if (state_changed)
{
state_changed();
}
}

PlcConnectionState connection_state = PlcConnectionState::Disconnected;
std::string last_error;
std::function<void()> state_changed;
std::function<void(bool)> initial_read_changed;
std::function<void()> cache_updated;
std::function<void(const std::string &)> error_reported;
};

void require(bool condition, const std::string &message) void require(bool condition, const std::string &message)
{ {
if (!condition) if (!condition)
@@ -273,6 +329,48 @@ void testPlcConfigurationUsesDialog()
"PLC configuration action must open the dedicated configuration dialog"); "PLC configuration action must open the dedicated configuration dialog");
} }


void testRepeatedPlcStatusNotificationsAreCoalesced()
{
TestProjectStorage storage;
ProjectService project_service(storage);
HmiEditorService editor_service(project_service);
LogicEditorService logic_editor_service(project_service);
VirtualRegisterRepository virtual_repository;
VirtualRegisterRepository plc_repository;
ActiveRegisterRepository active_repository(virtual_repository);
HmiRuntimeService runtime_service(active_repository);
OfflineSimulationService simulation_service(virtual_repository);
RuntimeModeService mode_service(project_service, simulation_service);
RepeatedStatusGateway gateway;
mode_service.configurePlc(
gateway, active_repository, virtual_repository, plc_repository);
MainWindow window(
mode_service,
project_service,
editor_service,
logic_editor_service,
runtime_service);
QListWidget *output = requiredChild<QListWidget>(window, "outputList");
const int initial_count = output->count();

const PlcCommunicationResult result = mode_service.connectPlc(
{"COM3", 1, 9600, 8, 2, 1, 1000, 2, 200});
require(result.succeeded, "PLC connection setup must succeed");
QApplication::processEvents();

const QString expected = QStringLiteral("PLC 已连接,正在读取工程使用的 M/D 地址");
int matching_count = 0;
for (int index = initial_count; index < output->count(); ++index)
{
if (output->item(index)->text() == expected)
{
++matching_count;
}
}
require(matching_count == 1,
"repeated PLC status callbacks must append one coalesced output message");
}

} // namespace } // namespace


int main(int argc, char *argv[]) int main(int argc, char *argv[])
@@ -285,6 +383,7 @@ int main(int argc, char *argv[])
{ {
testModeActionsControlEditingAvailability(); testModeActionsControlEditingAvailability();
testPlcConfigurationUsesDialog(); testPlcConfigurationUsesDialog();
testRepeatedPlcStatusNotificationsAreCoalesced();
} }
catch (const std::exception &error) catch (const std::exception &error)
{ {


読み込み中…
キャンセル
保存