综合平台编程器项目的远程存储
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 

703 rader
22 KiB

  1. #include "logic_editor_widget.h"
  2. #include <QGraphicsItem>
  3. #include <QGraphicsScene>
  4. #include <QGraphicsTextItem>
  5. #include <QPainter>
  6. #include <QPainterPath>
  7. #include <QResizeEvent>
  8. #include <QStyleOptionGraphicsItem>
  9. #include <algorithm>
  10. #include <cmath>
  11. #include <type_traits>
  12. namespace {
  13. constexpr qreal kMinimumSceneWidth = 1280.0;
  14. constexpr qreal kLeftRailX = 72.0;
  15. constexpr qreal kStageWidth = 184.0;
  16. constexpr qreal kNodeWidth = 128.0;
  17. constexpr qreal kNodeTerminalX = kNodeWidth / 2.0;
  18. constexpr qreal kBranchSpacing = 96.0;
  19. constexpr qreal kRungGap = 18.0;
  20. constexpr qreal kRungBaseHeight = 132.0;
  21. constexpr qreal kMainLineOffset = 78.0;
  22. constexpr qreal kLadderLineWidth = 1.6;
  23. constexpr qreal kPlaceholderWidth = 112.0;
  24. constexpr qreal kPlaceholderHeight = 30.0;
  25. const QColor kLadderColor(QStringLiteral("#26343d"));
  26. const QColor kSelectionColor(QStringLiteral("#dcecf4"));
  27. const QColor kSelectionBorderColor(QStringLiteral("#1677a8"));
  28. const QColor kPlaceholderColor(QStringLiteral("#82919b"));
  29. QString comparisonText(ComparisonOperator comparison)
  30. {
  31. switch (comparison)
  32. {
  33. case ComparisonOperator::Equal: return QStringLiteral("=");
  34. case ComparisonOperator::NotEqual: return QStringLiteral("<>");
  35. case ComparisonOperator::LessThan: return QStringLiteral("<");
  36. case ComparisonOperator::LessThanOrEqual: return QStringLiteral("<=");
  37. case ComparisonOperator::GreaterThan: return QStringLiteral(">");
  38. case ComparisonOperator::GreaterThanOrEqual: return QStringLiteral(">=");
  39. default: return QStringLiteral("?");
  40. }
  41. }
  42. QString registerAddressText(const RegisterAddress &address)
  43. {
  44. return QStringLiteral("%1%2")
  45. .arg(address.area() == RegisterArea::M ? QStringLiteral("M")
  46. : QStringLiteral("D"))
  47. .arg(address.index());
  48. }
  49. QString nodeToolTip(const LogicNodeConfig &config)
  50. {
  51. return std::visit(
  52. [](const auto &value) -> QString
  53. {
  54. using Config = std::decay_t<decltype(value)>;
  55. if constexpr (std::is_same_v<Config, ContactNodeConfig>)
  56. {
  57. return value.mode == ContactMode::NormallyOpen
  58. ? LogicEditorWidget::tr("常开触点 %1").arg(
  59. registerAddressText(value.address))
  60. : LogicEditorWidget::tr("常闭触点 %1").arg(
  61. registerAddressText(value.address));
  62. }
  63. else if constexpr (std::is_same_v<Config, CoilNodeConfig>)
  64. {
  65. QString mode = LogicEditorWidget::tr("普通线圈");
  66. if (value.mode == CoilMode::Set)
  67. {
  68. mode = LogicEditorWidget::tr("置位线圈");
  69. }
  70. else if (value.mode == CoilMode::Reset)
  71. {
  72. mode = LogicEditorWidget::tr("复位线圈");
  73. }
  74. return QStringLiteral("%1 %2").arg(
  75. mode, registerAddressText(value.address));
  76. }
  77. else
  78. {
  79. return LogicEditorWidget::tr("比较条件:%1 %2 %3")
  80. .arg(registerAddressText(value.address))
  81. .arg(comparisonText(value.comparison))
  82. .arg(value.value);
  83. }
  84. },
  85. config);
  86. }
  87. QString nodeAddressText(const RegisterAddress &address, bool configured)
  88. {
  89. return configured ? registerAddressText(address)
  90. : LogicEditorWidget::tr("< M 地址 >");
  91. }
  92. qreal rungHeight(const LadderRung &rung)
  93. {
  94. std::size_t maximum_branches = 1U;
  95. for (const LadderStage &stage : rung.stages)
  96. {
  97. maximum_branches = std::max(maximum_branches, stage.branches.size());
  98. }
  99. return kRungBaseHeight
  100. + static_cast<qreal>(maximum_branches - 1U) * kBranchSpacing;
  101. }
  102. void addPlaceholder(
  103. QGraphicsScene &scene,
  104. const QPointF &center,
  105. const QString &text,
  106. qreal width = kPlaceholderWidth)
  107. {
  108. const QRectF bounds(
  109. center.x() - width / 2.0,
  110. center.y() - kPlaceholderHeight / 2.0,
  111. width,
  112. kPlaceholderHeight);
  113. scene.addRect(
  114. bounds,
  115. QPen(kPlaceholderColor, 1.2, Qt::DashLine),
  116. QBrush(Qt::white));
  117. QGraphicsTextItem *label = scene.addText(text);
  118. label->setDefaultTextColor(kPlaceholderColor);
  119. label->setPos(
  120. center.x() - label->boundingRect().width() / 2.0,
  121. center.y() - label->boundingRect().height() / 2.0);
  122. }
  123. } // namespace
  124. class LogicEditorWidget::NodeItem final : public QGraphicsItem
  125. {
  126. public:
  127. NodeItem(
  128. const LogicNode &node,
  129. const std::string &rung_id,
  130. const std::string &stage_id,
  131. const QPointF &center)
  132. : node_id_(node.id),
  133. rung_id_(rung_id),
  134. stage_id_(stage_id),
  135. config_(node.config),
  136. configured_(node.configured)
  137. {
  138. setPos(center);
  139. setFlag(ItemIsSelectable, true);
  140. setZValue(1.0);
  141. setToolTip(configured_ ? nodeToolTip(config_)
  142. : LogicEditorWidget::tr(
  143. "待配置:请在属性区设置地址和参数"));
  144. }
  145. QRectF boundingRect() const override
  146. {
  147. return {-kNodeWidth / 2.0, -46.0, kNodeWidth, 92.0};
  148. }
  149. void paint(
  150. QPainter *painter,
  151. const QStyleOptionGraphicsItem *option,
  152. QWidget *) override
  153. {
  154. painter->setRenderHint(QPainter::Antialiasing, true);
  155. const bool selected = (option->state & QStyle::State_Selected) != 0;
  156. if (selected)
  157. {
  158. painter->fillRect(boundingRect(), kSelectionColor);
  159. }
  160. const QColor symbol_color = selected ? kSelectionBorderColor : kLadderColor;
  161. painter->setPen(QPen(symbol_color, selected ? 2.0 : kLadderLineWidth));
  162. QFont text_font = painter->font();
  163. text_font.setPointSizeF(9.5);
  164. painter->setFont(text_font);
  165. if (const auto *contact = std::get_if<ContactNodeConfig>(&config_))
  166. {
  167. painter->fillRect(QRectF(-24, -21, 48, 42), Qt::white);
  168. painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-18, 0));
  169. painter->drawLine(QPointF(18, 0), QPointF(kNodeTerminalX, 0));
  170. painter->drawLine(QPointF(-18, -15), QPointF(-18, 15));
  171. painter->drawLine(QPointF(18, -15), QPointF(18, 15));
  172. if (contact->mode == ContactMode::NormallyClosed)
  173. {
  174. painter->drawLine(QPointF(-23, 18), QPointF(23, -18));
  175. }
  176. painter->setPen(kLadderColor);
  177. painter->drawText(
  178. QRectF(-kNodeWidth / 2.0, -44, kNodeWidth, 18),
  179. Qt::AlignCenter,
  180. nodeAddressText(contact->address, configured_));
  181. }
  182. else if (const auto *coil = std::get_if<CoilNodeConfig>(&config_))
  183. {
  184. painter->fillRect(QRectF(-34, -23, 68, 46), Qt::white);
  185. painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-16, 0));
  186. painter->drawLine(QPointF(16, 0), QPointF(kNodeTerminalX, 0));
  187. QPainterPath left_coil;
  188. left_coil.moveTo(-2, -20);
  189. left_coil.cubicTo(-24, -16, -24, 16, -2, 20);
  190. painter->drawPath(left_coil);
  191. QPainterPath right_coil;
  192. right_coil.moveTo(2, -20);
  193. right_coil.cubicTo(24, -16, 24, 16, 2, 20);
  194. painter->drawPath(right_coil);
  195. if (coil->mode == CoilMode::Set || coil->mode == CoilMode::Reset)
  196. {
  197. painter->drawText(
  198. QRectF(-12, -14, 24, 28),
  199. Qt::AlignCenter,
  200. coil->mode == CoilMode::Set ? QStringLiteral("S")
  201. : QStringLiteral("R"));
  202. }
  203. painter->setPen(kLadderColor);
  204. painter->drawText(
  205. QRectF(-kNodeWidth / 2.0, -44, kNodeWidth, 18),
  206. Qt::AlignCenter,
  207. nodeAddressText(coil->address, configured_));
  208. }
  209. else if (const auto *comparison = std::get_if<CompareNodeConfig>(&config_))
  210. {
  211. constexpr qreal box_half_width = 47.0;
  212. const QRectF instruction_box(-box_half_width, -17, 94, 34);
  213. painter->fillRect(instruction_box.adjusted(-1, -1, 1, 1), Qt::white);
  214. painter->drawLine(
  215. QPointF(-kNodeTerminalX, 0), QPointF(-box_half_width, 0));
  216. painter->drawLine(
  217. QPointF(box_half_width, 0), QPointF(kNodeTerminalX, 0));
  218. painter->drawRect(instruction_box);
  219. painter->drawText(
  220. instruction_box,
  221. Qt::AlignCenter,
  222. QStringLiteral("%1 INT").arg(comparisonText(comparison->comparison)));
  223. painter->setPen(kLadderColor);
  224. painter->drawText(
  225. QRectF(-kNodeWidth / 2.0, -44, kNodeWidth, 18),
  226. Qt::AlignCenter,
  227. configured_ ? registerAddressText(comparison->address)
  228. : tr("< D 地址 >"));
  229. painter->drawText(
  230. QRectF(-kNodeWidth / 2.0, 22, kNodeWidth, 18),
  231. Qt::AlignCenter,
  232. configured_ ? QString::number(comparison->value)
  233. : LogicEditorWidget::tr("< 常量 >"));
  234. }
  235. }
  236. const std::string &nodeId() const { return node_id_; }
  237. const std::string &rungId() const { return rung_id_; }
  238. const std::string &stageId() const { return stage_id_; }
  239. private:
  240. std::string node_id_;
  241. std::string rung_id_;
  242. std::string stage_id_;
  243. LogicNodeConfig config_;
  244. bool configured_ = true;
  245. };
  246. class LogicEditorWidget::RungItem final : public QGraphicsItem
  247. {
  248. public:
  249. RungItem(
  250. const LadderRung &rung,
  251. int number,
  252. qreal top,
  253. qreal height,
  254. qreal scene_width)
  255. : rung_id_(rung.id),
  256. name_(QString::fromUtf8(rung.name.data(), static_cast<int>(rung.name.size()))),
  257. number_(number),
  258. height_(height),
  259. scene_width_(scene_width)
  260. {
  261. setPos(0, top);
  262. setFlag(ItemIsSelectable, true);
  263. setZValue(-2.0);
  264. }
  265. QRectF boundingRect() const override
  266. {
  267. return {20, 0, scene_width_ - 40, height_};
  268. }
  269. void paint(
  270. QPainter *painter,
  271. const QStyleOptionGraphicsItem *option,
  272. QWidget *) override
  273. {
  274. if ((option->state & QStyle::State_Selected) != 0)
  275. {
  276. painter->fillRect(
  277. boundingRect(), QColor(QStringLiteral("#f0f7fa")));
  278. }
  279. painter->setPen(QColor(QStringLiteral("#62717b")));
  280. QString title = QStringLiteral("网络 %1").arg(number_);
  281. if (!name_.isEmpty() && !name_.startsWith(QStringLiteral("网络 ")))
  282. {
  283. title += QStringLiteral(":") + name_;
  284. }
  285. painter->drawText(
  286. QRectF(28, 6, 260, 22),
  287. Qt::AlignLeft | Qt::AlignVCenter,
  288. title);
  289. painter->setPen(QPen(QColor(QStringLiteral("#d4dce1")), 1));
  290. painter->drawLine(
  291. QPointF(28, height_ - 1), QPointF(scene_width_ - 28, height_ - 1));
  292. }
  293. const std::string &rungId() const { return rung_id_; }
  294. private:
  295. std::string rung_id_;
  296. QString name_;
  297. int number_ = 0;
  298. qreal height_ = 0;
  299. qreal scene_width_ = 0;
  300. };
  301. LogicEditorWidget::LogicEditorWidget(
  302. LogicEditorService &editor_service,
  303. QWidget *parent)
  304. : QGraphicsView(parent), editor_service_(editor_service)
  305. {
  306. scene_ = new QGraphicsScene(this);
  307. setScene(scene_);
  308. setRenderHint(QPainter::Antialiasing, true);
  309. setBackgroundBrush(QColor(QStringLiteral("#ffffff")));
  310. setDragMode(QGraphicsView::RubberBandDrag);
  311. setAlignment(Qt::AlignLeft | Qt::AlignTop);
  312. connect(scene_, &QGraphicsScene::selectionChanged,
  313. this, &LogicEditorWidget::handleSelectionChanged);
  314. }
  315. void LogicEditorWidget::setLogicId(const std::string &logic_id)
  316. {
  317. if (logic_id_ == logic_id)
  318. {
  319. return;
  320. }
  321. logic_id_ = logic_id;
  322. current_rung_id_.clear();
  323. reloadLogic();
  324. }
  325. void LogicEditorWidget::setEditingEnabled(bool enabled)
  326. {
  327. editing_enabled_ = enabled;
  328. setInteractive(enabled);
  329. }
  330. void LogicEditorWidget::reloadLogic()
  331. {
  332. scene_->clear();
  333. const ControlLogic *logic = editor_service_.findLogic(logic_id_);
  334. if (logic == nullptr)
  335. {
  336. scene_->setSceneRect(0, 0, kMinimumSceneWidth, 400);
  337. return;
  338. }
  339. std::size_t maximum_stage_count = 0U;
  340. for (const LadderRung &rung : logic->rungs)
  341. {
  342. maximum_stage_count = std::max(maximum_stage_count, rung.stages.size());
  343. }
  344. const qreal scene_width = std::max(
  345. kMinimumSceneWidth,
  346. kLeftRailX + static_cast<qreal>(maximum_stage_count) * kStageWidth + 300.0);
  347. const qreal right_rail_x = scene_width - 72.0;
  348. if (current_rung_id_.empty() && !logic->rungs.empty())
  349. {
  350. current_rung_id_ = logic->rungs.front().id;
  351. }
  352. qreal top = 24.0;
  353. int rung_number = 1;
  354. for (const LadderRung &rung : logic->rungs)
  355. {
  356. const qreal height = rungHeight(rung);
  357. scene_->addItem(new RungItem(rung, rung_number, top, height, scene_width));
  358. const qreal main_y = top + kMainLineOffset;
  359. scene_->addLine(
  360. kLeftRailX, top + 38.0, kLeftRailX, top + height - 16.0,
  361. QPen(kLadderColor, 2.4));
  362. scene_->addLine(
  363. right_rail_x, top + 38.0, right_rail_x, top + height - 16.0,
  364. QPen(kLadderColor, 2.4));
  365. qreal cursor_x = kLeftRailX;
  366. for (const LadderStage &stage : rung.stages)
  367. {
  368. const qreal next_x = cursor_x + kStageWidth;
  369. const qreal left_join = cursor_x + 18.0;
  370. const qreal right_join = next_x - 18.0;
  371. scene_->addLine(
  372. cursor_x, main_y, left_join, main_y,
  373. QPen(kLadderColor, kLadderLineWidth));
  374. scene_->addLine(
  375. right_join, main_y, next_x, main_y,
  376. QPen(kLadderColor, kLadderLineWidth));
  377. if (stage.branches.size() > 1U)
  378. {
  379. const qreal bottom_y = main_y
  380. + static_cast<qreal>(stage.branches.size() - 1U) * kBranchSpacing;
  381. scene_->addLine(
  382. left_join, main_y, left_join, bottom_y,
  383. QPen(kLadderColor, kLadderLineWidth));
  384. scene_->addLine(
  385. right_join, main_y, right_join, bottom_y,
  386. QPen(kLadderColor, kLadderLineWidth));
  387. }
  388. for (std::size_t branch = 0; branch < stage.branches.size(); ++branch)
  389. {
  390. const qreal branch_y = main_y + static_cast<qreal>(branch) * kBranchSpacing;
  391. scene_->addLine(
  392. left_join, branch_y, right_join, branch_y,
  393. QPen(kLadderColor, kLadderLineWidth));
  394. scene_->addItem(new NodeItem(
  395. stage.branches.at(branch),
  396. rung.id,
  397. stage.id,
  398. QPointF((left_join + right_join) / 2.0, branch_y)));
  399. }
  400. cursor_x = next_x;
  401. }
  402. const qreal output_center_x = right_rail_x - 68.0;
  403. if (rung.stages.empty())
  404. {
  405. const qreal condition_center_x = kLeftRailX + kStageWidth / 2.0;
  406. scene_->addLine(
  407. kLeftRailX,
  408. main_y,
  409. condition_center_x - kPlaceholderWidth / 2.0,
  410. main_y,
  411. QPen(kLadderColor, kLadderLineWidth));
  412. scene_->addLine(
  413. condition_center_x + kPlaceholderWidth / 2.0,
  414. main_y,
  415. output_center_x,
  416. main_y,
  417. QPen(kLadderColor, kLadderLineWidth));
  418. addPlaceholder(
  419. *scene_,
  420. QPointF(condition_center_x, main_y),
  421. tr("添加条件"));
  422. }
  423. else
  424. {
  425. scene_->addLine(
  426. cursor_x, main_y, output_center_x, main_y,
  427. QPen(kLadderColor, kLadderLineWidth));
  428. }
  429. if (rung.output.has_value())
  430. {
  431. scene_->addLine(
  432. output_center_x, main_y, right_rail_x, main_y,
  433. QPen(kLadderColor, kLadderLineWidth));
  434. scene_->addItem(new NodeItem(
  435. *rung.output,
  436. rung.id,
  437. {},
  438. QPointF(output_center_x, main_y)));
  439. }
  440. else
  441. {
  442. constexpr qreal output_placeholder_width = 96.0;
  443. scene_->addLine(
  444. output_center_x + output_placeholder_width / 2.0,
  445. main_y,
  446. right_rail_x,
  447. main_y,
  448. QPen(kLadderColor, kLadderLineWidth));
  449. addPlaceholder(
  450. *scene_,
  451. QPointF(output_center_x, main_y),
  452. tr("输出线圈"),
  453. output_placeholder_width);
  454. }
  455. top += height + kRungGap;
  456. ++rung_number;
  457. }
  458. scene_->setSceneRect(0, 0, scene_width, std::max(400.0, top + 20.0));
  459. }
  460. void LogicEditorWidget::selectNode(const std::string &node_id)
  461. {
  462. for (QGraphicsItem *item : scene_->items())
  463. {
  464. NodeItem *node = dynamic_cast<NodeItem *>(item);
  465. if (node != nullptr)
  466. {
  467. node->setSelected(node->nodeId() == node_id);
  468. if (node->nodeId() == node_id)
  469. {
  470. ensureVisible(node);
  471. }
  472. }
  473. }
  474. }
  475. std::string LogicEditorWidget::selectedNodeId() const
  476. {
  477. for (QGraphicsItem *item : scene_->selectedItems())
  478. {
  479. const NodeItem *node = dynamic_cast<const NodeItem *>(item);
  480. if (node != nullptr)
  481. {
  482. return node->nodeId();
  483. }
  484. }
  485. return {};
  486. }
  487. std::string LogicEditorWidget::selectedRungId() const
  488. {
  489. for (QGraphicsItem *item : scene_->selectedItems())
  490. {
  491. const NodeItem *node = dynamic_cast<const NodeItem *>(item);
  492. if (node != nullptr)
  493. {
  494. return node->rungId();
  495. }
  496. const RungItem *rung = dynamic_cast<const RungItem *>(item);
  497. if (rung != nullptr)
  498. {
  499. return rung->rungId();
  500. }
  501. }
  502. return {};
  503. }
  504. std::string LogicEditorWidget::selectedStageId() const
  505. {
  506. for (QGraphicsItem *item : scene_->selectedItems())
  507. {
  508. const NodeItem *node = dynamic_cast<const NodeItem *>(item);
  509. if (node != nullptr)
  510. {
  511. return node->stageId();
  512. }
  513. }
  514. return {};
  515. }
  516. LogicEditorResult LogicEditorWidget::addRung()
  517. {
  518. const LogicEditorResult result = editor_service_.addRung(logic_id_);
  519. if (result.succeeded)
  520. {
  521. current_rung_id_ = result.id;
  522. reloadLogic();
  523. emit graphChanged();
  524. }
  525. else
  526. {
  527. reportFailure(result);
  528. }
  529. return result;
  530. }
  531. LogicEditorResult LogicEditorWidget::appendCondition(const LogicNodeConfig &config)
  532. {
  533. const LogicEditorResult result = editor_service_.appendCondition(
  534. logic_id_, currentRungId(), config);
  535. if (result.succeeded)
  536. {
  537. reloadLogic();
  538. selectNode(result.id);
  539. emit graphChanged();
  540. }
  541. else
  542. {
  543. reportFailure(result);
  544. }
  545. return result;
  546. }
  547. LogicEditorResult LogicEditorWidget::addParallelCondition(
  548. const LogicNodeConfig &config)
  549. {
  550. const std::string rung_id = selectedRungId();
  551. const std::string stage_id = selectedStageId();
  552. LogicEditorResult result;
  553. if (rung_id.empty() || stage_id.empty())
  554. {
  555. result = {false,
  556. LogicEditorError::InvalidOperation,
  557. "请先选择要添加并联分支的触点或比较条件",
  558. {}};
  559. }
  560. else
  561. {
  562. result = editor_service_.addParallelCondition(
  563. logic_id_, rung_id, stage_id, config);
  564. }
  565. if (result.succeeded)
  566. {
  567. reloadLogic();
  568. selectNode(result.id);
  569. emit graphChanged();
  570. }
  571. else
  572. {
  573. reportFailure(result);
  574. }
  575. return result;
  576. }
  577. LogicEditorResult LogicEditorWidget::setOutput(const LogicNodeConfig &config)
  578. {
  579. const LogicEditorResult result = editor_service_.setOutput(
  580. logic_id_, currentRungId(), config);
  581. if (result.succeeded)
  582. {
  583. reloadLogic();
  584. selectNode(result.id);
  585. emit graphChanged();
  586. }
  587. else
  588. {
  589. reportFailure(result);
  590. }
  591. return result;
  592. }
  593. LogicEditorResult LogicEditorWidget::deleteSelected()
  594. {
  595. const std::string node_id = selectedNodeId();
  596. LogicEditorResult result;
  597. if (!node_id.empty())
  598. {
  599. result = editor_service_.removeNode(logic_id_, node_id);
  600. }
  601. else
  602. {
  603. const std::string rung_id = selectedRungId();
  604. if (rung_id.empty())
  605. {
  606. return {false, LogicEditorError::InvalidOperation,
  607. "请先选择要删除的逻辑节点或网络", {}};
  608. }
  609. result = editor_service_.removeRung(logic_id_, rung_id);
  610. if (result.succeeded && current_rung_id_ == rung_id)
  611. {
  612. current_rung_id_ = editor_service_.firstRungId(logic_id_);
  613. }
  614. }
  615. if (result.succeeded)
  616. {
  617. reloadLogic();
  618. emit nodeSelected({});
  619. emit graphChanged();
  620. }
  621. else
  622. {
  623. reportFailure(result);
  624. }
  625. return result;
  626. }
  627. void LogicEditorWidget::resizeEvent(QResizeEvent *event)
  628. {
  629. QGraphicsView::resizeEvent(event);
  630. }
  631. void LogicEditorWidget::handleSelectionChanged()
  632. {
  633. const std::string rung_id = selectedRungId();
  634. if (!rung_id.empty())
  635. {
  636. current_rung_id_ = rung_id;
  637. }
  638. emit nodeSelected(QString::fromStdString(selectedNodeId()));
  639. }
  640. void LogicEditorWidget::reportFailure(const LogicEditorResult &result)
  641. {
  642. emit editorError(QString::fromStdString(result.message));
  643. }
  644. std::string LogicEditorWidget::currentRungId() const
  645. {
  646. const std::string selected = selectedRungId();
  647. if (!selected.empty())
  648. {
  649. return selected;
  650. }
  651. if (!current_rung_id_.empty())
  652. {
  653. return current_rung_id_;
  654. }
  655. return editor_service_.firstRungId(logic_id_);
  656. }