|
- #include "main_window.h"
-
- #include "hmi_editor_widget.h"
- #include "alarm_configuration_dialog.h"
- #include "logic_editor_widget.h"
- #include "plc_connection_dialog.h"
- #include "runtime_monitor_widget.h"
- #include "free_monitor_widget.h"
- #include "services/hmi_editor_service.h"
- #include "services/hmi_navigation_service.h"
- #include "services/hmi_runtime_service.h"
- #include "services/alarm_editor_service.h"
- #include "services/alarm_service.h"
- #include "services/logic_editor_service.h"
- #include "services/project_service.h"
- #include "services/runtime_mode_service.h"
- #include "services/register_monitor_service.h"
- #include "ui_main_window.h"
-
- #include <QActionGroup>
- #include <QApplication>
- #include <QComboBox>
- #include <QFileDialog>
- #include <QInputDialog>
- #include <QLabel>
- #include <QLineEdit>
- #include <QMessageBox>
- #include <QMenu>
- #include <QPushButton>
- #include <QSignalBlocker>
- #include <QSpinBox>
- #include <QStatusBar>
- #include <QStyle>
- #include <QTimer>
- #include <QTabWidget>
- #include <QToolButton>
- #include <QTreeWidget>
- #include <QTreeWidgetItem>
-
- #include <cstdint>
- #include <type_traits>
-
- namespace {
-
- enum class ProjectTreeItemKind
- {
- Root = 0,
- HmiPage = 1,
- ControlLogic = 2
- };
-
- constexpr int kProjectTreeKindRole = Qt::UserRole;
- constexpr int kProjectTreeIdRole = Qt::UserRole + 1;
-
- QString modeText(ApplicationMode mode)
- {
- switch (mode)
- {
- case ApplicationMode::Editing:
- {
- return MainWindow::tr("编辑态");
- }
- case ApplicationMode::OfflineRunning:
- {
- return MainWindow::tr("离线运行态");
- }
- case ApplicationMode::OnlineRunning:
- {
- return MainWindow::tr("真机运行态");
- }
- default:
- {
- return MainWindow::tr("未知状态");
- }
- }
- }
-
- QString transitionErrorText(ModeTransitionError error)
- {
- switch (error)
- {
- case ModeTransitionError::MustReturnToEditing:
- {
- return MainWindow::tr("请先返回编辑态,再切换运行模式");
- }
- case ModeTransitionError::InitialPlcReadRequired:
- {
- return MainWindow::tr("真机运行前必须连接 PLC 并完成首次读取");
- }
- case ModeTransitionError::AlreadyInRequestedMode:
- {
- return MainWindow::tr("当前已处于所选模式");
- }
- case ModeTransitionError::ProjectNotReady:
- {
- return MainWindow::tr("工程存在未配置或未完成的 HMI/梯形图节点,暂不能进入离线运行态");
- }
- case ModeTransitionError::SimulationStartFailed:
- {
- return MainWindow::tr("离线逻辑执行器预检或启动失败");
- }
- case ModeTransitionError::None:
- default:
- {
- return MainWindow::tr("模式切换失败");
- }
- }
- }
-
- std::string toUtf8(const QString &value)
- {
- const QByteArray bytes = value.toUtf8();
- return std::string(bytes.constData(), static_cast<std::size_t>(bytes.size()));
- }
-
- QString fromUtf8(const std::string &value)
- {
- return QString::fromUtf8(value.data(), static_cast<int>(value.size()));
- }
-
- QString plcStatusText(const RuntimeModeService &service)
- {
- switch (service.plcConnectionState())
- {
- case PlcConnectionState::Connecting:
- return MainWindow::tr("PLC 正在连接");
- case PlcConnectionState::Connected:
- return service.initialPlcReadCompleted()
- ? MainWindow::tr("PLC 已连接,首次读取完成")
- : MainWindow::tr("PLC 已连接,正在读取工程使用的 M/D 地址");
- case PlcConnectionState::Recovering:
- return MainWindow::tr("PLC 通信已恢复,正在重新读取工程使用的 M/D 地址");
- case PlcConnectionState::Faulted:
- {
- const QString error = fromUtf8(service.plcError());
- return error.isEmpty()
- ? MainWindow::tr("PLC 通信故障")
- : MainWindow::tr("PLC 通信故障:%1").arg(error);
- }
- case PlcConnectionState::Disconnected:
- {
- const QString error = fromUtf8(service.plcError());
- return error.isEmpty()
- ? MainWindow::tr("PLC 已断开")
- : MainWindow::tr("PLC 已断开:%1").arg(error);
- }
- default:
- {
- return MainWindow::tr("PLC 已断开");
- }
- }
- }
-
- QString controlTypeText(HmiControlType type)
- {
- switch (type)
- {
- case HmiControlType::Button:
- {
- return MainWindow::tr("按钮");
- }
- case HmiControlType::Indicator:
- {
- return MainWindow::tr("指示灯");
- }
- case HmiControlType::NumericDisplay:
- {
- return MainWindow::tr("数值显示");
- }
- case HmiControlType::NumericInput:
- {
- return MainWindow::tr("数值输入");
- }
- case HmiControlType::Label:
- {
- return MainWindow::tr("文本");
- }
- case HmiControlType::PageJump:
- {
- return MainWindow::tr("页面跳转");
- }
- case HmiControlType::AlarmList:
- {
- return MainWindow::tr("报警列表");
- }
- default:
- {
- return MainWindow::tr("未知控件");
- }
- }
- }
-
- } // namespace
-
- MainWindow::MainWindow(
- RuntimeModeService &runtime_mode_service,
- ProjectService &project_service,
- HmiEditorService &hmi_editor_service,
- LogicEditorService &logic_editor_service,
- HmiRuntimeService &hmi_runtime_service,
- AlarmEditorService &alarm_editor_service,
- AlarmService &alarm_service,
- RegisterMonitorService ®ister_monitor_service,
- QWidget *parent)
- : QMainWindow(parent),
- ui_(std::make_unique<Ui::MainWindow>()),
- runtime_mode_service_(runtime_mode_service),
- project_service_(project_service),
- hmi_editor_service_(hmi_editor_service),
- logic_editor_service_(logic_editor_service),
- hmi_runtime_service_(hmi_runtime_service),
- alarm_editor_service_(alarm_editor_service),
- alarm_service_(alarm_service),
- register_monitor_service_(register_monitor_service),
- owned_hmi_navigation_service_(
- std::make_unique<HmiNavigationService>(project_service)),
- hmi_navigation_service_(owned_hmi_navigation_service_.get())
- {
- initializeUi();
- }
-
- MainWindow::MainWindow(
- RuntimeModeService &runtime_mode_service,
- ProjectService &project_service,
- HmiEditorService &hmi_editor_service,
- LogicEditorService &logic_editor_service,
- HmiRuntimeService &hmi_runtime_service,
- AlarmEditorService &alarm_editor_service,
- AlarmService &alarm_service,
- HmiNavigationService &hmi_navigation_service,
- RegisterMonitorService ®ister_monitor_service,
- QWidget *parent)
- : QMainWindow(parent),
- ui_(std::make_unique<Ui::MainWindow>()),
- runtime_mode_service_(runtime_mode_service),
- project_service_(project_service),
- hmi_editor_service_(hmi_editor_service),
- logic_editor_service_(logic_editor_service),
- hmi_runtime_service_(hmi_runtime_service),
- alarm_editor_service_(alarm_editor_service),
- alarm_service_(alarm_service),
- register_monitor_service_(register_monitor_service),
- hmi_navigation_service_(&hmi_navigation_service)
- {
- initializeUi();
- }
-
- void MainWindow::initializeUi()
- {
- ui_->setupUi(this);
- configureAppearance();
- configureActions();
- configurePropertyEditor();
- configurePlcConnection();
- configureAlarms();
- configureHmiEditor();
- configureLogicEditor();
- configureRuntimeMonitor();
- configureProjectTree();
- runtime_mode_service_.setPlcStatusChangedCallback(
- [this] { schedulePlcStatusUpdate(); });
- hmi_editor_service_.ensureDefaultPage();
- logic_editor_service_.ensureDefaultLogic();
- current_hmi_page_id_ = hmi_editor_service_.firstPageId();
- current_logic_id_ = logic_editor_service_.firstLogicId();
- refreshProjectUi();
- updateModeUi(tr("系统已进入编辑态"));
- }
-
- MainWindow::~MainWindow()
- {
- runtime_mode_service_.setPlcStatusChangedCallback({});
- }
-
- const std::string &MainWindow::currentHmiPageId() const
- {
- return current_hmi_page_id_;
- }
-
- const std::string &MainWindow::currentLogicId() const
- {
- return current_logic_id_;
- }
-
- void MainWindow::configureActions()
- {
- mode_action_group_ = new QActionGroup(this);
- mode_action_group_->setExclusive(true);
- mode_action_group_->addAction(ui_->editingModeAction);
- mode_action_group_->addAction(ui_->offlineModeAction);
- mode_action_group_->addAction(ui_->onlineModeAction);
-
- connect(ui_->editingModeAction, &QAction::triggered, this,
- [this] { requestMode(ApplicationMode::Editing); });
- connect(ui_->offlineModeAction, &QAction::triggered, this,
- [this] { requestMode(ApplicationMode::OfflineRunning); });
- connect(ui_->onlineModeAction, &QAction::triggered, this,
- [this] { requestMode(ApplicationMode::OnlineRunning); });
- connect(ui_->exitAction, &QAction::triggered, qApp, &QApplication::closeAllWindows);
- connect(ui_->newProjectAction, &QAction::triggered, this, &MainWindow::createNewProject);
- connect(ui_->saveProjectAction, &QAction::triggered, this, &MainWindow::saveProject);
- connect(ui_->saveAsProjectAction, &QAction::triggered, this, &MainWindow::saveProjectAs);
- connect(ui_->loadProjectAction, &QAction::triggered, this, &MainWindow::loadProject);
-
- connect(ui_->addButtonAction, &QAction::triggered, this,
- [this] { addHmiControl(HmiControlType::Button); });
- connect(ui_->addIndicatorAction, &QAction::triggered, this,
- [this] { addHmiControl(HmiControlType::Indicator); });
- connect(ui_->addNumericDisplayAction, &QAction::triggered, this,
- [this] { addHmiControl(HmiControlType::NumericDisplay); });
- connect(ui_->addNumericInputAction, &QAction::triggered, this,
- [this] { addHmiControl(HmiControlType::NumericInput); });
- connect(ui_->addLabelAction, &QAction::triggered, this,
- [this] { addHmiControl(HmiControlType::Label); });
- connect(ui_->addPageJumpAction, &QAction::triggered, this,
- [this] { addHmiControl(HmiControlType::PageJump); });
- connect(ui_->addAlarmListAction, &QAction::triggered, this,
- [this] { addHmiControl(HmiControlType::AlarmList); });
- connect(ui_->deleteControlAction, &QAction::triggered,
- this, &MainWindow::deleteSelectedControl);
-
- connect(ui_->addRungAction, &QAction::triggered,
- this, &MainWindow::addLogicRung);
- connect(ui_->parallelInsertAction, &QAction::triggered,
- this,
- [this]
- {
- addLogicParallelBranch(ContactNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- ContactMode::NormallyOpen});
- });
- QMenu *parallel_menu = new QMenu(this);
- QAction *parallel_open = parallel_menu->addAction(tr("并联常开触点"));
- QAction *parallel_closed = parallel_menu->addAction(tr("并联常闭触点"));
- QAction *parallel_compare = parallel_menu->addAction(tr("并联比较条件"));
- connect(parallel_open, &QAction::triggered,
- this,
- [this]
- {
- addLogicParallelBranch(ContactNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- ContactMode::NormallyOpen});
- });
- connect(parallel_closed, &QAction::triggered,
- this,
- [this]
- {
- addLogicParallelBranch(ContactNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- ContactMode::NormallyClosed});
- });
- connect(parallel_compare, &QAction::triggered,
- this,
- [this]
- {
- addLogicParallelBranch(CompareNodeConfig{
- RegisterAddress{RegisterArea::D, 0},
- ComparisonOperator::Equal,
- 0});
- });
- ui_->parallelInsertAction->setMenu(parallel_menu);
- if (QToolButton *parallel_button = qobject_cast<QToolButton *>(
- ui_->logicToolBar->widgetForAction(ui_->parallelInsertAction)))
- {
- parallel_button->setPopupMode(QToolButton::MenuButtonPopup);
- }
- connect(ui_->addNormallyOpenAction, &QAction::triggered,
- this,
- [this]
- {
- addLogicCondition(ContactNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- ContactMode::NormallyOpen});
- });
- connect(ui_->addNormallyClosedAction, &QAction::triggered,
- this,
- [this]
- {
- addLogicCondition(ContactNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- ContactMode::NormallyClosed});
- });
- connect(ui_->addNormalCoilAction, &QAction::triggered,
- this,
- [this]
- {
- setLogicOutput(CoilNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- CoilMode::Normal});
- });
- connect(ui_->addSetCoilAction, &QAction::triggered,
- this,
- [this]
- {
- setLogicOutput(CoilNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- CoilMode::Set});
- });
- connect(ui_->addResetCoilAction, &QAction::triggered,
- this,
- [this]
- {
- setLogicOutput(CoilNodeConfig{
- RegisterAddress{RegisterArea::M, 0},
- CoilMode::Reset});
- });
- connect(ui_->addCompareAction, &QAction::triggered,
- this,
- [this]
- {
- addLogicCondition(CompareNodeConfig{
- RegisterAddress{RegisterArea::D, 0},
- ComparisonOperator::Equal,
- 0});
- });
- connect(ui_->deleteLogicAction, &QAction::triggered,
- this, &MainWindow::deleteSelectedLogicObject);
-
- connect(ui_->editorTabWidget, &QTabWidget::currentChanged,
- this,
- [this](int index)
- {
- ui_->hmiToolBar->setVisible(index == 0);
- ui_->logicToolBar->setVisible(index == 1);
- if (index == 0)
- {
- showControlProperties(selected_control_id_);
- }
- else if (index == 1)
- {
- showLogicNodeProperties(selected_logic_node_id_);
- }
- if (index == 0 || index == 1)
- {
- last_editing_tab_index_ = index;
- refreshProjectUi();
- }
- });
- ui_->hmiToolBar->setVisible(ui_->editorTabWidget->currentIndex() == 0);
- ui_->logicToolBar->setVisible(ui_->editorTabWidget->currentIndex() == 1);
-
- ui_->viewMenu->addAction(ui_->projectDock->toggleViewAction());
- ui_->viewMenu->addAction(ui_->propertiesDock->toggleViewAction());
- ui_->viewMenu->addAction(ui_->outputDock->toggleViewAction());
- ui_->viewMenu->addAction(ui_->hmiToolBar->toggleViewAction());
- ui_->viewMenu->addAction(ui_->logicToolBar->toggleViewAction());
- ui_->viewMenu->addSeparator();
- ui_->viewMenu->addAction(ui_->modeToolBar->toggleViewAction());
- }
-
- void MainWindow::configureAppearance()
- {
- setDockNestingEnabled(true);
- setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
- setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
-
- ui_->editingModeAction->setIcon(style()->standardIcon(QStyle::SP_FileDialogDetailedView));
- ui_->offlineModeAction->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
- ui_->onlineModeAction->setIcon(style()->standardIcon(QStyle::SP_DriveNetIcon));
- ui_->newProjectAction->setIcon(style()->standardIcon(QStyle::SP_FileIcon));
- ui_->saveProjectAction->setIcon(style()->standardIcon(QStyle::SP_DialogSaveButton));
- ui_->loadProjectAction->setIcon(style()->standardIcon(QStyle::SP_DialogOpenButton));
- ui_->configurePlcAction->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon));
- ui_->disconnectPlcAction->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
- ui_->addButtonAction->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton));
- ui_->addIndicatorAction->setIcon(style()->standardIcon(QStyle::SP_DialogYesButton));
- ui_->addNumericDisplayAction->setIcon(
- style()->standardIcon(QStyle::SP_FileDialogInfoView));
- ui_->addNumericInputAction->setIcon(
- style()->standardIcon(QStyle::SP_FileDialogContentsView));
- ui_->addLabelAction->setIcon(
- style()->standardIcon(QStyle::SP_FileDialogInfoView));
- ui_->addPageJumpAction->setIcon(
- style()->standardIcon(QStyle::SP_ArrowForward));
- ui_->addAlarmListAction->setIcon(
- style()->standardIcon(QStyle::SP_MessageBoxWarning));
- ui_->configureAlarmsAction->setIcon(
- style()->standardIcon(QStyle::SP_FileDialogDetailedView));
- ui_->deleteControlAction->setIcon(style()->standardIcon(QStyle::SP_TrashIcon));
- ui_->addRungAction->setIcon(style()->standardIcon(QStyle::SP_FileIcon));
- ui_->parallelInsertAction->setIcon(style()->standardIcon(QStyle::SP_ArrowDown));
- ui_->addNormallyOpenAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight));
- ui_->addNormallyClosedAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight));
- ui_->addNormalCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton));
- ui_->addSetCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogYesButton));
- ui_->addResetCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogNoButton));
- ui_->addCompareAction->setIcon(style()->standardIcon(QStyle::SP_FileDialogInfoView));
- ui_->deleteLogicAction->setIcon(style()->standardIcon(QStyle::SP_TrashIcon));
- ui_->editorTabWidget->setTabIcon(0, style()->standardIcon(QStyle::SP_DesktopIcon));
- ui_->editorTabWidget->setTabIcon(1, style()->standardIcon(QStyle::SP_FileDialogListView));
- ui_->editorTabWidget->setTabIcon(2, style()->standardIcon(QStyle::SP_ComputerIcon));
- ui_->outputDock->setMaximumHeight(220);
-
- mode_status_label_ = new QLabel(this);
- mode_status_label_->setObjectName(QStringLiteral("modeStatusLabel"));
- mode_status_label_->setMinimumWidth(88);
- mode_status_label_->setAlignment(Qt::AlignCenter);
- register_status_label_ = new QLabel(this);
- register_status_label_->setObjectName(QStringLiteral("registerStatusLabel"));
- register_status_label_->setMinimumWidth(132);
- executor_status_label_ = new QLabel(this);
- executor_status_label_->setObjectName(QStringLiteral("executorStatusLabel"));
- executor_status_label_->setMinimumWidth(132);
- statusBar()->addPermanentWidget(mode_status_label_);
- statusBar()->addPermanentWidget(register_status_label_);
- statusBar()->addPermanentWidget(executor_status_label_);
-
- setStyleSheet(QStringLiteral(
- "QMainWindow { background: #f3f5f7; }"
- "QToolBar { background: #ffffff; border: 0; border-bottom: 1px solid #cbd2d9;"
- " padding: 4px; spacing: 3px; }"
- "QToolButton { min-height: 26px; padding: 3px 9px; border: 1px solid transparent; }"
- "QToolButton:hover { background: #edf2f6; border-color: #c5ced6; }"
- "QToolButton:checked { background: #dcece3; border-color: #75a58a; }"
- "QDockWidget { color: #24313b; font-weight: 600; }"
- "QDockWidget::title { background: #e9edf0; padding: 6px;"
- " border-bottom: 1px solid #cbd2d9; }"
- "QTreeWidget, QListWidget, QScrollArea { background: #ffffff; border: 1px solid #d5dbe0; }"
- "QTabWidget::pane { border: 0; background: #f3f5f7; }"
- "QTabBar::tab { background: #e5e9ec; padding: 7px 14px; border-right: 1px solid #cbd2d9; }"
- "QTabBar::tab:selected { background: #ffffff; color: #15232d; }"
- "QFrame#hmiCanvasPlaceholder, QFrame#logicCanvasPlaceholder { background: #ffffff;"
- " border: 1px solid #cbd2d9; }"
- "QLabel#hmiEmptyLabel, QLabel#logicEmptyLabel { color: #75838d; }"
- "QLabel#hmiPageTitleLabel { color: #1d2a33; font-weight: 600; }"
- "QLabel#hmiPageSizeLabel { color: #66747e; }"));
- }
-
- void MainWindow::configureHmiEditor()
- {
- QLayout *layout = ui_->hmiCanvasPlaceholder->layout();
- delete ui_->hmiEmptyLabel;
- hmi_editor_widget_ = new HmiEditorWidget(
- hmi_editor_service_,
- hmi_runtime_service_,
- alarm_service_,
- ui_->hmiCanvasPlaceholder);
- hmi_editor_widget_->setMinimumHeight(320);
- layout->addWidget(hmi_editor_widget_);
- connect(hmi_editor_widget_, &HmiEditorWidget::controlSelected,
- this,
- [this](const QString &id) { showControlProperties(toUtf8(id)); });
- connect(hmi_editor_widget_, &HmiEditorWidget::controlChanged,
- this,
- [this](const QString &id)
- {
- showControlProperties(toUtf8(id));
- refreshProjectUi();
- });
- connect(hmi_editor_widget_, &HmiEditorWidget::editorError,
- this,
- [this](const QString &message) { statusBar()->showMessage(message, 5000); });
- // 运行态定时投影仓库最新值,编辑态不触发寄存器读取
- runtime_refresh_timer_ = new QTimer(this);
- runtime_refresh_timer_->setInterval(150);
- connect(runtime_refresh_timer_, &QTimer::timeout,
- this,
- [this]
- {
- // 离线 / 联机运行模式
- if (runtime_mode_service_.mode() != ApplicationMode::Editing)
- {
- alarm_service_.refresh();
- hmi_editor_widget_->refreshRuntimeValues();
- runtime_monitor_widget_->refreshValues(
- runtime_mode_service_.mode(),
- runtime_mode_service_.plcConnectionState());
- updateSimulationUi(false);
- }
- });
- runtime_refresh_timer_->start();
- connect(&runtime_mode_service_.offlineSimulationService(),
- &OfflineSimulationService::stateChanged,
- this,
- [this] { updateSimulationUi(true); },
- Qt::QueuedConnection);
- connect(&runtime_mode_service_.offlineSimulationService(),
- &OfflineSimulationService::scanCompleted,
- this,
- [this]
- {
- logic_editor_widget_->setRuntimeTrace(
- runtime_mode_service_.offlineSimulationService()
- .traceSnapshot().forLogic(current_logic_id_));
- runtime_monitor_widget_->setLogicTrace(
- runtime_mode_service_.offlineSimulationService().traceSnapshot());
- },
- Qt::QueuedConnection);
- }
-
- void MainWindow::configureLogicEditor()
- {
- QLayout *layout = ui_->logicCanvasPlaceholder->layout();
- delete ui_->logicEmptyLabel;
- logic_editor_widget_ = new LogicEditorWidget(
- logic_editor_service_, ui_->logicCanvasPlaceholder);
- logic_editor_widget_->setObjectName(QStringLiteral("logicEditorWidget"));
- logic_editor_widget_->setMinimumHeight(320);
- layout->addWidget(logic_editor_widget_);
- connect(logic_editor_widget_, &LogicEditorWidget::nodeSelected,
- this,
- [this](const QString &id)
- {
- showLogicNodeProperties(toUtf8(id));
- });
- connect(logic_editor_widget_, &LogicEditorWidget::graphChanged,
- this, &MainWindow::refreshProjectUi);
- connect(logic_editor_widget_, &LogicEditorWidget::editorError,
- this,
- [this](const QString &message)
- {
- statusBar()->showMessage(message, 5000);
- });
- }
-
- void MainWindow::configureRuntimeMonitor()
- {
- runtime_monitor_widget_ = new RuntimeMonitorWidget(
- hmi_editor_service_,
- hmi_runtime_service_,
- logic_editor_service_,
- project_service_,
- *hmi_navigation_service_,
- alarm_service_,
- register_monitor_service_,
- ui_->runtimeMonitorTab);
- runtime_monitor_widget_->setObjectName(QStringLiteral("runtimeMonitorWidget"));
- ui_->runtimeMonitorLayout->addWidget(runtime_monitor_widget_);
- connect(runtime_monitor_widget_->freeMonitorWidget(),
- &FreeMonitorWidget::monitorAddressesChanged,
- this,
- [this]
- {
- runtime_mode_service_.setMonitorAddresses(
- register_monitor_service_.addresses());
- runtime_monitor_widget_->refreshValues(
- runtime_mode_service_.mode(),
- runtime_mode_service_.plcConnectionState());
- });
- connect(runtime_monitor_widget_->freeMonitorWidget(),
- &FreeMonitorWidget::operationMessage,
- this,
- [this](const QString &message)
- {
- statusBar()->showMessage(message, 4000);
- });
- connect(runtime_monitor_widget_, &RuntimeMonitorWidget::navigationFailed,
- this,
- [this](const QString &message)
- {
- statusBar()->showMessage(message, 5000);
- ui_->outputList->addItem(tr("页面导航失败:%1").arg(message));
- });
- ui_->editorTabWidget->setTabVisible(2, false);
- }
-
- void MainWindow::configureProjectTree()
- {
- ui_->projectTree->setContextMenuPolicy(Qt::ActionsContextMenu);
-
- add_page_action_ = new QAction(tr("新建页面"), this);
- add_page_action_->setObjectName(QStringLiteral("addPageAction"));
- add_logic_action_ = new QAction(tr("新建控制逻辑"), this);
- add_logic_action_->setObjectName(QStringLiteral("addLogicAction"));
- rename_project_item_action_ = new QAction(tr("重命名"), this);
- rename_project_item_action_->setObjectName(
- QStringLiteral("renameProjectItemAction"));
- delete_project_item_action_ = new QAction(tr("删除"), this);
- delete_project_item_action_->setObjectName(
- QStringLiteral("deleteProjectItemAction"));
- move_project_item_up_action_ = new QAction(tr("上移"), this);
- move_project_item_up_action_->setObjectName(
- QStringLiteral("moveProjectItemUpAction"));
- move_project_item_down_action_ = new QAction(tr("下移"), this);
- move_project_item_down_action_->setObjectName(
- QStringLiteral("moveProjectItemDownAction"));
- set_initial_page_action_ = new QAction(tr("设为初始页面"), this);
- set_initial_page_action_->setObjectName(
- QStringLiteral("setInitialPageAction"));
- toggle_logic_enabled_action_ = new QAction(tr("禁用控制逻辑"), this);
- toggle_logic_enabled_action_->setObjectName(
- QStringLiteral("toggleLogicEnabledAction"));
-
- ui_->projectTree->addActions({
- add_page_action_,
- add_logic_action_,
- rename_project_item_action_,
- delete_project_item_action_,
- move_project_item_up_action_,
- move_project_item_down_action_,
- set_initial_page_action_,
- toggle_logic_enabled_action_});
-
- connect(add_page_action_, &QAction::triggered, this, &MainWindow::addPage);
- connect(add_logic_action_, &QAction::triggered, this, &MainWindow::addLogic);
- connect(rename_project_item_action_, &QAction::triggered,
- this, &MainWindow::renameProjectItem);
- connect(delete_project_item_action_, &QAction::triggered,
- this, &MainWindow::deleteProjectItem);
- connect(move_project_item_up_action_, &QAction::triggered,
- this, [this] { moveProjectItem(-1); });
- connect(move_project_item_down_action_, &QAction::triggered,
- this, [this] { moveProjectItem(1); });
- connect(set_initial_page_action_, &QAction::triggered,
- this, &MainWindow::setSelectedPageAsInitial);
- connect(toggle_logic_enabled_action_, &QAction::triggered,
- this, &MainWindow::toggleSelectedLogicEnabled);
- connect(ui_->projectTree, &QTreeWidget::currentItemChanged,
- this,
- [this](QTreeWidgetItem *, QTreeWidgetItem *)
- {
- handleProjectTreeSelection();
- });
- updateProjectTreeActions();
- }
-
- void MainWindow::configurePropertyEditor()
- {
- ui_->bindingAreaComboBox->setItemData(0, -1);
- ui_->bindingAreaComboBox->setItemData(1, 0);
- ui_->bindingAreaComboBox->setItemData(2, 1);
- ui_->buttonOperationComboBox->setItemData(
- 0, static_cast<int>(HmiButtonOperation::SetOn));
- ui_->buttonOperationComboBox->setItemData(
- 1, static_cast<int>(HmiButtonOperation::SetOff));
- ui_->buttonOperationComboBox->setItemData(
- 2, static_cast<int>(HmiButtonOperation::Toggle));
- ui_->buttonOperationComboBox->setItemData(
- 3, static_cast<int>(HmiButtonOperation::MomentaryOn));
- for (int index = 0; index < ui_->logicComparisonComboBox->count(); ++index)
- {
- ui_->logicComparisonComboBox->setItemData(index, index);
- }
- connect(ui_->applyPropertiesButton, &QPushButton::clicked,
- this, &MainWindow::applySelectedControlProperties);
- connect(ui_->applyLogicPropertiesButton, &QPushButton::clicked,
- this, &MainWindow::applySelectedLogicNodeProperties);
- ui_->targetPageLabel->setVisible(false);
- ui_->targetPageComboBox->setVisible(false);
- showControlProperties({});
- }
-
- void MainWindow::configurePlcConnection()
- {
- connect(ui_->configurePlcAction, &QAction::triggered, this, &MainWindow::connectPlc);
- connect(ui_->disconnectPlcAction, &QAction::triggered, this, &MainWindow::disconnectPlc);
- }
-
- void MainWindow::configureAlarms()
- {
- connect(ui_->configureAlarmsAction, &QAction::triggered,
- this,
- [this]
- {
- AlarmConfigurationDialog dialog(alarm_editor_service_, this);
- dialog.exec();
- refreshProjectUi();
- });
- }
-
- void MainWindow::refreshProjectUi()
- {
- // 仅在当前对象消失时回退,普通刷新必须保留用户的会话选择
- if (hmi_editor_service_.findPage(current_hmi_page_id_) == nullptr)
- {
- const std::string &initial_id = project_service_.project().initialHmiPageId;
- current_hmi_page_id_ = hmi_editor_service_.findPage(initial_id) != nullptr
- ? initial_id : hmi_editor_service_.firstPageId();
- selected_control_id_.clear();
- }
- if (logic_editor_service_.findLogic(current_logic_id_) == nullptr)
- {
- current_logic_id_ = logic_editor_service_.firstLogicId();
- selected_logic_node_id_.clear();
- }
-
- if (hmi_editor_widget_ != nullptr)
- {
- hmi_editor_widget_->setPageId(current_hmi_page_id_);
- }
- const HmiPage *page = hmi_editor_service_.findPage(current_hmi_page_id_);
- if (logic_editor_widget_ != nullptr)
- {
- logic_editor_widget_->setLogicId(current_logic_id_);
- }
- if (runtime_monitor_widget_ != nullptr
- && runtime_mode_service_.mode() != ApplicationMode::Editing)
- {
- runtime_monitor_widget_->setProjectObjects(
- hmi_navigation_service_->currentPageId(), current_logic_id_);
- }
- runtime_mode_service_.refreshPlcPollAddresses();
-
- const QSignalBlocker blocker(ui_->projectTree);
- ui_->projectTree->clear();
- auto *hmi_root = new QTreeWidgetItem(ui_->projectTree, {tr("HMI 页面")});
- hmi_root->setData(0, kProjectTreeKindRole,
- static_cast<int>(ProjectTreeItemKind::Root));
- QTreeWidgetItem *selected_tree_item = nullptr;
- for (const HmiPage &candidate : project_service_.project().hmiPages)
- {
- const QString initial_suffix = candidate.id
- == project_service_.project().initialHmiPageId
- ? tr("(初始)") : QString{};
- auto *item = new QTreeWidgetItem(
- hmi_root, {fromUtf8(candidate.name) + initial_suffix});
- item->setData(0, kProjectTreeKindRole,
- static_cast<int>(ProjectTreeItemKind::HmiPage));
- item->setData(0, kProjectTreeIdRole, fromUtf8(candidate.id));
- if (candidate.id == current_hmi_page_id_
- && ui_->editorTabWidget->currentIndex() == 0)
- {
- selected_tree_item = item;
- }
- }
- if (page != nullptr)
- {
- ui_->hmiPageTitleLabel->setText(fromUtf8(page->name));
- ui_->hmiPageSizeLabel->setText(
- tr("%1 x %2").arg(page->width).arg(page->height));
- }
- else
- {
- ui_->hmiPageTitleLabel->setText(tr("尚未创建 HMI 页面"));
- ui_->hmiPageSizeLabel->clear();
- }
- auto *logic_root = new QTreeWidgetItem(ui_->projectTree, {tr("控制逻辑")});
- logic_root->setData(0, kProjectTreeKindRole,
- static_cast<int>(ProjectTreeItemKind::Root));
- for (const ControlLogic &logic : project_service_.project().controlLogics)
- {
- const QString suffix = logic.enabled ? QString{} : tr("(已禁用)");
- auto *item = new QTreeWidgetItem(
- logic_root, {fromUtf8(logic.name) + suffix});
- item->setData(0, kProjectTreeKindRole,
- static_cast<int>(ProjectTreeItemKind::ControlLogic));
- item->setData(0, kProjectTreeIdRole, fromUtf8(logic.id));
- if (logic.id == current_logic_id_
- && ui_->editorTabWidget->currentIndex() == 1)
- {
- selected_tree_item = item;
- }
- }
- ui_->projectTree->expandAll();
- if (selected_tree_item != nullptr)
- {
- ui_->projectTree->setCurrentItem(selected_tree_item);
- }
- updateProjectTreeActions();
- }
-
- void MainWindow::handleProjectTreeSelection()
- {
- QTreeWidgetItem *item = ui_->projectTree->currentItem();
- if (item == nullptr)
- {
- updateProjectTreeActions();
- return;
- }
- const auto kind = static_cast<ProjectTreeItemKind>(
- item->data(0, kProjectTreeKindRole).toInt());
- const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString());
- if (kind == ProjectTreeItemKind::HmiPage
- && hmi_editor_service_.findPage(id) != nullptr)
- {
- current_hmi_page_id_ = id;
- selected_control_id_.clear();
- hmi_editor_widget_->setPageId(id);
- hmi_editor_widget_->reloadPage();
- ui_->editorTabWidget->setCurrentWidget(ui_->hmiEditorTab);
- showControlProperties({});
- const HmiPage *page = hmi_editor_service_.findPage(id);
- ui_->hmiPageTitleLabel->setText(fromUtf8(page->name));
- ui_->hmiPageSizeLabel->setText(
- tr("%1 x %2").arg(page->width).arg(page->height));
- }
- else if (kind == ProjectTreeItemKind::ControlLogic
- && logic_editor_service_.findLogic(id) != nullptr)
- {
- current_logic_id_ = id;
- selected_logic_node_id_.clear();
- logic_editor_widget_->setLogicId(id);
- logic_editor_widget_->reloadLogic();
- ui_->editorTabWidget->setCurrentWidget(ui_->logicEditorTab);
- showLogicNodeProperties({});
- }
- updateProjectTreeActions();
- }
-
- void MainWindow::updateProjectTreeActions()
- {
- if (add_page_action_ == nullptr)
- {
- return;
- }
- QTreeWidgetItem *item = ui_->projectTree->currentItem();
- const auto kind = item == nullptr ? ProjectTreeItemKind::Root
- : static_cast<ProjectTreeItemKind>(
- item->data(0, kProjectTreeKindRole).toInt());
- const bool page_selected = kind == ProjectTreeItemKind::HmiPage;
- const bool logic_selected = kind == ProjectTreeItemKind::ControlLogic;
- const bool editable = runtime_mode_service_.policy().allowsProjectEditing;
- add_page_action_->setEnabled(editable);
- add_logic_action_->setEnabled(editable);
- rename_project_item_action_->setEnabled(
- editable && (page_selected || logic_selected));
- delete_project_item_action_->setEnabled(
- editable && (page_selected || logic_selected));
- move_project_item_up_action_->setEnabled(
- editable && (page_selected || logic_selected));
- move_project_item_down_action_->setEnabled(
- editable && (page_selected || logic_selected));
- set_initial_page_action_->setEnabled(editable && page_selected);
- toggle_logic_enabled_action_->setEnabled(editable && logic_selected);
- if (logic_selected)
- {
- const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString());
- const ControlLogic *logic = logic_editor_service_.findLogic(id);
- toggle_logic_enabled_action_->setText(
- logic != nullptr && logic->enabled
- ? tr("禁用控制逻辑") : tr("启用控制逻辑"));
- }
- }
-
- void MainWindow::addPage()
- {
- const QString name = QInputDialog::getText(
- this, tr("新建页面"), tr("页面名称"));
- if (name.isEmpty())
- {
- return;
- }
- const HmiEditorResult result = hmi_editor_service_.addPage(toUtf8(name));
- if (!result.succeeded)
- {
- showProjectResult(tr("新建页面"), fromUtf8(result.message), false);
- return;
- }
- current_hmi_page_id_ = result.id;
- selected_control_id_.clear();
- ui_->editorTabWidget->setCurrentWidget(ui_->hmiEditorTab);
- refreshProjectUi();
- hmi_editor_widget_->reloadPage();
- }
-
- void MainWindow::addLogic()
- {
- const QString name = QInputDialog::getText(
- this, tr("新建控制逻辑"), tr("逻辑名称"));
- if (name.isEmpty())
- {
- return;
- }
- const LogicEditorResult result = logic_editor_service_.addLogic(toUtf8(name));
- if (!result.succeeded)
- {
- showProjectResult(tr("新建控制逻辑"), fromUtf8(result.message), false);
- return;
- }
- current_logic_id_ = result.id;
- selected_logic_node_id_.clear();
- ui_->editorTabWidget->setCurrentWidget(ui_->logicEditorTab);
- refreshProjectUi();
- logic_editor_widget_->reloadLogic();
- }
-
- void MainWindow::renameProjectItem()
- {
- QTreeWidgetItem *item = ui_->projectTree->currentItem();
- if (item == nullptr)
- {
- return;
- }
- const auto kind = static_cast<ProjectTreeItemKind>(
- item->data(0, kProjectTreeKindRole).toInt());
- const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString());
- QString current_name;
- if (kind == ProjectTreeItemKind::HmiPage)
- {
- const HmiPage *page = hmi_editor_service_.findPage(id);
- current_name = page == nullptr ? QString{} : fromUtf8(page->name);
- }
- else if (kind == ProjectTreeItemKind::ControlLogic)
- {
- const ControlLogic *logic = logic_editor_service_.findLogic(id);
- current_name = logic == nullptr ? QString{} : fromUtf8(logic->name);
- }
- else
- {
- return;
- }
- bool accepted = false;
- const QString name = QInputDialog::getText(
- this, tr("重命名"), tr("新名称"), QLineEdit::Normal,
- current_name, &accepted);
- if (!accepted)
- {
- return;
- }
- if (kind == ProjectTreeItemKind::HmiPage)
- {
- const HmiEditorResult result = hmi_editor_service_.renamePage(id, toUtf8(name));
- if (!result.succeeded)
- {
- showProjectResult(tr("重命名页面"), fromUtf8(result.message), false);
- return;
- }
- }
- else
- {
- const LogicEditorResult result = logic_editor_service_.renameLogic(id, toUtf8(name));
- if (!result.succeeded)
- {
- showProjectResult(tr("重命名控制逻辑"), fromUtf8(result.message), false);
- return;
- }
- }
- refreshProjectUi();
- }
-
- void MainWindow::deleteProjectItem()
- {
- QTreeWidgetItem *item = ui_->projectTree->currentItem();
- if (item == nullptr)
- {
- return;
- }
- const auto kind = static_cast<ProjectTreeItemKind>(
- item->data(0, kProjectTreeKindRole).toInt());
- const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString());
- if (kind == ProjectTreeItemKind::HmiPage)
- {
- const HmiEditorResult result = hmi_editor_service_.removePage(id);
- if (!result.succeeded)
- {
- showProjectResult(tr("删除页面"), fromUtf8(result.message), false);
- return;
- }
- }
- else if (kind == ProjectTreeItemKind::ControlLogic)
- {
- const LogicEditorResult result = logic_editor_service_.removeLogic(id);
- if (!result.succeeded)
- {
- showProjectResult(tr("删除控制逻辑"), fromUtf8(result.message), false);
- return;
- }
- }
- else
- {
- return;
- }
- refreshProjectUi();
- showControlProperties({});
- showLogicNodeProperties({});
- }
-
- void MainWindow::moveProjectItem(int offset)
- {
- QTreeWidgetItem *item = ui_->projectTree->currentItem();
- if (item == nullptr)
- {
- return;
- }
- const auto kind = static_cast<ProjectTreeItemKind>(
- item->data(0, kProjectTreeKindRole).toInt());
- const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString());
- if (kind == ProjectTreeItemKind::HmiPage)
- {
- const HmiEditorResult result = hmi_editor_service_.movePage(id, offset);
- if (!result.succeeded)
- {
- statusBar()->showMessage(fromUtf8(result.message), 4000);
- return;
- }
- }
- else if (kind == ProjectTreeItemKind::ControlLogic)
- {
- const LogicEditorResult result = logic_editor_service_.moveLogic(id, offset);
- if (!result.succeeded)
- {
- statusBar()->showMessage(fromUtf8(result.message), 4000);
- return;
- }
- }
- refreshProjectUi();
- }
-
- void MainWindow::setSelectedPageAsInitial()
- {
- QTreeWidgetItem *item = ui_->projectTree->currentItem();
- if (item == nullptr
- || static_cast<ProjectTreeItemKind>(
- item->data(0, kProjectTreeKindRole).toInt())
- != ProjectTreeItemKind::HmiPage)
- {
- return;
- }
- const HmiEditorResult result = hmi_editor_service_.setInitialPage(
- toUtf8(item->data(0, kProjectTreeIdRole).toString()));
- if (!result.succeeded)
- {
- showProjectResult(tr("设置初始页面"), fromUtf8(result.message), false);
- return;
- }
- refreshProjectUi();
- }
-
- void MainWindow::toggleSelectedLogicEnabled()
- {
- QTreeWidgetItem *item = ui_->projectTree->currentItem();
- if (item == nullptr
- || static_cast<ProjectTreeItemKind>(
- item->data(0, kProjectTreeKindRole).toInt())
- != ProjectTreeItemKind::ControlLogic)
- {
- return;
- }
- const std::string id = toUtf8(item->data(0, kProjectTreeIdRole).toString());
- const ControlLogic *logic = logic_editor_service_.findLogic(id);
- if (logic == nullptr)
- {
- return;
- }
- const LogicEditorResult result = logic_editor_service_.setLogicEnabled(
- id, !logic->enabled);
- if (!result.succeeded)
- {
- showProjectResult(tr("启停控制逻辑"), fromUtf8(result.message), false);
- return;
- }
- refreshProjectUi();
- }
-
- void MainWindow::connectPlc()
- {
- PlcConnectionDialog dialog(plc_configuration_, this);
- if (dialog.exec() != QDialog::Accepted)
- {
- return;
- }
- plc_configuration_ = dialog.configuration();
- if (plc_configuration_.portName.empty())
- {
- showProjectResult(tr("连接 PLC"), tr("请选择或输入串口端口"), false);
- return;
- }
- const PlcCommunicationResult result = runtime_mode_service_.connectPlc(
- plc_configuration_);
- if (!result.succeeded)
- {
- const QString message = fromUtf8(result.message);
- if (result.message == runtime_mode_service_.plcError())
- {
- statusBar()->showMessage(message, 5000);
- QMessageBox::warning(this, tr("连接 PLC"), message);
- }
- else
- {
- showProjectResult(tr("连接 PLC"), message, false);
- }
- return;
- }
- statusBar()->showMessage(tr("PLC 正在连接,等待首次 M/D 读取"), 5000);
- }
-
- void MainWindow::disconnectPlc()
- {
- runtime_mode_service_.disconnectPlc();
- }
-
- 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加载控件属性到右侧属性面板
- void MainWindow::showControlProperties(const std::string &control_id)
- {
- ui_->propertyStack->setCurrentWidget(ui_->hmiPropertiesPage);
- // 更新窗口全局状态:记录当前选中控件ID
- selected_control_id_ = control_id;
-
- // 在第一页画面中,根据ID查找目标控件,获取只读模型指针
- const HmiControl *control = hmi_editor_service_.findControl(
- current_hmi_page_id_, control_id);
- // 判断是否查询到有效控件
- const bool has_control = control != nullptr;
-
- // 更新属性面板顶部状态标签,显示选中控件ID或提示未选择
- ui_->selectionValueLabel->setText(
- has_control ? fromUtf8(control->id)
- + (control->isConfigured() ? QString{}
- : control->type == HmiControlType::PageJump
- ? tr("(未配置)") : tr("(未绑定)"))
- : tr("未选择"));
-
- // 批量控制所有属性编辑组件可用性:无选中控件时整体置灰,禁止编辑
- for (QWidget *widget : {static_cast<QWidget *>(ui_->controlIdEdit),
- static_cast<QWidget *>(ui_->controlTextEdit),
- static_cast<QWidget *>(ui_->controlXSpinBox),
- static_cast<QWidget *>(ui_->controlYSpinBox),
- static_cast<QWidget *>(ui_->controlWidthSpinBox),
- static_cast<QWidget *>(ui_->controlHeightSpinBox),
- static_cast<QWidget *>(ui_->bindingAreaComboBox),
- static_cast<QWidget *>(ui_->bindingIndexSpinBox),
- static_cast<QWidget *>(ui_->targetPageComboBox),
- static_cast<QWidget *>(ui_->buttonOperationComboBox),
- static_cast<QWidget *>(ui_->applyPropertiesButton)})
- {
- widget->setEnabled(has_control);
- }
-
- // 未选中有效控件,无需填充属性,直接退出
- if (!has_control)
- {
- ui_->bindingAreaLabel->setVisible(false);
- ui_->bindingAreaComboBox->setVisible(false);
- ui_->bindingIndexLabel->setVisible(false);
- ui_->bindingIndexSpinBox->setVisible(false);
- ui_->targetPageLabel->setVisible(false);
- ui_->targetPageComboBox->setVisible(false);
- ui_->buttonOperationLabel->setVisible(false);
- ui_->buttonOperationComboBox->setVisible(false);
- return;
- }
-
- // 将控件基础信息填充至UI输入框
- ui_->controlIdEdit->setText(fromUtf8(control->id));
- ui_->controlTextEdit->setText(fromUtf8(control->text));
- // 控件位置坐标
- ui_->controlXSpinBox->setValue(control->bounds.x);
- ui_->controlYSpinBox->setValue(control->bounds.y);
- // 控件宽高尺寸
- ui_->controlWidthSpinBox->setValue(control->bounds.width);
- ui_->controlHeightSpinBox->setValue(control->bounds.height);
-
- // 根据控件类型获取默认寄存器区域:按钮/指示灯默认M区;数值类控件默认D区
- // 未绑定必须保留明确状态,避免属性编辑把显示默认值误提交为 M0/D0
- ui_->bindingAreaComboBox->setCurrentIndex(!control->binding.has_value() ? 0
- : control->binding->area() == RegisterArea::M ? 1 : 2);
- ui_->bindingIndexSpinBox->setValue(
- control->binding.has_value() ? control->binding->index() : 0);
- const bool has_binding = control->type != HmiControlType::Label
- && control->type != HmiControlType::PageJump
- && control->type != HmiControlType::AlarmList;
- ui_->bindingAreaLabel->setVisible(has_binding);
- ui_->bindingAreaComboBox->setVisible(has_binding);
- ui_->bindingIndexLabel->setVisible(has_binding);
- ui_->bindingIndexSpinBox->setVisible(has_binding);
- const bool is_button = control->type == HmiControlType::Button;
- ui_->buttonOperationLabel->setVisible(is_button);
- ui_->buttonOperationComboBox->setVisible(is_button);
- ui_->buttonOperationComboBox->setEnabled(is_button);
- ui_->buttonOperationComboBox->setCurrentIndex(
- ui_->buttonOperationComboBox->findData(
- static_cast<int>(control->buttonOperation)));
- const bool is_page_jump = control->type == HmiControlType::PageJump;
- ui_->targetPageLabel->setVisible(is_page_jump);
- ui_->targetPageComboBox->setVisible(is_page_jump);
- ui_->targetPageComboBox->setEnabled(is_page_jump);
- ui_->targetPageComboBox->clear();
- if (is_page_jump)
- {
- for (const HmiPage &page : project_service_.project().hmiPages)
- {
- ui_->targetPageComboBox->addItem(
- fromUtf8(page.name), fromUtf8(page.id));
- }
- const std::string target_id = control->pageJump.has_value()
- ? control->pageJump->targetPageId : std::string{};
- ui_->targetPageComboBox->setCurrentIndex(
- ui_->targetPageComboBox->findData(fromUtf8(target_id)));
- }
- }
-
- void MainWindow::showLogicNodeProperties(const std::string &node_id)
- {
- ui_->propertyStack->setCurrentWidget(ui_->logicPropertiesPage);
- selected_logic_node_id_ = node_id;
- const LogicNode *node = logic_editor_service_.findNode(
- current_logic_id_, node_id);
- const bool has_node = node != nullptr;
- ui_->selectionValueLabel->setText(
- has_node ? fromUtf8(node->id) : tr("未选择"));
- for (QWidget *widget : {static_cast<QWidget *>(ui_->logicNodeIdEdit),
- static_cast<QWidget *>(ui_->logicAddressSpinBox),
- static_cast<QWidget *>(ui_->logicModeComboBox),
- static_cast<QWidget *>(ui_->logicComparisonComboBox),
- static_cast<QWidget *>(ui_->logicValueSpinBox),
- static_cast<QWidget *>(ui_->applyLogicPropertiesButton)})
- {
- widget->setEnabled(has_node);
- }
- ui_->logicNodeTypeLabel->setEnabled(has_node);
- ui_->logicAddressAreaLabel->setEnabled(has_node);
- if (!has_node)
- {
- ui_->logicNodeIdEdit->clear();
- ui_->logicNodeTypeLabel->clear();
- ui_->logicAddressAreaLabel->clear();
- ui_->logicModeComboBox->clear();
- return;
- }
-
- ui_->logicNodeIdEdit->setText(fromUtf8(node->id));
- ui_->logicModeComboBox->clear();
- if (const auto *contact = std::get_if<ContactNodeConfig>(&node->config))
- {
- ui_->logicNodeTypeLabel->setText(tr("触点"));
- ui_->logicAddressAreaLabel->setText(QStringLiteral("M"));
- ui_->logicAddressSpinBox->setValue(contact->address.index());
- ui_->logicModeComboBox->addItem(
- tr("常开"), static_cast<int>(ContactMode::NormallyOpen));
- ui_->logicModeComboBox->addItem(
- tr("常闭"), static_cast<int>(ContactMode::NormallyClosed));
- ui_->logicModeComboBox->setCurrentIndex(
- ui_->logicModeComboBox->findData(static_cast<int>(contact->mode)));
- ui_->logicModeComboBox->setEnabled(true);
- ui_->logicComparisonComboBox->setEnabled(false);
- ui_->logicValueSpinBox->setEnabled(false);
- }
- else if (const auto *coil = std::get_if<CoilNodeConfig>(&node->config))
- {
- ui_->logicNodeTypeLabel->setText(tr("线圈"));
- ui_->logicAddressAreaLabel->setText(QStringLiteral("M"));
- ui_->logicAddressSpinBox->setValue(coil->address.index());
- ui_->logicModeComboBox->addItem(
- tr("普通"), static_cast<int>(CoilMode::Normal));
- ui_->logicModeComboBox->addItem(
- tr("置位"), static_cast<int>(CoilMode::Set));
- ui_->logicModeComboBox->addItem(
- tr("复位"), static_cast<int>(CoilMode::Reset));
- ui_->logicModeComboBox->setCurrentIndex(
- ui_->logicModeComboBox->findData(static_cast<int>(coil->mode)));
- ui_->logicModeComboBox->setEnabled(true);
- ui_->logicComparisonComboBox->setEnabled(false);
- ui_->logicValueSpinBox->setEnabled(false);
- }
- else
- {
- const auto &compare = std::get<CompareNodeConfig>(node->config);
- ui_->logicNodeTypeLabel->setText(tr("D 值比较"));
- ui_->logicAddressAreaLabel->setText(QStringLiteral("D"));
- ui_->logicAddressSpinBox->setValue(compare.address.index());
- ui_->logicModeComboBox->addItem(tr("不适用"));
- ui_->logicModeComboBox->setEnabled(false);
- ui_->logicComparisonComboBox->setEnabled(true);
- ui_->logicComparisonComboBox->setCurrentIndex(
- ui_->logicComparisonComboBox->findData(
- static_cast<int>(compare.comparison)));
- ui_->logicValueSpinBox->setEnabled(true);
- ui_->logicValueSpinBox->setValue(compare.value);
- }
- if (!node->configured)
- {
- ui_->logicNodeTypeLabel->setText(tr("待配置:%1").arg(
- ui_->logicNodeTypeLabel->text()));
- }
- }
-
- void MainWindow::addHmiControl(HmiControlType type)
- {
- const HmiEditorResult result = hmi_editor_service_.addControl(
- current_hmi_page_id_, type);
- if (!result.succeeded)
- {
- showProjectResult(tr("添加控件"), fromUtf8(result.message), false);
- return;
- }
- hmi_editor_widget_->reloadPage();
- hmi_editor_widget_->selectControl(result.id);
- showControlProperties(result.id);
- refreshProjectUi();
- statusBar()->showMessage(tr("已添加%1").arg(controlTypeText(type)), 3000);
- }
-
- void MainWindow::deleteSelectedControl()
- {
- if (selected_control_id_.empty())
- {
- return;
- }
- const HmiEditorResult result = hmi_editor_service_.removeControl(
- current_hmi_page_id_, selected_control_id_);
- if (!result.succeeded)
- {
- showProjectResult(tr("删除控件"), fromUtf8(result.message), false);
- return;
- }
- selected_control_id_.clear();
- hmi_editor_widget_->reloadPage();
- showControlProperties({});
- refreshProjectUi();
- }
-
- void MainWindow::applySelectedControlProperties()
- {
- const HmiControl *old_control = hmi_editor_service_.findControl(
- current_hmi_page_id_, selected_control_id_);
- if (old_control == nullptr)
- {
- return;
- }
- // 从现有控件复制不可见属性,避免属性面板覆盖扩展配置
- HmiControl control = *old_control;
- control.id = toUtf8(ui_->controlIdEdit->text());
- control.text = toUtf8(ui_->controlTextEdit->text());
- control.bounds.x = ui_->controlXSpinBox->value();
- control.bounds.y = ui_->controlYSpinBox->value();
- control.bounds.width = ui_->controlWidthSpinBox->value();
- control.bounds.height = ui_->controlHeightSpinBox->value();
- if (control.type == HmiControlType::Label
- || control.type == HmiControlType::PageJump
- || control.type == HmiControlType::AlarmList)
- {
- control.binding.reset();
- }
- else
- {
- const int binding_area = ui_->bindingAreaComboBox->currentData().toInt();
- if (binding_area < 0)
- {
- control.binding.reset();
- }
- else
- {
- const RegisterArea area = binding_area == 0
- ? RegisterArea::M : RegisterArea::D;
- control.binding = RegisterAddress{
- area, ui_->bindingIndexSpinBox->value()};
- }
- }
- if (control.type == HmiControlType::Button)
- {
- control.buttonOperation = static_cast<HmiButtonOperation>(
- ui_->buttonOperationComboBox->currentData().toInt());
- }
- if (control.type == HmiControlType::PageJump)
- {
- control.pageJump = HmiPageJumpConfig{
- toUtf8(ui_->targetPageComboBox->currentData().toString())};
- }
- const HmiEditorResult result = hmi_editor_service_.updateControl(
- current_hmi_page_id_, selected_control_id_, control);
- if (!result.succeeded)
- {
- showProjectResult(tr("应用属性"), fromUtf8(result.message), false);
- return;
- }
- selected_control_id_ = result.id;
- hmi_editor_widget_->reloadPage();
- hmi_editor_widget_->selectControl(selected_control_id_);
- refreshProjectUi();
- statusBar()->showMessage(tr("控件属性已更新"), 3000);
- }
-
- void MainWindow::addLogicCondition(const LogicNodeConfig &config)
- {
- const LogicEditorResult result = logic_editor_widget_->addCondition(config);
- if (!result.succeeded)
- {
- showProjectResult(tr("添加逻辑条件"), fromUtf8(result.message), false);
- return;
- }
- selected_logic_node_id_ = result.id;
- showLogicNodeProperties(result.id);
- refreshProjectUi();
- statusBar()->showMessage(tr("已添加逻辑条件"), 3000);
- }
-
- void MainWindow::addLogicParallelBranch(const LogicNodeConfig &config)
- {
- const LogicEditorResult result = logic_editor_widget_->addParallelBranch(config);
- if (!result.succeeded)
- {
- showProjectResult(tr("建立并联支路"), fromUtf8(result.message), false);
- return;
- }
- selected_logic_node_id_ = result.id;
- showLogicNodeProperties(result.id);
- refreshProjectUi();
- statusBar()->showMessage(tr("已建立并联支路,请配置新触点"), 3000);
- }
-
- void MainWindow::setLogicOutput(const LogicNodeConfig &config)
- {
- const LogicEditorResult result = logic_editor_widget_->setOutput(config);
- if (!result.succeeded)
- {
- showProjectResult(tr("设置输出线圈"), fromUtf8(result.message), false);
- return;
- }
- selected_logic_node_id_ = result.id;
- showLogicNodeProperties(result.id);
- refreshProjectUi();
- statusBar()->showMessage(tr("输出线圈已设置"), 3000);
- }
-
- void MainWindow::addLogicRung()
- {
- const LogicEditorResult result = logic_editor_widget_->addRung();
- if (!result.succeeded)
- {
- showProjectResult(tr("新建网络"), fromUtf8(result.message), false);
- return;
- }
- selected_logic_node_id_.clear();
- showLogicNodeProperties({});
- refreshProjectUi();
- statusBar()->showMessage(tr("已新建网络"), 3000);
- }
-
- void MainWindow::deleteSelectedLogicObject()
- {
- const LogicEditorResult result = logic_editor_widget_->deleteSelected();
- if (!result.succeeded)
- {
- return;
- }
- selected_logic_node_id_.clear();
- showLogicNodeProperties({});
- refreshProjectUi();
- }
-
- void MainWindow::applySelectedLogicNodeProperties()
- {
- const LogicNode *node = logic_editor_service_.findNode(
- current_logic_id_, selected_logic_node_id_);
- if (node == nullptr)
- {
- return;
- }
-
- LogicNodeConfig config = node->config;
- if (std::holds_alternative<ContactNodeConfig>(config))
- {
- config = ContactNodeConfig{
- RegisterAddress{RegisterArea::M, ui_->logicAddressSpinBox->value()},
- static_cast<ContactMode>(ui_->logicModeComboBox->currentData().toInt())};
- }
- else if (std::holds_alternative<CoilNodeConfig>(config))
- {
- config = CoilNodeConfig{
- RegisterAddress{RegisterArea::M, ui_->logicAddressSpinBox->value()},
- static_cast<CoilMode>(ui_->logicModeComboBox->currentData().toInt())};
- }
- else
- {
- config = CompareNodeConfig{
- RegisterAddress{RegisterArea::D, ui_->logicAddressSpinBox->value()},
- static_cast<ComparisonOperator>(
- ui_->logicComparisonComboBox->currentData().toInt()),
- static_cast<std::int16_t>(ui_->logicValueSpinBox->value())};
- }
-
- const LogicEditorResult result = logic_editor_service_.updateNodeConfig(
- current_logic_id_, selected_logic_node_id_, config);
- if (!result.succeeded)
- {
- showProjectResult(tr("应用逻辑属性"), fromUtf8(result.message), false);
- return;
- }
- logic_editor_widget_->reloadLogic();
- logic_editor_widget_->selectNode(selected_logic_node_id_);
- showLogicNodeProperties(selected_logic_node_id_);
- refreshProjectUi();
- statusBar()->showMessage(tr("逻辑节点属性已更新,节点已配置"), 3000);
- }
-
- void MainWindow::createNewProject()
- {
- const QString name = QInputDialog::getText(
- this, tr("新建工程"), tr("工程名称"));
- if (name.isEmpty())
- {
- return;
- }
- const ProjectOperationResult result = project_service_.createNewProject(toUtf8(name));
- if (!result.succeeded)
- {
- showProjectResult(tr("新建工程"), fromUtf8(result.message), false);
- return;
- }
- hmi_editor_service_.ensureDefaultPage();
- logic_editor_service_.ensureDefaultLogic();
- selected_control_id_.clear();
- selected_logic_node_id_.clear();
- current_hmi_page_id_ = project_service_.project().initialHmiPageId;
- current_logic_id_ = logic_editor_service_.firstLogicId();
- refreshProjectUi();
- hmi_editor_widget_->reloadPage();
- logic_editor_widget_->reloadLogic();
- showControlProperties({});
- statusBar()->showMessage(tr("已创建新工程"), 3000);
- }
-
- void MainWindow::saveProject()
- {
- const ProjectOperationResult result = project_service_.save();
- if (!result.succeeded)
- {
- saveProjectAs();
- return;
- }
- showProjectResult(tr("保存工程"), tr("工程已保存"), true);
- }
-
- void MainWindow::saveProjectAs()
- {
- const QString path = QFileDialog::getSaveFileName(
- this, tr("工程另存为"), {}, tr("工程文件 (*.json)"));
- if (path.isEmpty())
- {
- return;
- }
- const ProjectOperationResult result = project_service_.saveAs(toUtf8(path));
- showProjectResult(
- tr("保存工程"),
- result.succeeded ? tr("工程已保存") : fromUtf8(result.message),
- result.succeeded);
- }
-
- void MainWindow::loadProject()
- {
- const QString path = QFileDialog::getOpenFileName(
- this, tr("加载工程"), {}, tr("工程文件 (*.json)"));
- if (path.isEmpty())
- {
- return;
- }
- const ProjectOperationResult result = project_service_.load(toUtf8(path));
- if (!result.succeeded)
- {
- showProjectResult(tr("加载工程"), fromUtf8(result.message), false);
- return;
- }
- selected_control_id_.clear();
- selected_logic_node_id_.clear();
- current_hmi_page_id_ = project_service_.project().initialHmiPageId;
- current_logic_id_ = logic_editor_service_.firstLogicId();
- refreshProjectUi();
- hmi_editor_widget_->reloadPage();
- logic_editor_widget_->reloadLogic();
- showControlProperties({});
- showProjectResult(tr("加载工程"), tr("工程已加载"), true);
- }
-
- void MainWindow::showProjectResult(
- const QString &action, const QString &message, bool succeeded)
- {
- const QString output = action + QStringLiteral(": ") + message;
- statusBar()->showMessage(output, 5000);
- ui_->outputList->addItem(output);
- ui_->outputList->scrollToBottom();
- if (!succeeded)
- {
- QMessageBox::warning(this, action, message);
- }
- }
-
- void MainWindow::requestMode(ApplicationMode requested_mode)
- {
- // UI 仅转发模式意图,合法性由服务层和领域状态机决定
- ModeTransitionResult result;
- switch (requested_mode)
- {
- case ApplicationMode::Editing:
- {
- result = runtime_mode_service_.enterEditing();
- break;
- }
- case ApplicationMode::OfflineRunning:
- {
- result = runtime_mode_service_.enterOfflineRunning();
- break;
- }
- case ApplicationMode::OnlineRunning:
- {
- result = runtime_mode_service_.enterOnlineRunning();
- break;
- }
- default:
- {
- restoreCurrentModeAction();
- statusBar()->showMessage(tr("不支持的运行模式"), 5000);
- return;
- }
- }
- if (!result.succeeded)
- {
- restoreCurrentModeAction();
- QString message = transitionErrorText(result.error);
- if (result.error == ModeTransitionError::InitialPlcReadRequired)
- {
- message += tr(";当前状态:%1").arg(plcStatusText(runtime_mode_service_));
- }
- if (result.error == ModeTransitionError::SimulationStartFailed)
- {
- const LogicScanResult &error = runtime_mode_service_.simulationError();
- if (!error.message.empty())
- {
- message += QStringLiteral(": ") + fromUtf8(error.message);
- }
- }
- statusBar()->showMessage(message, 5000);
- return;
- }
- updateModeUi(tr("已进入%1").arg(modeText(runtime_mode_service_.mode())));
- }
-
- void MainWindow::updateModeUi(const QString &message)
- {
- const ApplicationMode mode = runtime_mode_service_.mode();
- const ModePolicy policy = runtime_mode_service_.policy();
- restoreCurrentModeAction();
- const bool running = mode != ApplicationMode::Editing;
- ui_->editorTabWidget->setTabVisible(2, running);
- if (running)
- {
- if (hmi_navigation_service_->currentPageId().empty())
- {
- const HmiNavigationResult navigation = hmi_navigation_service_->start();
- if (!navigation.succeeded)
- {
- statusBar()->showMessage(fromUtf8(navigation.message), 5000);
- }
- }
- runtime_monitor_widget_->setProjectObjects(
- hmi_navigation_service_->currentPageId(), current_logic_id_);
- runtime_monitor_widget_->setMode(mode, runtime_mode_service_.plcConnectionState());
- ui_->editorTabWidget->setCurrentWidget(ui_->runtimeMonitorTab);
- }
- else
- {
- hmi_navigation_service_->stop();
- runtime_monitor_widget_->setMode(
- mode, runtime_mode_service_.plcConnectionState());
- if (ui_->editorTabWidget->currentWidget() == ui_->runtimeMonitorTab)
- {
- ui_->editorTabWidget->setCurrentIndex(last_editing_tab_index_);
- }
- }
- // 将同一份模式策略同步到所有可编辑入口,避免只禁用部分操作
- ui_->projectDock->setEnabled(policy.allowsProjectEditing);
- ui_->propertiesDock->setEnabled(policy.allowsProjectEditing);
- hmi_editor_widget_->setEditingEnabled(policy.allowsProjectEditing);
- hmi_editor_widget_->setRuntimeActive(
- policy.usesVirtualRegisters || policy.usesPlcRegisters);
- logic_editor_widget_->setEditingEnabled(policy.allowsProjectEditing);
- if (mode == ApplicationMode::Editing)
- {
- alarm_service_.reset();
- logic_editor_widget_->clearRuntimeTrace();
- }
- ui_->hmiToolBar->setEnabled(policy.allowsProjectEditing);
- ui_->logicToolBar->setEnabled(policy.allowsProjectEditing);
- ui_->addButtonAction->setEnabled(policy.allowsProjectEditing);
- ui_->addIndicatorAction->setEnabled(policy.allowsProjectEditing);
- ui_->addNumericDisplayAction->setEnabled(policy.allowsProjectEditing);
- ui_->addNumericInputAction->setEnabled(policy.allowsProjectEditing);
- ui_->addLabelAction->setEnabled(policy.allowsProjectEditing);
- ui_->addPageJumpAction->setEnabled(policy.allowsProjectEditing);
- ui_->addAlarmListAction->setEnabled(policy.allowsProjectEditing);
- ui_->configureAlarmsAction->setEnabled(policy.allowsProjectEditing);
- ui_->deleteControlAction->setEnabled(policy.allowsProjectEditing);
- ui_->addNormallyOpenAction->setEnabled(policy.allowsProjectEditing);
- ui_->addNormallyClosedAction->setEnabled(policy.allowsProjectEditing);
- ui_->addNormalCoilAction->setEnabled(policy.allowsProjectEditing);
- ui_->addSetCoilAction->setEnabled(policy.allowsProjectEditing);
- ui_->addResetCoilAction->setEnabled(policy.allowsProjectEditing);
- ui_->addCompareAction->setEnabled(policy.allowsProjectEditing);
- ui_->deleteLogicAction->setEnabled(policy.allowsProjectEditing);
- ui_->newProjectAction->setEnabled(policy.allowsProjectEditing);
- ui_->saveProjectAction->setEnabled(policy.allowsProjectEditing);
- ui_->saveAsProjectAction->setEnabled(policy.allowsProjectEditing);
- ui_->loadProjectAction->setEnabled(policy.allowsProjectEditing);
- updateProjectTreeActions();
-
- mode_status_label_->setText(modeText(mode));
- if (mode == ApplicationMode::Editing)
- {
- mode_status_label_->setStyleSheet(QStringLiteral(
- "border: 1px solid #75a58a; background: #e3f0e8; color: #205c3b; padding: 2px 8px;"));
- }
- else if (mode == ApplicationMode::OfflineRunning)
- {
- mode_status_label_->setStyleSheet(QStringLiteral(
- "border: 1px solid #bd8739; background: #fff0d2; color: #744b0e; padding: 2px 8px;"));
- }
- else
- {
- mode_status_label_->setStyleSheet(QStringLiteral(
- "border: 1px solid #4b88a8; background: #deeff7; color: #174f6c; padding: 2px 8px;"));
- }
- register_status_label_->setText(
- policy.usesVirtualRegisters ? tr("数据源:虚拟 M/D")
- : policy.usesPlcRegisters ? tr("数据源:PLC 缓存") : tr("数据源:未启用"));
- const PlcConnectionState plc_state = runtime_mode_service_.plcConnectionState();
- const bool plc_configuration_available = plc_state == PlcConnectionState::Disconnected
- || plc_state == PlcConnectionState::Faulted;
- ui_->configurePlcAction->setEnabled(
- policy.allowsProjectEditing && plc_configuration_available);
- ui_->configurePlcAction->setText(
- plc_state == PlcConnectionState::Faulted ? tr("PLC 重新配置") : tr("PLC 配置"));
- ui_->configurePlcAction->setToolTip(
- plc_state == PlcConnectionState::Faulted
- ? tr("清理故障会话后重新配置并连接真实 PLC")
- : tr("配置参数并连接真实 PLC"));
- ui_->disconnectPlcAction->setEnabled(plc_state != PlcConnectionState::Disconnected);
- if (plc_state == PlcConnectionState::Connecting)
- {
- register_status_label_->setText(tr("PLC:正在连接"));
- }
- else if (plc_state == PlcConnectionState::Connected)
- {
- register_status_label_->setText(
- runtime_mode_service_.initialPlcReadCompleted()
- ? tr("PLC:已连接,首读完成") : tr("PLC:已连接,正在首读"));
- }
- else if (plc_state == PlcConnectionState::Recovering)
- {
- register_status_label_->setText(tr("PLC:通信已恢复,正在重新首读"));
- }
- else if (plc_state == PlcConnectionState::Faulted)
- {
- register_status_label_->setText(tr("PLC:通信故障"));
- }
- updateSimulationUi(false);
- statusBar()->showMessage(message, 4000);
- 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)
- {
- const bool offline = runtime_mode_service_.mode()
- == ApplicationMode::OfflineRunning;
- const bool online = runtime_mode_service_.mode()
- == ApplicationMode::OnlineRunning;
- const bool plc_connected = runtime_mode_service_.plcConnectionState()
- == PlcConnectionState::Connected;
- const SimulationState state = runtime_mode_service_.simulationState();
- hmi_editor_widget_->setRuntimeWriteEnabled(
- (online && plc_connected)
- || (offline && state == SimulationState::Running));
- if (runtime_monitor_widget_ != nullptr)
- {
- runtime_monitor_widget_->setHmiWriteEnabled(
- (online && plc_connected)
- || (offline && state == SimulationState::Running));
- runtime_monitor_widget_->refreshValues(
- runtime_mode_service_.mode(), runtime_mode_service_.plcConnectionState());
- }
-
- switch (state)
- {
- case SimulationState::Running:
- {
- executor_status_label_->setText(
- tr("逻辑执行器:运行(%1 次)")
- .arg(runtime_mode_service_.successfulScanCount()));
- break;
- }
- case SimulationState::Faulted:
- {
- executor_status_label_->setText(tr("逻辑执行器:故障"));
- if (report_fault)
- {
- const LogicScanResult &error = runtime_mode_service_.simulationError();
- if (!error.logicId.empty()
- && logic_editor_service_.findLogic(error.logicId) != nullptr)
- {
- current_logic_id_ = error.logicId;
- logic_editor_widget_->setLogicId(current_logic_id_);
- runtime_monitor_widget_->setProjectObjects(
- hmi_navigation_service_->currentPageId(), current_logic_id_);
- }
- logic_editor_widget_->setRuntimeTrace(
- runtime_mode_service_.offlineSimulationService()
- .traceSnapshot().forLogic(current_logic_id_),
- error.nodeId);
- runtime_monitor_widget_->setLogicTrace(
- runtime_mode_service_.offlineSimulationService().traceSnapshot(),
- error.nodeId);
- if (!error.nodeId.empty())
- {
- logic_editor_widget_->selectNode(error.nodeId);
- showLogicNodeProperties(error.nodeId);
- }
- QString message = tr("离线仿真异常:%1").arg(fromUtf8(error.message));
- if (!error.logicId.empty())
- {
- message += tr(",逻辑 %1").arg(fromUtf8(error.logicId));
- }
- if (!error.rungId.empty())
- {
- message += tr(",网络 %1").arg(fromUtf8(error.rungId));
- }
- if (!error.nodeId.empty())
- {
- message += tr(",节点 %1").arg(fromUtf8(error.nodeId));
- }
- statusBar()->showMessage(message);
- ui_->outputList->addItem(message);
- ui_->outputList->scrollToBottom();
- }
- break;
- }
- case SimulationState::Stopped:
- default:
- {
- executor_status_label_->setText(tr("逻辑执行器:停止"));
- break;
- }
- }
- }
-
- // 根据当前真实运行模式,同步更新模式工具栏单选按钮选中状态
- void MainWindow::restoreCurrentModeAction()
- {
- ui_->editingModeAction->setChecked(runtime_mode_service_.mode()
- == ApplicationMode::Editing);
- ui_->offlineModeAction->setChecked(runtime_mode_service_.mode()
- == ApplicationMode::OfflineRunning);
- ui_->onlineModeAction->setChecked(runtime_mode_service_.mode()
- == ApplicationMode::OnlineRunning);
- }
|