综合平台编程器项目的远程存储
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

658 regels
26 KiB

  1. #include "main_window.h"
  2. #include "hmi_editor_widget.h"
  3. #include "services/hmi_editor_service.h"
  4. #include "services/hmi_runtime_service.h"
  5. #include "services/project_service.h"
  6. #include "services/runtime_mode_service.h"
  7. #include "ui_main_window.h"
  8. #include <QActionGroup>
  9. #include <QApplication>
  10. #include <QComboBox>
  11. #include <QFileDialog>
  12. #include <QFormLayout>
  13. #include <QInputDialog>
  14. #include <QLabel>
  15. #include <QLineEdit>
  16. #include <QMessageBox>
  17. #include <QPushButton>
  18. #include <QSpinBox>
  19. #include <QStatusBar>
  20. #include <QStyle>
  21. #include <QTimer>
  22. #include <QToolBar>
  23. #include <QTreeWidget>
  24. #include <QTreeWidgetItem>
  25. namespace {
  26. QString modeText(ApplicationMode mode)
  27. {
  28. switch (mode)
  29. {
  30. case ApplicationMode::Editing:
  31. {
  32. return MainWindow::tr("编辑态");
  33. }
  34. case ApplicationMode::OfflineRunning:
  35. {
  36. return MainWindow::tr("离线运行态");
  37. }
  38. case ApplicationMode::OnlineRunning:
  39. {
  40. return MainWindow::tr("真机运行态");
  41. }
  42. default:
  43. {
  44. return MainWindow::tr("未知状态");
  45. }
  46. }
  47. }
  48. QString transitionErrorText(ModeTransitionError error)
  49. {
  50. switch (error)
  51. {
  52. case ModeTransitionError::MustReturnToEditing:
  53. {
  54. return MainWindow::tr("请先返回编辑态,再切换运行模式");
  55. }
  56. case ModeTransitionError::InitialPlcReadRequired:
  57. {
  58. return MainWindow::tr("真机运行前必须连接 PLC 并完成首次读取");
  59. }
  60. case ModeTransitionError::AlreadyInRequestedMode:
  61. {
  62. return MainWindow::tr("当前已处于所选模式");
  63. }
  64. case ModeTransitionError::None:
  65. default:
  66. {
  67. return MainWindow::tr("模式切换失败");
  68. }
  69. }
  70. }
  71. std::string toUtf8(const QString &value)
  72. {
  73. const QByteArray bytes = value.toUtf8();
  74. return std::string(bytes.constData(), static_cast<std::size_t>(bytes.size()));
  75. }
  76. QString fromUtf8(const std::string &value)
  77. {
  78. return QString::fromUtf8(value.data(), static_cast<int>(value.size()));
  79. }
  80. QString controlTypeText(HmiControlType type)
  81. {
  82. switch (type)
  83. {
  84. case HmiControlType::Button:
  85. {
  86. return MainWindow::tr("按钮");
  87. }
  88. case HmiControlType::Indicator:
  89. {
  90. return MainWindow::tr("指示灯");
  91. }
  92. case HmiControlType::NumericDisplay:
  93. {
  94. return MainWindow::tr("数值显示");
  95. }
  96. case HmiControlType::NumericInput:
  97. {
  98. return MainWindow::tr("数值输入");
  99. }
  100. case HmiControlType::Label:
  101. default:
  102. {
  103. return MainWindow::tr("文本");
  104. }
  105. }
  106. }
  107. bool usesMAddress(HmiControlType type)
  108. {
  109. return type == HmiControlType::Button || type == HmiControlType::Indicator;
  110. }
  111. } // namespace
  112. MainWindow::MainWindow(
  113. RuntimeModeService &runtime_mode_service,
  114. ProjectService &project_service,
  115. HmiEditorService &hmi_editor_service,
  116. HmiRuntimeService &hmi_runtime_service,
  117. QWidget *parent)
  118. : QMainWindow(parent),
  119. ui_(std::make_unique<Ui::MainWindow>()),
  120. runtime_mode_service_(runtime_mode_service),
  121. project_service_(project_service),
  122. hmi_editor_service_(hmi_editor_service),
  123. hmi_runtime_service_(hmi_runtime_service)
  124. {
  125. ui_->setupUi(this);
  126. configureAppearance();
  127. configureActions();
  128. configurePropertyEditor();
  129. configureHmiEditor();
  130. hmi_editor_service_.ensureDefaultPage();
  131. refreshProjectUi();
  132. updateModeUi(tr("系统已进入编辑态"));
  133. }
  134. MainWindow::~MainWindow() = default;
  135. void MainWindow::configureActions()
  136. {
  137. mode_action_group_ = new QActionGroup(this);
  138. mode_action_group_->setExclusive(true);
  139. mode_action_group_->addAction(ui_->editingModeAction);
  140. mode_action_group_->addAction(ui_->offlineModeAction);
  141. mode_action_group_->addAction(ui_->onlineModeAction);
  142. connect(ui_->editingModeAction, &QAction::triggered, this,
  143. [this] { requestMode(ApplicationMode::Editing); });
  144. connect(ui_->offlineModeAction, &QAction::triggered, this,
  145. [this] { requestMode(ApplicationMode::OfflineRunning); });
  146. connect(ui_->onlineModeAction, &QAction::triggered, this,
  147. [this] { requestMode(ApplicationMode::OnlineRunning); });
  148. connect(ui_->exitAction, &QAction::triggered, qApp, &QApplication::closeAllWindows);
  149. new_project_action_ = new QAction(tr("新建工程"), this);
  150. save_project_action_ = new QAction(tr("保存工程"), this);
  151. save_as_project_action_ = new QAction(tr("工程另存为"), this);
  152. load_project_action_ = new QAction(tr("加载工程"), this);
  153. new_project_action_->setObjectName(QStringLiteral("newProjectAction"));
  154. save_project_action_->setObjectName(QStringLiteral("saveProjectAction"));
  155. save_as_project_action_->setObjectName(QStringLiteral("saveAsProjectAction"));
  156. load_project_action_->setObjectName(QStringLiteral("loadProjectAction"));
  157. new_project_action_->setIcon(style()->standardIcon(QStyle::SP_FileIcon));
  158. save_project_action_->setIcon(style()->standardIcon(QStyle::SP_DialogSaveButton));
  159. load_project_action_->setIcon(style()->standardIcon(QStyle::SP_DialogOpenButton));
  160. ui_->fileMenu->insertAction(ui_->exitAction, new_project_action_);
  161. ui_->fileMenu->insertAction(ui_->exitAction, save_project_action_);
  162. ui_->fileMenu->insertAction(ui_->exitAction, save_as_project_action_);
  163. ui_->fileMenu->insertAction(ui_->exitAction, load_project_action_);
  164. ui_->fileMenu->insertSeparator(ui_->exitAction);
  165. connect(new_project_action_, &QAction::triggered, this, &MainWindow::createNewProject);
  166. connect(save_project_action_, &QAction::triggered, this, &MainWindow::saveProject);
  167. connect(save_as_project_action_, &QAction::triggered, this, &MainWindow::saveProjectAs);
  168. connect(load_project_action_, &QAction::triggered, this, &MainWindow::loadProject);
  169. hmi_tool_bar_ = addToolBar(tr("HMI 控件"));
  170. hmi_tool_bar_->setObjectName(QStringLiteral("hmiToolBar"));
  171. hmi_tool_bar_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
  172. add_button_action_ = hmi_tool_bar_->addAction(
  173. style()->standardIcon(QStyle::SP_DialogApplyButton), tr("按钮"));
  174. add_indicator_action_ = hmi_tool_bar_->addAction(
  175. style()->standardIcon(QStyle::SP_DialogYesButton), tr("指示灯"));
  176. add_numeric_display_action_ = hmi_tool_bar_->addAction(
  177. style()->standardIcon(QStyle::SP_FileDialogInfoView), tr("数值显示"));
  178. add_numeric_input_action_ = hmi_tool_bar_->addAction(
  179. style()->standardIcon(QStyle::SP_FileDialogContentsView), tr("数值输入"));
  180. delete_control_action_ = hmi_tool_bar_->addAction(
  181. style()->standardIcon(QStyle::SP_TrashIcon), tr("删除"));
  182. add_button_action_->setObjectName(QStringLiteral("addButtonAction"));
  183. add_indicator_action_->setObjectName(QStringLiteral("addIndicatorAction"));
  184. add_numeric_display_action_->setObjectName(QStringLiteral("addNumericDisplayAction"));
  185. add_numeric_input_action_->setObjectName(QStringLiteral("addNumericInputAction"));
  186. delete_control_action_->setObjectName(QStringLiteral("deleteControlAction"));
  187. add_button_action_->setToolTip(tr("添加按钮控件"));
  188. add_indicator_action_->setToolTip(tr("添加指示灯控件"));
  189. add_numeric_display_action_->setToolTip(tr("添加数值显示控件"));
  190. add_numeric_input_action_->setToolTip(tr("添加数值输入控件"));
  191. delete_control_action_->setToolTip(tr("删除当前控件"));
  192. connect(add_button_action_, &QAction::triggered, this,
  193. [this] { addHmiControl(HmiControlType::Button); });
  194. connect(add_indicator_action_, &QAction::triggered, this,
  195. [this] { addHmiControl(HmiControlType::Indicator); });
  196. connect(add_numeric_display_action_, &QAction::triggered, this,
  197. [this] { addHmiControl(HmiControlType::NumericDisplay); });
  198. connect(add_numeric_input_action_, &QAction::triggered, this,
  199. [this] { addHmiControl(HmiControlType::NumericInput); });
  200. connect(delete_control_action_, &QAction::triggered,
  201. this, &MainWindow::deleteSelectedControl);
  202. ui_->viewMenu->addAction(ui_->projectDock->toggleViewAction());
  203. ui_->viewMenu->addAction(ui_->propertiesDock->toggleViewAction());
  204. ui_->viewMenu->addAction(ui_->outputDock->toggleViewAction());
  205. ui_->viewMenu->addAction(hmi_tool_bar_->toggleViewAction());
  206. ui_->viewMenu->addSeparator();
  207. ui_->viewMenu->addAction(ui_->modeToolBar->toggleViewAction());
  208. }
  209. void MainWindow::configureAppearance()
  210. {
  211. setDockNestingEnabled(true);
  212. setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
  213. setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
  214. ui_->editingModeAction->setIcon(style()->standardIcon(QStyle::SP_FileDialogDetailedView));
  215. ui_->offlineModeAction->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
  216. ui_->onlineModeAction->setIcon(style()->standardIcon(QStyle::SP_DriveNetIcon));
  217. ui_->editorTabWidget->setTabIcon(0, style()->standardIcon(QStyle::SP_DesktopIcon));
  218. ui_->editorTabWidget->setTabIcon(1, style()->standardIcon(QStyle::SP_FileDialogListView));
  219. ui_->outputDock->setMaximumHeight(220);
  220. mode_status_label_ = new QLabel(this);
  221. mode_status_label_->setObjectName(QStringLiteral("modeStatusLabel"));
  222. mode_status_label_->setMinimumWidth(88);
  223. mode_status_label_->setAlignment(Qt::AlignCenter);
  224. register_status_label_ = new QLabel(this);
  225. register_status_label_->setMinimumWidth(132);
  226. executor_status_label_ = new QLabel(this);
  227. executor_status_label_->setMinimumWidth(132);
  228. statusBar()->addPermanentWidget(mode_status_label_);
  229. statusBar()->addPermanentWidget(register_status_label_);
  230. statusBar()->addPermanentWidget(executor_status_label_);
  231. setStyleSheet(QStringLiteral(
  232. "QMainWindow { background: #f3f5f7; }"
  233. "QToolBar { background: #ffffff; border: 0; border-bottom: 1px solid #cbd2d9;"
  234. " padding: 4px; spacing: 3px; }"
  235. "QToolButton { min-height: 26px; padding: 3px 9px; border: 1px solid transparent; }"
  236. "QToolButton:hover { background: #edf2f6; border-color: #c5ced6; }"
  237. "QToolButton:checked { background: #dcece3; border-color: #75a58a; }"
  238. "QDockWidget { color: #24313b; font-weight: 600; }"
  239. "QDockWidget::title { background: #e9edf0; padding: 6px; border-bottom: 1px solid #cbd2d9; }"
  240. "QTreeWidget, QListWidget, QScrollArea { background: #ffffff; border: 1px solid #d5dbe0; }"
  241. "QTabWidget::pane { border: 0; background: #f3f5f7; }"
  242. "QTabBar::tab { background: #e5e9ec; padding: 7px 14px; border-right: 1px solid #cbd2d9; }"
  243. "QTabBar::tab:selected { background: #ffffff; color: #15232d; }"
  244. "QFrame#hmiCanvasPlaceholder, QFrame#logicCanvasPlaceholder { background: #ffffff;"
  245. " border: 1px solid #cbd2d9; }"
  246. "QLabel#hmiEmptyLabel, QLabel#logicEmptyLabel { color: #75838d; }"
  247. "QLabel#hmiPageTitleLabel { color: #1d2a33; font-weight: 600; }"
  248. "QLabel#hmiPageSizeLabel { color: #66747e; }"));
  249. }
  250. void MainWindow::configureHmiEditor()
  251. {
  252. QLayout *layout = ui_->hmiCanvasPlaceholder->layout();
  253. delete ui_->hmiEmptyLabel;
  254. hmi_editor_widget_ = new HmiEditorWidget(
  255. hmi_editor_service_, hmi_runtime_service_, ui_->hmiCanvasPlaceholder);
  256. hmi_editor_widget_->setMinimumHeight(320);
  257. layout->addWidget(hmi_editor_widget_);
  258. connect(hmi_editor_widget_, &HmiEditorWidget::controlSelected,
  259. this,
  260. [this](const QString &id) { showControlProperties(toUtf8(id)); });
  261. connect(hmi_editor_widget_, &HmiEditorWidget::controlChanged,
  262. this,
  263. [this](const QString &id)
  264. {
  265. showControlProperties(toUtf8(id));
  266. refreshProjectUi();
  267. });
  268. connect(hmi_editor_widget_, &HmiEditorWidget::editorError,
  269. this,
  270. [this](const QString &message) { statusBar()->showMessage(message, 5000); });
  271. runtime_refresh_timer_ = new QTimer(this);
  272. runtime_refresh_timer_->setInterval(150);
  273. connect(runtime_refresh_timer_, &QTimer::timeout,
  274. this,
  275. [this]
  276. {
  277. if (runtime_mode_service_.mode() != ApplicationMode::Editing)
  278. {
  279. hmi_editor_widget_->refreshRuntimeValues();
  280. }
  281. });
  282. runtime_refresh_timer_->start();
  283. }
  284. void MainWindow::configurePropertyEditor()
  285. {
  286. control_id_edit_ = new QLineEdit(ui_->propertiesPage);
  287. control_id_edit_->setObjectName(QStringLiteral("controlIdEdit"));
  288. control_text_edit_ = new QLineEdit(ui_->propertiesPage);
  289. control_text_edit_->setObjectName(QStringLiteral("controlTextEdit"));
  290. control_x_spin_box_ = new QSpinBox(ui_->propertiesPage);
  291. control_y_spin_box_ = new QSpinBox(ui_->propertiesPage);
  292. control_width_spin_box_ = new QSpinBox(ui_->propertiesPage);
  293. control_height_spin_box_ = new QSpinBox(ui_->propertiesPage);
  294. control_x_spin_box_->setObjectName(QStringLiteral("controlXSpinBox"));
  295. control_y_spin_box_->setObjectName(QStringLiteral("controlYSpinBox"));
  296. control_width_spin_box_->setObjectName(QStringLiteral("controlWidthSpinBox"));
  297. control_height_spin_box_->setObjectName(QStringLiteral("controlHeightSpinBox"));
  298. for (QSpinBox *box : {control_x_spin_box_, control_y_spin_box_})
  299. {
  300. box->setRange(0, 4000);
  301. }
  302. for (QSpinBox *box : {control_width_spin_box_, control_height_spin_box_})
  303. {
  304. box->setRange(1, 4000);
  305. }
  306. binding_area_combo_box_ = new QComboBox(ui_->propertiesPage);
  307. binding_area_combo_box_->setObjectName(QStringLiteral("bindingAreaComboBox"));
  308. binding_area_combo_box_->addItem(QStringLiteral("M"), QVariant::fromValue(0));
  309. binding_area_combo_box_->addItem(QStringLiteral("D"), QVariant::fromValue(1));
  310. binding_index_spin_box_ = new QSpinBox(ui_->propertiesPage);
  311. binding_index_spin_box_->setObjectName(QStringLiteral("bindingIndexSpinBox"));
  312. binding_index_spin_box_->setRange(0, RegisterAddress::kMaximumIndex);
  313. apply_properties_button_ = new QPushButton(tr("应用属性"), ui_->propertiesPage);
  314. apply_properties_button_->setObjectName(QStringLiteral("applyPropertiesButton"));
  315. QFormLayout *form = ui_->propertiesForm;
  316. form->addRow(tr("控件 ID"), control_id_edit_);
  317. form->addRow(tr("显示文本"), control_text_edit_);
  318. form->addRow(tr("X"), control_x_spin_box_);
  319. form->addRow(tr("Y"), control_y_spin_box_);
  320. form->addRow(tr("宽度"), control_width_spin_box_);
  321. form->addRow(tr("高度"), control_height_spin_box_);
  322. form->addRow(tr("绑定区域"), binding_area_combo_box_);
  323. form->addRow(tr("绑定地址"), binding_index_spin_box_);
  324. form->addRow(apply_properties_button_);
  325. connect(apply_properties_button_, &QPushButton::clicked,
  326. this, &MainWindow::applySelectedControlProperties);
  327. showControlProperties({});
  328. }
  329. void MainWindow::refreshProjectUi()
  330. {
  331. const std::string current_page_id = hmi_editor_service_.firstPageId();
  332. if (hmi_editor_widget_ != nullptr)
  333. {
  334. hmi_editor_widget_->setPageId(current_page_id);
  335. }
  336. const HmiPage *page = hmi_editor_service_.findPage(current_page_id);
  337. ui_->projectTree->clear();
  338. auto *hmi_root = new QTreeWidgetItem(ui_->projectTree, {tr("HMI 页面")});
  339. if (page != nullptr)
  340. {
  341. hmi_root->addChild(new QTreeWidgetItem(hmi_root, {fromUtf8(page->name)}));
  342. ui_->hmiPageTitleLabel->setText(fromUtf8(page->name));
  343. ui_->hmiPageSizeLabel->setText(
  344. tr("%1 x %2").arg(page->width).arg(page->height));
  345. }
  346. else
  347. {
  348. ui_->hmiPageTitleLabel->setText(tr("尚未创建 HMI 页面"));
  349. ui_->hmiPageSizeLabel->clear();
  350. }
  351. new QTreeWidgetItem(ui_->projectTree, {tr("控制逻辑")});
  352. ui_->projectTree->expandAll();
  353. }
  354. void MainWindow::showControlProperties(const std::string &control_id)
  355. {
  356. selected_control_id_ = control_id;
  357. const HmiControl *control = hmi_editor_service_.findControl(
  358. hmi_editor_service_.firstPageId(), control_id);
  359. const bool has_control = control != nullptr;
  360. ui_->selectionValueLabel->setText(
  361. has_control ? fromUtf8(control->id) : tr("未选择"));
  362. for (QWidget *widget : {static_cast<QWidget *>(control_id_edit_),
  363. static_cast<QWidget *>(control_text_edit_),
  364. static_cast<QWidget *>(control_x_spin_box_),
  365. static_cast<QWidget *>(control_y_spin_box_),
  366. static_cast<QWidget *>(control_width_spin_box_),
  367. static_cast<QWidget *>(control_height_spin_box_),
  368. static_cast<QWidget *>(binding_area_combo_box_),
  369. static_cast<QWidget *>(binding_index_spin_box_),
  370. static_cast<QWidget *>(apply_properties_button_)})
  371. {
  372. widget->setEnabled(has_control);
  373. }
  374. if (!has_control)
  375. {
  376. return;
  377. }
  378. control_id_edit_->setText(fromUtf8(control->id));
  379. control_text_edit_->setText(fromUtf8(control->text));
  380. control_x_spin_box_->setValue(control->bounds.x);
  381. control_y_spin_box_->setValue(control->bounds.y);
  382. control_width_spin_box_->setValue(control->bounds.width);
  383. control_height_spin_box_->setValue(control->bounds.height);
  384. const RegisterArea default_area = usesMAddress(control->type)
  385. ? RegisterArea::M : RegisterArea::D;
  386. const RegisterArea area = control->binding.has_value()
  387. ? control->binding->area() : default_area;
  388. binding_area_combo_box_->setCurrentIndex(area == RegisterArea::M ? 0 : 1);
  389. binding_index_spin_box_->setValue(
  390. control->binding.has_value() ? control->binding->index() : 0);
  391. }
  392. void MainWindow::addHmiControl(HmiControlType type)
  393. {
  394. const std::string page_id = hmi_editor_service_.firstPageId();
  395. const HmiEditorResult result = hmi_editor_service_.addControl(page_id, type);
  396. if (!result.succeeded)
  397. {
  398. showProjectResult(tr("添加控件"), fromUtf8(result.message), false);
  399. return;
  400. }
  401. hmi_editor_widget_->reloadPage();
  402. hmi_editor_widget_->selectControl(result.id);
  403. showControlProperties(result.id);
  404. refreshProjectUi();
  405. statusBar()->showMessage(tr("已添加%1").arg(controlTypeText(type)), 3000);
  406. }
  407. void MainWindow::deleteSelectedControl()
  408. {
  409. if (selected_control_id_.empty())
  410. {
  411. return;
  412. }
  413. const HmiEditorResult result = hmi_editor_service_.removeControl(
  414. hmi_editor_service_.firstPageId(), selected_control_id_);
  415. if (!result.succeeded)
  416. {
  417. showProjectResult(tr("删除控件"), fromUtf8(result.message), false);
  418. return;
  419. }
  420. selected_control_id_.clear();
  421. hmi_editor_widget_->reloadPage();
  422. showControlProperties({});
  423. refreshProjectUi();
  424. }
  425. void MainWindow::applySelectedControlProperties()
  426. {
  427. const HmiControl *old_control = hmi_editor_service_.findControl(
  428. hmi_editor_service_.firstPageId(), selected_control_id_);
  429. if (old_control == nullptr)
  430. {
  431. return;
  432. }
  433. HmiControl control = *old_control;
  434. control.id = toUtf8(control_id_edit_->text());
  435. control.text = toUtf8(control_text_edit_->text());
  436. control.bounds.x = control_x_spin_box_->value();
  437. control.bounds.y = control_y_spin_box_->value();
  438. control.bounds.width = control_width_spin_box_->value();
  439. control.bounds.height = control_height_spin_box_->value();
  440. const RegisterArea area = binding_area_combo_box_->currentIndex() == 0
  441. ? RegisterArea::M : RegisterArea::D;
  442. control.binding = RegisterAddress{area, binding_index_spin_box_->value()};
  443. const HmiEditorResult result = hmi_editor_service_.updateControl(
  444. hmi_editor_service_.firstPageId(), selected_control_id_, control);
  445. if (!result.succeeded)
  446. {
  447. showProjectResult(tr("应用属性"), fromUtf8(result.message), false);
  448. return;
  449. }
  450. selected_control_id_ = result.id;
  451. hmi_editor_widget_->reloadPage();
  452. hmi_editor_widget_->selectControl(selected_control_id_);
  453. refreshProjectUi();
  454. statusBar()->showMessage(tr("控件属性已更新"), 3000);
  455. }
  456. void MainWindow::createNewProject()
  457. {
  458. const QString name = QInputDialog::getText(
  459. this, tr("新建工程"), tr("工程名称"));
  460. if (name.isEmpty())
  461. {
  462. return;
  463. }
  464. const ProjectOperationResult result = project_service_.createNewProject(toUtf8(name));
  465. if (!result.succeeded)
  466. {
  467. showProjectResult(tr("新建工程"), fromUtf8(result.message), false);
  468. return;
  469. }
  470. hmi_editor_service_.ensureDefaultPage();
  471. selected_control_id_.clear();
  472. refreshProjectUi();
  473. hmi_editor_widget_->reloadPage();
  474. showControlProperties({});
  475. statusBar()->showMessage(tr("已创建新工程"), 3000);
  476. }
  477. void MainWindow::saveProject()
  478. {
  479. const ProjectOperationResult result = project_service_.save();
  480. if (!result.succeeded)
  481. {
  482. saveProjectAs();
  483. return;
  484. }
  485. showProjectResult(tr("保存工程"), tr("工程已保存"), true);
  486. }
  487. void MainWindow::saveProjectAs()
  488. {
  489. const QString path = QFileDialog::getSaveFileName(
  490. this, tr("工程另存为"), {}, tr("工程文件 (*.json)"));
  491. if (path.isEmpty())
  492. {
  493. return;
  494. }
  495. const ProjectOperationResult result = project_service_.saveAs(toUtf8(path));
  496. showProjectResult(
  497. tr("保存工程"),
  498. result.succeeded ? tr("工程已保存") : fromUtf8(result.message),
  499. result.succeeded);
  500. }
  501. void MainWindow::loadProject()
  502. {
  503. const QString path = QFileDialog::getOpenFileName(
  504. this, tr("加载工程"), {}, tr("工程文件 (*.json)"));
  505. if (path.isEmpty())
  506. {
  507. return;
  508. }
  509. const ProjectOperationResult result = project_service_.load(toUtf8(path));
  510. if (!result.succeeded)
  511. {
  512. showProjectResult(tr("加载工程"), fromUtf8(result.message), false);
  513. return;
  514. }
  515. selected_control_id_.clear();
  516. refreshProjectUi();
  517. hmi_editor_widget_->reloadPage();
  518. showControlProperties({});
  519. showProjectResult(tr("加载工程"), tr("工程已加载"), true);
  520. }
  521. void MainWindow::showProjectResult(
  522. const QString &action, const QString &message, bool succeeded)
  523. {
  524. const QString output = action + QStringLiteral(": ") + message;
  525. statusBar()->showMessage(output, 5000);
  526. ui_->outputList->addItem(output);
  527. ui_->outputList->scrollToBottom();
  528. if (!succeeded)
  529. {
  530. QMessageBox::warning(this, action, message);
  531. }
  532. }
  533. void MainWindow::requestMode(ApplicationMode requested_mode)
  534. {
  535. ModeTransitionResult result;
  536. switch (requested_mode)
  537. {
  538. case ApplicationMode::Editing:
  539. {
  540. result = runtime_mode_service_.enterEditing();
  541. break;
  542. }
  543. case ApplicationMode::OfflineRunning:
  544. {
  545. result = runtime_mode_service_.enterOfflineRunning();
  546. break;
  547. }
  548. case ApplicationMode::OnlineRunning:
  549. {
  550. result = runtime_mode_service_.enterOnlineRunning();
  551. break;
  552. }
  553. default:
  554. {
  555. restoreCurrentModeAction();
  556. statusBar()->showMessage(tr("不支持的运行模式"), 5000);
  557. return;
  558. }
  559. }
  560. if (!result.succeeded)
  561. {
  562. restoreCurrentModeAction();
  563. statusBar()->showMessage(transitionErrorText(result.error), 5000);
  564. return;
  565. }
  566. updateModeUi(tr("已进入%1").arg(modeText(runtime_mode_service_.mode())));
  567. }
  568. void MainWindow::updateModeUi(const QString &message)
  569. {
  570. const ApplicationMode mode = runtime_mode_service_.mode();
  571. const ModePolicy policy = runtime_mode_service_.policy();
  572. restoreCurrentModeAction();
  573. ui_->projectDock->setEnabled(policy.allowsProjectEditing);
  574. ui_->propertiesDock->setEnabled(policy.allowsProjectEditing);
  575. hmi_editor_widget_->setEditingEnabled(policy.allowsProjectEditing);
  576. hmi_editor_widget_->setRuntimeActive(policy.usesVirtualRegisters);
  577. hmi_tool_bar_->setEnabled(policy.allowsProjectEditing);
  578. add_button_action_->setEnabled(policy.allowsProjectEditing);
  579. add_indicator_action_->setEnabled(policy.allowsProjectEditing);
  580. add_numeric_display_action_->setEnabled(policy.allowsProjectEditing);
  581. add_numeric_input_action_->setEnabled(policy.allowsProjectEditing);
  582. delete_control_action_->setEnabled(policy.allowsProjectEditing);
  583. new_project_action_->setEnabled(policy.allowsProjectEditing);
  584. save_project_action_->setEnabled(policy.allowsProjectEditing);
  585. save_as_project_action_->setEnabled(policy.allowsProjectEditing);
  586. load_project_action_->setEnabled(policy.allowsProjectEditing);
  587. mode_status_label_->setText(modeText(mode));
  588. if (mode == ApplicationMode::Editing)
  589. {
  590. mode_status_label_->setStyleSheet(QStringLiteral(
  591. "border: 1px solid #75a58a; background: #e3f0e8; color: #205c3b; padding: 2px 8px;"));
  592. }
  593. else if (mode == ApplicationMode::OfflineRunning)
  594. {
  595. mode_status_label_->setStyleSheet(QStringLiteral(
  596. "border: 1px solid #bd8739; background: #fff0d2; color: #744b0e; padding: 2px 8px;"));
  597. }
  598. else
  599. {
  600. mode_status_label_->setStyleSheet(QStringLiteral(
  601. "border: 1px solid #4b88a8; background: #deeff7; color: #174f6c; padding: 2px 8px;"));
  602. }
  603. register_status_label_->setText(
  604. policy.usesVirtualRegisters ? tr("数据源:虚拟 M/D")
  605. : policy.usesPlcRegisters ? tr("数据源:PLC 缓存") : tr("数据源:未启用"));
  606. executor_status_label_->setText(
  607. policy.runsLogicExecutor ? tr("逻辑执行器:运行") : tr("逻辑执行器:停止"));
  608. statusBar()->showMessage(message, 4000);
  609. ui_->outputList->addItem(message);
  610. ui_->outputList->scrollToBottom();
  611. }
  612. void MainWindow::restoreCurrentModeAction()
  613. {
  614. ui_->editingModeAction->setChecked(runtime_mode_service_.mode()
  615. == ApplicationMode::Editing);
  616. ui_->offlineModeAction->setChecked(runtime_mode_service_.mode()
  617. == ApplicationMode::OfflineRunning);
  618. ui_->onlineModeAction->setChecked(runtime_mode_service_.mode()
  619. == ApplicationMode::OnlineRunning);
  620. }