#include "runtime_panel_controller.h" #include "free_monitor_widget.h" #include "hmi_editor_widget.h" #include "logic_editor_widget.h" #include "runtime_monitor_widget.h" #include "runtime_monitor_window.h" #include "services/alarm_service.h" #include "services/hmi_editor_service.h" #include "services/hmi_navigation_service.h" #include "services/hmi_runtime_service.h" #include "services/logic_editor_service.h" #include "services/offline_simulation_service.h" #include "services/project_service.h" #include "services/register_monitor_service.h" #include "services/runtime_mode_service.h" #include #include #include #include #include #include namespace { QString fromUtf8(const std::string &value) { return QString::fromUtf8(value.data(), static_cast(value.size())); } } // namespace RuntimePanelController::RuntimePanelController( QWidget &parent, RuntimeModeService &runtime_mode_service, ProjectService &project_service, HmiEditorService &hmi_editor_service, HmiRuntimeService &hmi_runtime_service, LogicEditorService &logic_editor_service, HmiNavigationService &hmi_navigation_service, AlarmService &alarm_service, RegisterMonitorService ®ister_monitor_service, HmiEditorWidget &hmi_editor_widget, LogicEditorWidget &logic_editor_widget, QLabel &executor_status_label, std::function current_logic_id, std::function select_logic, std::function show_logic_properties, StatusReporter status_reporter, OutputReporter output_reporter, RuntimeExitRequester runtime_exit_requester) : parent_(parent), runtime_mode_service_(runtime_mode_service), project_service_(project_service), hmi_editor_service_(hmi_editor_service), hmi_runtime_service_(hmi_runtime_service), logic_editor_service_(logic_editor_service), hmi_navigation_service_(hmi_navigation_service), alarm_service_(alarm_service), register_monitor_service_(register_monitor_service), hmi_editor_widget_(hmi_editor_widget), logic_editor_widget_(logic_editor_widget), executor_status_label_(executor_status_label), current_logic_id_(std::move(current_logic_id)), select_logic_(std::move(select_logic)), show_logic_properties_(std::move(show_logic_properties)), status_reporter_(std::move(status_reporter)), output_reporter_(std::move(output_reporter)), runtime_exit_requester_(std::move(runtime_exit_requester)) { } RuntimePanelController::~RuntimePanelController() = default; void RuntimePanelController::configure() { runtime_monitor_window_ = std::make_unique(&parent_); runtime_monitor_window_->setObjectName( QStringLiteral("runtimeMonitorWindow")); runtime_monitor_widget_ = new RuntimeMonitorWidget( hmi_editor_service_, hmi_runtime_service_, logic_editor_service_, project_service_, hmi_navigation_service_, alarm_service_, register_monitor_service_, runtime_monitor_window_.get()); runtime_monitor_widget_->setObjectName(QStringLiteral("runtimeMonitorWidget")); runtime_monitor_window_->setMonitorWidget(*runtime_monitor_widget_); QObject::connect(runtime_monitor_widget_, &RuntimeMonitorWidget::exitRequested, &parent_, [this] { if (runtime_exit_requester_) { runtime_exit_requester_(); } }); QObject::connect(runtime_monitor_widget_->freeMonitorWidget(), &FreeMonitorWidget::monitorAddressesChanged, &parent_, [this] { runtime_mode_service_.setMonitorAddresses( register_monitor_service_.addresses()); runtime_monitor_widget_->refreshValues( runtime_mode_service_.mode(), runtime_mode_service_.plcConnectionState()); }); QObject::connect(runtime_monitor_widget_->freeMonitorWidget(), &FreeMonitorWidget::operationMessage, &parent_, [this](const QString &message) { status_reporter_(message, 4000); }); QObject::connect(runtime_monitor_widget_, &RuntimeMonitorWidget::navigationFailed, &parent_, [this](const QString &message) { status_reporter_(message, 5000); output_reporter_(QObject::tr("页面导航失败:%1").arg(message)); }); runtime_refresh_timer_ = new QTimer(&parent_); runtime_refresh_timer_->setInterval(150); QObject::connect(runtime_refresh_timer_, &QTimer::timeout, &parent_, [this] { handleRuntimeTimer(); }); runtime_refresh_timer_->start(); QObject::connect(&runtime_mode_service_.offlineSimulationService(), &OfflineSimulationService::stateChanged, &parent_, [this] { handleSimulationStateChanged(); }, Qt::QueuedConnection); QObject::connect(&runtime_mode_service_.offlineSimulationService(), &OfflineSimulationService::scanCompleted, &parent_, [this] { handleScanCompleted(); }, Qt::QueuedConnection); QObject::connect(&runtime_mode_service_.onlineLogicMonitorService(), &OnlineLogicMonitorService::stateChanged, &parent_, [this] { handleSimulationStateChanged(); }); QObject::connect(&runtime_mode_service_.onlineLogicMonitorService(), &OnlineLogicMonitorService::scanCompleted, &parent_, [this] { handleScanCompleted(); }, Qt::QueuedConnection); } RuntimeMonitorWidget *RuntimePanelController::runtimeMonitorWidget() const { return runtime_monitor_widget_; } void RuntimePanelController::enterRuntime( const std::string &page_id, const std::string &logic_id, ApplicationMode mode, PlcConnectionState plc_state) { if (!runtime_session_active_) { runtime_monitor_widget_->setProjectObjects(page_id, logic_id); // 运行窗口只创建一次,后续进入运行态复用同一个监控投影 runtime_session_active_ = true; } runtime_monitor_widget_->setMode(mode, plc_state); runtime_monitor_window_->showForRuntime(); } void RuntimePanelController::leaveRuntime( ApplicationMode mode, PlcConnectionState plc_state) { runtime_session_active_ = false; runtime_monitor_widget_->setMode(mode, plc_state); runtime_monitor_window_->hideForEditing(); } void RuntimePanelController::closeForApplicationExit() { if (runtime_monitor_window_ != nullptr) { runtime_monitor_window_->closeForApplicationExit(); } } void RuntimePanelController::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(); const bool write_enabled = (online && plc_connected) || (offline && state == SimulationState::Running); // 编辑画布始终只读,运行操作统一由运行监控中的 HMI 发出 hmi_editor_widget_.setRuntimeWriteEnabled(false); if (runtime_monitor_widget_ != nullptr) { runtime_monitor_widget_->setHmiWriteEnabled(write_enabled); runtime_monitor_widget_->setFreeMonitorWriteEnabled(write_enabled); runtime_monitor_widget_->refreshValues( runtime_mode_service_.mode(), runtime_mode_service_.plcConnectionState()); } if (online) { switch (runtime_mode_service_.onlineLogicMonitorService().state()) { case OnlineLogicMonitorState::Running: { executor_status_label_.setText( QObject::tr("本地推算轨迹:运行(%1 次)") .arg(runtime_mode_service_.onlineLogicMonitorService() .successfulScanCount())); return; } case OnlineLogicMonitorState::Faulted: { executor_status_label_.setText(QObject::tr("本地推算轨迹:故障")); if (report_fault) { const LogicScanResult &error = runtime_mode_service_ .onlineLogicMonitorService().lastError(); const QString message = QObject::tr("真机本地轨迹异常:%1") .arg(fromUtf8(error.message)); status_reporter_(message, 0); output_reporter_(message); } return; } case OnlineLogicMonitorState::Stopped: default: { executor_status_label_.setText(QObject::tr("本地推算轨迹:停止")); return; } } } switch (state) { case SimulationState::Running: { executor_status_label_.setText( QObject::tr("逻辑执行器:运行(%1 次)") .arg(runtime_mode_service_.successfulScanCount())); break; } case SimulationState::Faulted: { executor_status_label_.setText(QObject::tr("逻辑执行器:故障")); if (!report_fault) { break; } const LogicScanResult &error = runtime_mode_service_.simulationError(); if (!error.logicId.empty() && logic_editor_service_.findLogic(error.logicId) != nullptr) { select_logic_(error.logicId); } const std::string logic_id = current_logic_id_(); logic_editor_widget_.setLogicId(logic_id); if (runtime_monitor_widget_ != nullptr) { runtime_monitor_widget_->setProjectObjects( hmi_navigation_service_.currentPageId(), logic_id); } logic_editor_widget_.setRuntimeTrace( runtime_mode_service_.offlineSimulationService() .traceSnapshot().forLogic(logic_id), error.nodeId); if (runtime_monitor_widget_ != nullptr) { runtime_monitor_widget_->setLogicTrace( runtime_mode_service_.offlineSimulationService().traceSnapshot(), error.nodeId); } if (!error.nodeId.empty()) { logic_editor_widget_.selectNode(error.nodeId); show_logic_properties_(error.nodeId); } QString message = QObject::tr("离线仿真异常:%1").arg(fromUtf8(error.message)); if (!error.logicId.empty()) { message += QObject::tr(",逻辑 %1").arg(fromUtf8(error.logicId)); } if (!error.rungId.empty()) { message += QObject::tr(",网络 %1").arg(fromUtf8(error.rungId)); } if (!error.nodeId.empty()) { message += QObject::tr(",节点 %1").arg(fromUtf8(error.nodeId)); } status_reporter_(message, 0); output_reporter_(message); break; } case SimulationState::Stopped: default: { executor_status_label_.setText(QObject::tr("逻辑执行器:停止")); break; } } } void RuntimePanelController::handleRuntimeTimer() { // 定时器只刷新投影;离线按定时器扫描,真机在完整 PLC 轮询后推算 if (runtime_mode_service_.mode() == ApplicationMode::Editing) { return; } alarm_service_.refresh(); hmi_editor_widget_.refreshRuntimeValues(); if (runtime_monitor_widget_ != nullptr) { runtime_monitor_widget_->refreshValues( runtime_mode_service_.mode(), runtime_mode_service_.plcConnectionState()); } updateSimulationUi(false); } void RuntimePanelController::handleSimulationStateChanged() { updateSimulationUi(true); } void RuntimePanelController::handleScanCompleted() { const ApplicationMode mode = runtime_mode_service_.mode(); const bool offline_running = mode == ApplicationMode::OfflineRunning && runtime_mode_service_.simulationState() == SimulationState::Running; const bool online_running = mode == ApplicationMode::OnlineRunning && runtime_mode_service_.onlineLogicMonitorService().state() == OnlineLogicMonitorState::Running; // 排队信号可能晚于退出操作到达,编辑态不得恢复旧运行轨迹 if (!offline_running && !online_running) { return; } const std::string logic_id = current_logic_id_(); const LogicTraceSnapshot &trace = offline_running ? runtime_mode_service_.offlineSimulationService().traceSnapshot() : runtime_mode_service_.onlineLogicMonitorService().traceSnapshot(); logic_editor_widget_.setRuntimeTrace(trace.forLogic(logic_id)); if (runtime_monitor_widget_ != nullptr) { runtime_monitor_widget_->setLogicTrace(trace); } }