综合平台编程器项目的远程存储
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.
 
 
 
 

1704 lines
58 KiB

  1. #include "logic_editor_service.h"
  2. #include "project_service.h"
  3. #include "domain/project_limits.h"
  4. #include <algorithm>
  5. #include <cctype>
  6. #include <cstddef>
  7. #include <iterator>
  8. #include <unordered_set>
  9. #include <type_traits>
  10. #include <utility>
  11. namespace {
  12. bool isBlank(const std::string &value)
  13. {
  14. return value.empty()
  15. || std::all_of(
  16. value.cbegin(), value.cend(),
  17. [](unsigned char character) { return std::isspace(character) != 0; });
  18. }
  19. std::string makeUniqueLogicId(const Project &project)
  20. {
  21. int suffix = 1;
  22. while (true)
  23. {
  24. const std::string candidate = "logic-" + std::to_string(suffix);
  25. const bool found = std::any_of(
  26. project.controlLogics.cbegin(), project.controlLogics.cend(),
  27. [&candidate](const ControlLogic &logic) { return logic.id == candidate; });
  28. if (!found)
  29. {
  30. return candidate;
  31. }
  32. ++suffix;
  33. }
  34. }
  35. LogicNode makeNode(const std::string &id, const LogicNodeConfig &config)
  36. {
  37. return {id, config, false};
  38. }
  39. ConditionExpression makeContainer(
  40. const std::string &id,
  41. ConditionExpressionKind kind,
  42. ConditionExpression first,
  43. ConditionExpression second)
  44. {
  45. ConditionExpression expression;
  46. expression.id = id;
  47. expression.kind = kind;
  48. expression.children.push_back(std::move(first));
  49. expression.children.push_back(std::move(second));
  50. return expression;
  51. }
  52. ConditionExpression *findParentExpression(
  53. ConditionExpression &expression, const std::string &child_id)
  54. {
  55. for (ConditionExpression &child : expression.children)
  56. {
  57. if (child.id == child_id)
  58. {
  59. return &expression;
  60. }
  61. if (ConditionExpression *parent = findParentExpression(child, child_id))
  62. {
  63. return parent;
  64. }
  65. }
  66. return nullptr;
  67. }
  68. using NodeIdSet = std::unordered_set<std::string>;
  69. NodeIdSet conditionLeafIds(const ConditionExpression &expression)
  70. {
  71. NodeIdSet ids;
  72. if (expression.kind == ConditionExpressionKind::Node
  73. || expression.kind == ConditionExpressionKind::Wire)
  74. {
  75. ids.insert(expression.id);
  76. return ids;
  77. }
  78. for (const ConditionExpression &child : expression.children)
  79. {
  80. NodeIdSet child_ids = conditionLeafIds(child);
  81. ids.insert(child_ids.cbegin(), child_ids.cend());
  82. }
  83. return ids;
  84. }
  85. int expressionColumns(const ConditionExpression &expression)
  86. {
  87. if (expression.kind == ConditionExpressionKind::Node)
  88. {
  89. return 1;
  90. }
  91. if (expression.kind == ConditionExpressionKind::Wire)
  92. {
  93. return expression.wire->columnSpan;
  94. }
  95. int columns = expression.kind == ConditionExpressionKind::Series ? 0 : 1;
  96. for (const ConditionExpression &child : expression.children)
  97. {
  98. const int child_columns = expressionColumns(child);
  99. columns = expression.kind == ConditionExpressionKind::Series
  100. ? columns + child_columns : std::max(columns, child_columns);
  101. }
  102. return columns;
  103. }
  104. bool isSubset(const NodeIdSet &subset, const NodeIdSet &values)
  105. {
  106. return std::all_of(
  107. subset.cbegin(), subset.cend(),
  108. [&values](const std::string &value) { return values.count(value) != 0U; });
  109. }
  110. bool sameValues(const NodeIdSet &left, const NodeIdSet &right)
  111. {
  112. return left.size() == right.size() && isSubset(left, right);
  113. }
  114. void addParallelSibling(
  115. ConditionExpression *target,
  116. ConditionExpression branch,
  117. const std::string &container_id)
  118. {
  119. if (target->kind == ConditionExpressionKind::Parallel)
  120. {
  121. target->children.push_back(std::move(branch));
  122. return;
  123. }
  124. ConditionExpression original = std::move(*target);
  125. *target = makeContainer(
  126. container_id,
  127. ConditionExpressionKind::Parallel,
  128. std::move(original),
  129. std::move(branch));
  130. }
  131. bool addParallelForSelection(
  132. ConditionExpression *expression,
  133. const NodeIdSet &selected_ids,
  134. ConditionExpression *branch,
  135. const std::string &parallel_id,
  136. const std::string &series_id)
  137. {
  138. const NodeIdSet expression_ids = conditionLeafIds(*expression);
  139. if (sameValues(expression_ids, selected_ids))
  140. {
  141. addParallelSibling(expression, std::move(*branch), parallel_id);
  142. return true;
  143. }
  144. std::vector<NodeIdSet> child_ids;
  145. child_ids.reserve(expression->children.size());
  146. std::vector<std::size_t> matching_children;
  147. for (std::size_t index = 0; index < expression->children.size(); ++index)
  148. {
  149. child_ids.push_back(conditionLeafIds(expression->children[index]));
  150. const bool intersects = std::any_of(
  151. child_ids.back().cbegin(), child_ids.back().cend(),
  152. [&selected_ids](const std::string &id)
  153. {
  154. return selected_ids.count(id) != 0U;
  155. });
  156. if (intersects)
  157. {
  158. matching_children.push_back(index);
  159. }
  160. }
  161. if (matching_children.empty())
  162. {
  163. return false;
  164. }
  165. if (matching_children.size() == 1U)
  166. {
  167. const std::size_t child_index = matching_children.front();
  168. if (expression->kind == ConditionExpressionKind::Parallel
  169. && sameValues(child_ids[child_index], selected_ids))
  170. {
  171. expression->children.push_back(std::move(*branch));
  172. return true;
  173. }
  174. return addParallelForSelection(
  175. &expression->children[child_index],
  176. selected_ids,
  177. branch,
  178. parallel_id,
  179. series_id);
  180. }
  181. if (expression->kind != ConditionExpressionKind::Series)
  182. {
  183. return false;
  184. }
  185. const std::size_t first = matching_children.front();
  186. const std::size_t last = matching_children.back();
  187. if (last - first + 1U != matching_children.size())
  188. {
  189. return false;
  190. }
  191. NodeIdSet range_ids;
  192. for (std::size_t index = first; index <= last; ++index)
  193. {
  194. range_ids.insert(child_ids[index].cbegin(), child_ids[index].cend());
  195. }
  196. if (!sameValues(range_ids, selected_ids))
  197. {
  198. return false;
  199. }
  200. ConditionExpression range;
  201. range.id = series_id;
  202. range.kind = ConditionExpressionKind::Series;
  203. auto range_begin = expression->children.begin() + static_cast<std::ptrdiff_t>(first);
  204. auto range_end = expression->children.begin() + static_cast<std::ptrdiff_t>(last + 1U);
  205. range.children.insert(
  206. range.children.end(),
  207. std::make_move_iterator(range_begin),
  208. std::make_move_iterator(range_end));
  209. range_begin = expression->children.erase(range_begin, range_end);
  210. ConditionExpression parallel = makeContainer(
  211. parallel_id,
  212. ConditionExpressionKind::Parallel,
  213. std::move(range),
  214. std::move(*branch));
  215. expression->children.insert(range_begin, std::move(parallel));
  216. return true;
  217. }
  218. bool removeExpressionRecursive(
  219. ConditionExpression *expression, const std::string &expression_id)
  220. {
  221. if (expression == nullptr
  222. || expression->kind == ConditionExpressionKind::Node
  223. || expression->kind == ConditionExpressionKind::Wire)
  224. {
  225. return false;
  226. }
  227. const auto removable = std::find_if(
  228. expression->children.begin(),
  229. expression->children.end(),
  230. [&expression_id](const ConditionExpression &child)
  231. {
  232. return child.id == expression_id;
  233. });
  234. if (removable != expression->children.end())
  235. {
  236. expression->children.erase(removable);
  237. return true;
  238. }
  239. for (ConditionExpression &child : expression->children)
  240. {
  241. if (removeExpressionRecursive(&child, expression_id))
  242. {
  243. return true;
  244. }
  245. }
  246. return false;
  247. }
  248. std::optional<int> selectedExpressionColumns(
  249. const ConditionExpression &expression,
  250. const NodeIdSet &selected_ids)
  251. {
  252. const NodeIdSet expression_ids = conditionLeafIds(expression);
  253. if (sameValues(expression_ids, selected_ids))
  254. {
  255. return expressionColumns(expression);
  256. }
  257. if (expression.kind == ConditionExpressionKind::Node
  258. || expression.kind == ConditionExpressionKind::Wire)
  259. {
  260. return std::nullopt;
  261. }
  262. std::vector<NodeIdSet> child_ids;
  263. std::vector<std::size_t> matching_children;
  264. child_ids.reserve(expression.children.size());
  265. for (std::size_t index = 0; index < expression.children.size(); ++index)
  266. {
  267. child_ids.push_back(conditionLeafIds(expression.children[index]));
  268. const bool intersects = std::any_of(
  269. child_ids.back().cbegin(), child_ids.back().cend(),
  270. [&selected_ids](const std::string &id)
  271. {
  272. return selected_ids.count(id) != 0U;
  273. });
  274. if (intersects)
  275. {
  276. matching_children.push_back(index);
  277. }
  278. }
  279. if (matching_children.size() == 1U)
  280. {
  281. return selectedExpressionColumns(
  282. expression.children[matching_children.front()], selected_ids);
  283. }
  284. if (matching_children.empty()
  285. || expression.kind != ConditionExpressionKind::Series)
  286. {
  287. return std::nullopt;
  288. }
  289. const std::size_t first = matching_children.front();
  290. const std::size_t last = matching_children.back();
  291. if (last - first + 1U != matching_children.size())
  292. {
  293. return std::nullopt;
  294. }
  295. NodeIdSet range_ids;
  296. int columns = 0;
  297. for (std::size_t index = first; index <= last; ++index)
  298. {
  299. range_ids.insert(child_ids[index].cbegin(), child_ids[index].cend());
  300. columns += expressionColumns(expression.children[index]);
  301. }
  302. return sameValues(range_ids, selected_ids)
  303. ? std::optional<int>{columns} : std::nullopt;
  304. }
  305. LadderRung *findEditableRung(
  306. Project &project,
  307. const std::string &logic_id,
  308. const std::string &rung_id)
  309. {
  310. const auto logic = std::find_if(
  311. project.controlLogics.begin(), project.controlLogics.end(),
  312. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  313. if (logic == project.controlLogics.end())
  314. {
  315. return nullptr;
  316. }
  317. const auto rung = std::find_if(
  318. logic->rungs.begin(), logic->rungs.end(),
  319. [&rung_id](const LadderRung &candidate) { return candidate.id == rung_id; });
  320. return rung == logic->rungs.end() ? nullptr : &*rung;
  321. }
  322. } // namespace
  323. LogicEditorService::LogicEditorService(ProjectService &project_service)
  324. : project_service_(project_service)
  325. {
  326. }
  327. LogicEditorService::HistoryState LogicEditorService::captureState() const
  328. {
  329. return {project_service_.project().controlLogics};
  330. }
  331. void LogicEditorService::recordHistory(HistoryState before)
  332. {
  333. const HistoryState after = captureState();
  334. history_.record(std::move(before), after, &LogicEditorService::statesEqual);
  335. }
  336. bool LogicEditorService::statesEqual(
  337. const HistoryState &left, const HistoryState &right)
  338. {
  339. if (left.logics.size() != right.logics.size())
  340. {
  341. return false;
  342. }
  343. for (std::size_t index = 0; index < left.logics.size(); ++index)
  344. {
  345. if (!logicsEqual(left.logics[index], right.logics[index]))
  346. {
  347. return false;
  348. }
  349. }
  350. return true;
  351. }
  352. bool LogicEditorService::logicsEqual(
  353. const ControlLogic &left, const ControlLogic &right)
  354. {
  355. if (left.id != right.id || left.name != right.name
  356. || left.enabled != right.enabled || left.rungs.size() != right.rungs.size())
  357. {
  358. return false;
  359. }
  360. for (std::size_t index = 0; index < left.rungs.size(); ++index)
  361. {
  362. if (!rungsEqual(left.rungs[index], right.rungs[index]))
  363. {
  364. return false;
  365. }
  366. }
  367. return true;
  368. }
  369. bool LogicEditorService::rungsEqual(
  370. const LadderRung &left, const LadderRung &right)
  371. {
  372. if (left.id != right.id || left.name != right.name
  373. || left.comment != right.comment
  374. || left.condition.has_value() != right.condition.has_value()
  375. || left.output.has_value() != right.output.has_value())
  376. {
  377. return false;
  378. }
  379. return (!left.condition.has_value()
  380. || expressionsEqual(*left.condition, *right.condition))
  381. && (!left.output.has_value() || nodesEqual(*left.output, *right.output));
  382. }
  383. bool LogicEditorService::expressionsEqual(
  384. const ConditionExpression &left, const ConditionExpression &right)
  385. {
  386. if (left.id != right.id || left.kind != right.kind
  387. || left.node.has_value() != right.node.has_value()
  388. || left.wire.has_value() != right.wire.has_value()
  389. || left.children.size() != right.children.size())
  390. {
  391. return false;
  392. }
  393. if (left.node.has_value() && !nodesEqual(*left.node, *right.node))
  394. {
  395. return false;
  396. }
  397. if (left.wire.has_value()
  398. && left.wire->columnSpan != right.wire->columnSpan)
  399. {
  400. return false;
  401. }
  402. for (std::size_t index = 0; index < left.children.size(); ++index)
  403. {
  404. if (!expressionsEqual(left.children[index], right.children[index]))
  405. {
  406. return false;
  407. }
  408. }
  409. return true;
  410. }
  411. bool LogicEditorService::nodesEqual(
  412. const LogicNode &left, const LogicNode &right)
  413. {
  414. return left.id == right.id && left.config.index() == right.config.index()
  415. && left.configured == right.configured && configsEqual(left.config, right.config);
  416. }
  417. bool LogicEditorService::configsEqual(
  418. const LogicNodeConfig &left, const LogicNodeConfig &right)
  419. {
  420. return std::visit(
  421. [](const auto &left_config, const auto &right_config)
  422. {
  423. using Left = std::decay_t<decltype(left_config)>;
  424. using Right = std::decay_t<decltype(right_config)>;
  425. if constexpr (!std::is_same_v<Left, Right>)
  426. {
  427. return false;
  428. }
  429. else if constexpr (std::is_same_v<Left, ContactNodeConfig>)
  430. {
  431. return left_config.address == right_config.address
  432. && left_config.mode == right_config.mode;
  433. }
  434. else if constexpr (std::is_same_v<Left, EdgeContactNodeConfig>)
  435. {
  436. return left_config.address == right_config.address
  437. && left_config.mode == right_config.mode;
  438. }
  439. else if constexpr (std::is_same_v<Left, TimerContactNodeConfig>)
  440. {
  441. return left_config.address == right_config.address
  442. && left_config.mode == right_config.mode;
  443. }
  444. else if constexpr (std::is_same_v<Left, CounterContactNodeConfig>)
  445. {
  446. return left_config.address == right_config.address
  447. && left_config.mode == right_config.mode;
  448. }
  449. else if constexpr (std::is_same_v<Left, CoilNodeConfig>)
  450. {
  451. return left_config.address == right_config.address
  452. && left_config.mode == right_config.mode;
  453. }
  454. else if constexpr (std::is_same_v<Left, CompareNodeConfig>)
  455. {
  456. return left_config.address == right_config.address
  457. && left_config.comparison == right_config.comparison
  458. && left_config.value == right_config.value;
  459. }
  460. else if constexpr (std::is_same_v<Left, TonNodeConfig>)
  461. {
  462. return left_config.address == right_config.address
  463. && left_config.presetMs == right_config.presetMs;
  464. }
  465. else if constexpr (std::is_same_v<Left, CounterNodeConfig>)
  466. {
  467. return left_config.address == right_config.address
  468. && left_config.mode == right_config.mode
  469. && left_config.currentValueAddress == right_config.currentValueAddress
  470. && left_config.preset.kind == right_config.preset.kind
  471. && left_config.preset.address == right_config.preset.address
  472. && left_config.preset.constant == right_config.preset.constant
  473. && left_config.resetAddress == right_config.resetAddress;
  474. }
  475. else if constexpr (std::is_same_v<Left, MoveNodeConfig>)
  476. {
  477. return left_config.source.kind == right_config.source.kind
  478. && left_config.source.address == right_config.source.address
  479. && left_config.source.constant == right_config.source.constant
  480. && left_config.destination == right_config.destination;
  481. }
  482. else
  483. {
  484. return left_config.operation == right_config.operation
  485. && left_config.left.kind == right_config.left.kind
  486. && left_config.left.address == right_config.left.address
  487. && left_config.left.constant == right_config.left.constant
  488. && left_config.right.kind == right_config.right.kind
  489. && left_config.right.address == right_config.right.address
  490. && left_config.right.constant == right_config.right.constant
  491. && left_config.destination == right_config.destination;
  492. }
  493. },
  494. left,
  495. right);
  496. }
  497. LogicEditorResult LogicEditorService::historyFailure(const std::string &message)
  498. {
  499. return {false, LogicEditorError::InvalidOperation, message, {}};
  500. }
  501. const ControlLogic *LogicEditorService::findLogic(const std::string &logic_id) const
  502. {
  503. const auto &logics = project_service_.project().controlLogics;
  504. const auto logic = std::find_if(
  505. logics.cbegin(), logics.cend(),
  506. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  507. return logic == logics.cend() ? nullptr : &*logic;
  508. }
  509. const LadderRung *LogicEditorService::findRung(
  510. const std::string &logic_id, const std::string &rung_id) const
  511. {
  512. const ControlLogic *logic = findLogic(logic_id);
  513. if (logic == nullptr)
  514. {
  515. return nullptr;
  516. }
  517. const auto rung = std::find_if(
  518. logic->rungs.cbegin(), logic->rungs.cend(),
  519. [&rung_id](const LadderRung &candidate) { return candidate.id == rung_id; });
  520. return rung == logic->rungs.cend() ? nullptr : &*rung;
  521. }
  522. const LogicNode *LogicEditorService::findNode(
  523. const std::string &logic_id, const std::string &node_id) const
  524. {
  525. const ControlLogic *logic = findLogic(logic_id);
  526. if (logic == nullptr)
  527. {
  528. return nullptr;
  529. }
  530. for (const LadderRung &rung : logic->rungs)
  531. {
  532. if (rung.output.has_value() && rung.output->id == node_id)
  533. {
  534. return &*rung.output;
  535. }
  536. if (rung.condition.has_value())
  537. {
  538. if (const LogicNode *node = findConditionNode(*rung.condition, node_id))
  539. {
  540. return node;
  541. }
  542. }
  543. }
  544. return nullptr;
  545. }
  546. const ConditionExpression *LogicEditorService::findExpression(
  547. const std::string &logic_id,
  548. const std::string &rung_id,
  549. const std::string &expression_id) const
  550. {
  551. const LadderRung *rung = findRung(logic_id, rung_id);
  552. return rung == nullptr || !rung->condition.has_value()
  553. ? nullptr : findConditionExpression(*rung->condition, expression_id);
  554. }
  555. std::string LogicEditorService::firstLogicId() const
  556. {
  557. const auto &logics = project_service_.project().controlLogics;
  558. return logics.empty() ? std::string{} : logics.front().id;
  559. }
  560. std::string LogicEditorService::firstRungId(const std::string &logic_id) const
  561. {
  562. const ControlLogic *logic = findLogic(logic_id);
  563. return logic == nullptr || logic->rungs.empty() ? std::string{} : logic->rungs.front().id;
  564. }
  565. std::string LogicEditorService::rungIdForNode(
  566. const std::string &logic_id, const std::string &node_id) const
  567. {
  568. const ControlLogic *logic = findLogic(logic_id);
  569. if (logic == nullptr)
  570. {
  571. return {};
  572. }
  573. for (const LadderRung &rung : logic->rungs)
  574. {
  575. if ((rung.output.has_value() && rung.output->id == node_id)
  576. || (rung.condition.has_value()
  577. && findConditionNode(*rung.condition, node_id) != nullptr))
  578. {
  579. return rung.id;
  580. }
  581. }
  582. return {};
  583. }
  584. std::string LogicEditorService::registerCommentFor(
  585. const RegisterAddress &address) const
  586. {
  587. const RegisterComment *comment = project_service_.project().findRegisterComment(address);
  588. return comment == nullptr ? std::string{} : comment->text;
  589. }
  590. LogicEditorResult LogicEditorService::ensureDefaultLogic()
  591. {
  592. if (!project_service_.project().controlLogics.empty())
  593. {
  594. return {true, LogicEditorError::None, {}, firstLogicId()};
  595. }
  596. ControlLogic logic;
  597. logic.id = "logic-1";
  598. logic.name = "控制逻辑 1";
  599. logic.rungs.push_back({"rung-1", "网络 1", {}, std::nullopt, std::nullopt});
  600. Project &project = project_service_.editProject();
  601. project.controlLogics.push_back(std::move(logic));
  602. return {true, LogicEditorError::None, {}, project.controlLogics.back().id};
  603. }
  604. LogicEditorResult LogicEditorService::addLogic(const std::string &name)
  605. {
  606. if (isBlank(name))
  607. {
  608. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能为空");
  609. }
  610. const Project &current = project_service_.project();
  611. if (current.controlLogics.size() >= ProjectLimits::kMaximumControlLogics)
  612. {
  613. return failure(LogicEditorError::InvalidOperation, "单个工程最多包含 128 组控制逻辑");
  614. }
  615. if (name.size() > ProjectLimits::kMaximumTextBytes)
  616. {
  617. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能超过 4096 个 UTF-8 字节");
  618. }
  619. const bool duplicate = std::any_of(
  620. current.controlLogics.cbegin(), current.controlLogics.cend(),
  621. [&name](const ControlLogic &logic) { return logic.name == name; });
  622. if (duplicate)
  623. {
  624. return failure(LogicEditorError::DuplicateName, "控制逻辑名称必须唯一");
  625. }
  626. ControlLogic logic;
  627. logic.id = makeUniqueLogicId(current);
  628. logic.name = name;
  629. logic.rungs.push_back({"rung-1", "网络 1", {}, std::nullopt, std::nullopt});
  630. HistoryState before = captureState();
  631. Project &project = project_service_.editProject();
  632. project.controlLogics.push_back(std::move(logic));
  633. recordHistory(std::move(before));
  634. return {true, LogicEditorError::None, {}, project.controlLogics.back().id};
  635. }
  636. LogicEditorResult LogicEditorService::renameLogic(
  637. const std::string &logic_id, const std::string &name)
  638. {
  639. if (isBlank(name))
  640. {
  641. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能为空");
  642. }
  643. if (name.size() > ProjectLimits::kMaximumTextBytes)
  644. {
  645. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能超过 4096 个 UTF-8 字节");
  646. }
  647. const Project &current = project_service_.project();
  648. const auto logic = std::find_if(
  649. current.controlLogics.cbegin(), current.controlLogics.cend(),
  650. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  651. if (logic == current.controlLogics.cend())
  652. {
  653. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  654. }
  655. const bool duplicate = std::any_of(
  656. current.controlLogics.cbegin(), current.controlLogics.cend(),
  657. [&logic_id, &name](const ControlLogic &candidate)
  658. {
  659. return candidate.id != logic_id && candidate.name == name;
  660. });
  661. if (duplicate)
  662. {
  663. return failure(LogicEditorError::DuplicateName, "控制逻辑名称必须唯一");
  664. }
  665. if (logic->name == name)
  666. {
  667. return {true, LogicEditorError::None, {}, logic_id};
  668. }
  669. HistoryState before = captureState();
  670. Project &project = project_service_.editProject();
  671. auto editable = std::find_if(
  672. project.controlLogics.begin(), project.controlLogics.end(),
  673. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  674. editable->name = name;
  675. recordHistory(std::move(before));
  676. return {true, LogicEditorError::None, {}, logic_id};
  677. }
  678. LogicEditorResult LogicEditorService::removeLogic(const std::string &logic_id)
  679. {
  680. const Project &current = project_service_.project();
  681. const auto logic = std::find_if(
  682. current.controlLogics.cbegin(), current.controlLogics.cend(),
  683. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  684. if (logic == current.controlLogics.cend())
  685. {
  686. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  687. }
  688. if (current.controlLogics.size() <= 1)
  689. {
  690. return failure(LogicEditorError::LastLogicRequired, "工程至少需要保留一个控制逻辑");
  691. }
  692. HistoryState before = captureState();
  693. Project &project = project_service_.editProject();
  694. project.controlLogics.erase(
  695. std::remove_if(
  696. project.controlLogics.begin(), project.controlLogics.end(),
  697. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; }),
  698. project.controlLogics.end());
  699. recordHistory(std::move(before));
  700. return {true, LogicEditorError::None, {}, logic_id};
  701. }
  702. LogicEditorResult LogicEditorService::moveLogic(const std::string &logic_id, int offset)
  703. {
  704. if (offset != -1 && offset != 1)
  705. {
  706. return failure(LogicEditorError::InvalidOperation, "控制逻辑每次只能上移或下移一位");
  707. }
  708. const Project &current = project_service_.project();
  709. const auto logic = std::find_if(
  710. current.controlLogics.cbegin(), current.controlLogics.cend(),
  711. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  712. if (logic == current.controlLogics.cend())
  713. {
  714. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  715. }
  716. const auto index = std::distance(current.controlLogics.cbegin(), logic);
  717. const std::ptrdiff_t target_index = index + offset;
  718. if (target_index < 0
  719. || target_index
  720. >= static_cast<std::ptrdiff_t>(current.controlLogics.size()))
  721. {
  722. return failure(LogicEditorError::InvalidOperation, "控制逻辑已经位于目标边界");
  723. }
  724. HistoryState before = captureState();
  725. Project &project = project_service_.editProject();
  726. std::iter_swap(
  727. project.controlLogics.begin() + index,
  728. project.controlLogics.begin() + target_index);
  729. recordHistory(std::move(before));
  730. return {true, LogicEditorError::None, {}, logic_id};
  731. }
  732. LogicEditorResult LogicEditorService::setLogicEnabled(
  733. const std::string &logic_id, bool enabled)
  734. {
  735. if (findLogic(logic_id) == nullptr)
  736. {
  737. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  738. }
  739. const ControlLogic *existing = findLogic(logic_id);
  740. if (existing->enabled == enabled)
  741. {
  742. return {true, LogicEditorError::None, {}, logic_id};
  743. }
  744. HistoryState before = captureState();
  745. Project &project = project_service_.editProject();
  746. auto logic = std::find_if(
  747. project.controlLogics.begin(), project.controlLogics.end(),
  748. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  749. logic->enabled = enabled;
  750. recordHistory(std::move(before));
  751. return {true, LogicEditorError::None, {}, logic_id};
  752. }
  753. LogicEditorResult LogicEditorService::addRung(const std::string &logic_id)
  754. {
  755. const ControlLogic *logic = findLogic(logic_id);
  756. if (logic == nullptr)
  757. {
  758. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  759. }
  760. if (logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
  761. {
  762. return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
  763. }
  764. LadderRung rung;
  765. rung.id = makeUniqueRungId(*logic);
  766. rung.name = "网络 " + std::to_string(logic->rungs.size() + 1U);
  767. HistoryState before = captureState();
  768. Project &project = project_service_.editProject();
  769. auto target = std::find_if(
  770. project.controlLogics.begin(), project.controlLogics.end(),
  771. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  772. target->rungs.push_back(std::move(rung));
  773. recordHistory(std::move(before));
  774. return {true, LogicEditorError::None, {}, target->rungs.back().id};
  775. }
  776. LogicEditorResult LogicEditorService::removeRung(
  777. const std::string &logic_id, const std::string &rung_id)
  778. {
  779. const ControlLogic *logic = findLogic(logic_id);
  780. if (logic == nullptr)
  781. {
  782. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  783. }
  784. if (findRung(logic_id, rung_id) == nullptr)
  785. {
  786. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  787. }
  788. if (logic->rungs.size() == 1U)
  789. {
  790. return failure(LogicEditorError::InvalidOperation, "控制逻辑至少需要保留一个网络");
  791. }
  792. HistoryState before = captureState();
  793. Project &project = project_service_.editProject();
  794. auto target = std::find_if(
  795. project.controlLogics.begin(), project.controlLogics.end(),
  796. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  797. target->rungs.erase(
  798. std::remove_if(
  799. target->rungs.begin(), target->rungs.end(),
  800. [&rung_id](const LadderRung &rung) { return rung.id == rung_id; }),
  801. target->rungs.end());
  802. recordHistory(std::move(before));
  803. return {true, LogicEditorError::None, {}, rung_id};
  804. }
  805. LogicEditorResult LogicEditorService::updateRungComment(
  806. const std::string &logic_id,
  807. const std::string &rung_id,
  808. const std::string &comment)
  809. {
  810. const LadderRung *rung = findRung(logic_id, rung_id);
  811. if (rung == nullptr)
  812. {
  813. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  814. }
  815. if (comment.size() > ProjectLimits::kMaximumTextBytes)
  816. {
  817. return failure(
  818. LogicEditorError::InvalidOperation,
  819. "梯形图网络注释不能超过 4096 个 UTF-8 字节");
  820. }
  821. if (rung->comment == comment)
  822. {
  823. return {true, LogicEditorError::None, {}, rung_id};
  824. }
  825. HistoryState before = captureState();
  826. LadderRung &editable = *findEditableRung(
  827. project_service_.editProject(), logic_id, rung_id);
  828. editable.comment = comment;
  829. recordHistory(std::move(before));
  830. return {true, LogicEditorError::None, {}, rung_id};
  831. }
  832. LogicEditorResult LogicEditorService::appendCondition(
  833. const std::string &logic_id,
  834. const std::string &rung_id,
  835. const LogicNodeConfig &config)
  836. {
  837. const ControlLogic *logic = findLogic(logic_id);
  838. if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
  839. {
  840. return failure(
  841. logic == nullptr ? LogicEditorError::LogicNotFound : LogicEditorError::RungNotFound,
  842. logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
  843. }
  844. if (!isConditionConfig(config))
  845. {
  846. return failure(LogicEditorError::InvalidNode, "梯形图条件不能使用线圈节点");
  847. }
  848. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  849. ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
  850. HistoryState before = captureState();
  851. Project &project = project_service_.editProject();
  852. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  853. if (!rung->condition.has_value())
  854. {
  855. rung->condition = std::move(leaf);
  856. }
  857. else if (rung->condition->kind == ConditionExpressionKind::Series)
  858. {
  859. rung->condition->children.push_back(std::move(leaf));
  860. }
  861. else
  862. {
  863. rung->condition = makeContainer(
  864. makeUniqueExpressionId(*logic),
  865. ConditionExpressionKind::Series,
  866. std::move(*rung->condition),
  867. std::move(leaf));
  868. }
  869. std::string validation_error;
  870. if (!rung->validate(&validation_error))
  871. {
  872. project.controlLogics = std::move(before.logics);
  873. return failure(LogicEditorError::InvalidOperation, validation_error);
  874. }
  875. recordHistory(std::move(before));
  876. return {true, LogicEditorError::None, {}, node_id};
  877. }
  878. LogicEditorResult LogicEditorService::appendWire(
  879. const std::string &logic_id,
  880. const std::string &rung_id,
  881. int column_span)
  882. {
  883. const ControlLogic *logic = findLogic(logic_id);
  884. if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
  885. {
  886. return failure(
  887. logic == nullptr ? LogicEditorError::LogicNotFound
  888. : LogicEditorError::RungNotFound,
  889. logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
  890. }
  891. WireSegment wire{column_span};
  892. std::string error;
  893. if (!wire.validate(&error))
  894. {
  895. return failure(LogicEditorError::InvalidOperation, error);
  896. }
  897. const std::string wire_id = makeUniqueWireId(*logic);
  898. ConditionExpression leaf = ConditionExpression::fromWire(wire_id, column_span);
  899. HistoryState before = captureState();
  900. LadderRung *rung = findEditableRung(
  901. project_service_.editProject(), logic_id, rung_id);
  902. if (!rung->condition.has_value())
  903. {
  904. rung->condition = std::move(leaf);
  905. }
  906. else if (rung->condition->kind == ConditionExpressionKind::Series)
  907. {
  908. rung->condition->children.push_back(std::move(leaf));
  909. }
  910. else
  911. {
  912. rung->condition = makeContainer(
  913. makeUniqueExpressionId(*logic),
  914. ConditionExpressionKind::Series,
  915. std::move(*rung->condition),
  916. std::move(leaf));
  917. }
  918. std::string validation_error;
  919. if (!rung->validate(&validation_error))
  920. {
  921. project_service_.editProject().controlLogics = std::move(before.logics);
  922. return failure(LogicEditorError::InvalidOperation, validation_error);
  923. }
  924. recordHistory(std::move(before));
  925. return {true, LogicEditorError::None, {}, wire_id};
  926. }
  927. LogicEditorResult LogicEditorService::insertConditionAfter(
  928. const std::string &logic_id,
  929. const std::string &rung_id,
  930. const std::string &target_node_id,
  931. const LogicNodeConfig &config)
  932. {
  933. const ControlLogic *logic = findLogic(logic_id);
  934. if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
  935. {
  936. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  937. }
  938. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  939. if (!isConditionConfig(config)
  940. || existing_rung == nullptr
  941. || !existing_rung->condition.has_value()
  942. || findConditionNode(*existing_rung->condition, target_node_id) == nullptr)
  943. {
  944. return failure(LogicEditorError::NodeNotFound, "未找到串联插入目标");
  945. }
  946. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  947. ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
  948. HistoryState before = captureState();
  949. Project &project = project_service_.editProject();
  950. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  951. ConditionExpression *target = findConditionExpression(*rung->condition, target_node_id);
  952. ConditionExpression *parent = findParentExpression(*rung->condition, target_node_id);
  953. if (parent != nullptr && parent->kind == ConditionExpressionKind::Series)
  954. {
  955. const auto target_iterator = std::find_if(
  956. parent->children.begin(), parent->children.end(),
  957. [&target_node_id](const ConditionExpression &child)
  958. {
  959. return child.id == target_node_id;
  960. });
  961. parent->children.insert(target_iterator + 1, std::move(leaf));
  962. }
  963. else
  964. {
  965. ConditionExpression original = std::move(*target);
  966. *target = makeContainer(
  967. makeUniqueExpressionId(*logic),
  968. ConditionExpressionKind::Series,
  969. std::move(original),
  970. std::move(leaf));
  971. }
  972. std::string validation_error;
  973. if (!rung->validate(&validation_error))
  974. {
  975. project.controlLogics = std::move(before.logics);
  976. return failure(LogicEditorError::InvalidOperation, validation_error);
  977. }
  978. recordHistory(std::move(before));
  979. return {true, LogicEditorError::None, {}, node_id};
  980. }
  981. LogicEditorResult LogicEditorService::insertWireAfter(
  982. const std::string &logic_id,
  983. const std::string &rung_id,
  984. const std::string &target_expression_id,
  985. int column_span)
  986. {
  987. const ControlLogic *logic = findLogic(logic_id);
  988. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  989. const ConditionExpression *target = findExpression(
  990. logic_id, rung_id, target_expression_id);
  991. WireSegment wire{column_span};
  992. std::string error;
  993. if (logic == nullptr || existing_rung == nullptr || target == nullptr)
  994. {
  995. return failure(LogicEditorError::ExpressionNotFound, "未找到横线插入位置");
  996. }
  997. if ((target->kind != ConditionExpressionKind::Node
  998. && target->kind != ConditionExpressionKind::Wire)
  999. || !wire.validate(&error))
  1000. {
  1001. return failure(
  1002. LogicEditorError::InvalidOperation,
  1003. error.empty() ? "只能在触点或横线后插入横线" : error);
  1004. }
  1005. const std::string wire_id = makeUniqueWireId(*logic);
  1006. ConditionExpression leaf = ConditionExpression::fromWire(wire_id, column_span);
  1007. HistoryState before = captureState();
  1008. LadderRung *rung = findEditableRung(
  1009. project_service_.editProject(), logic_id, rung_id);
  1010. ConditionExpression *editable_target = findConditionExpression(
  1011. *rung->condition, target_expression_id);
  1012. ConditionExpression *parent = findParentExpression(
  1013. *rung->condition, target_expression_id);
  1014. if (parent != nullptr && parent->kind == ConditionExpressionKind::Series)
  1015. {
  1016. const auto target_iterator = std::find_if(
  1017. parent->children.begin(), parent->children.end(),
  1018. [&target_expression_id](const ConditionExpression &child)
  1019. {
  1020. return child.id == target_expression_id;
  1021. });
  1022. parent->children.insert(target_iterator + 1, std::move(leaf));
  1023. }
  1024. else
  1025. {
  1026. ConditionExpression original = std::move(*editable_target);
  1027. *editable_target = makeContainer(
  1028. makeUniqueExpressionId(*logic),
  1029. ConditionExpressionKind::Series,
  1030. std::move(original),
  1031. std::move(leaf));
  1032. }
  1033. std::string validation_error;
  1034. if (!rung->validate(&validation_error))
  1035. {
  1036. project_service_.editProject().controlLogics = std::move(before.logics);
  1037. return failure(LogicEditorError::InvalidOperation, validation_error);
  1038. }
  1039. recordHistory(std::move(before));
  1040. return {true, LogicEditorError::None, {}, wire_id};
  1041. }
  1042. LogicEditorResult LogicEditorService::replaceWireWithCondition(
  1043. const std::string &logic_id,
  1044. const std::string &rung_id,
  1045. const std::string &wire_expression_id,
  1046. const LogicNodeConfig &config)
  1047. {
  1048. const ControlLogic *logic = findLogic(logic_id);
  1049. const ConditionExpression *wire = findExpression(
  1050. logic_id, rung_id, wire_expression_id);
  1051. if (logic == nullptr || wire == nullptr
  1052. || wire->kind != ConditionExpressionKind::Wire)
  1053. {
  1054. return failure(LogicEditorError::ExpressionNotFound, "未找到要替换的横线");
  1055. }
  1056. if (!isConditionConfig(config))
  1057. {
  1058. return failure(LogicEditorError::InvalidNode, "横线只能替换为条件节点");
  1059. }
  1060. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1061. HistoryState before = captureState();
  1062. LadderRung *rung = findEditableRung(
  1063. project_service_.editProject(), logic_id, rung_id);
  1064. ConditionExpression *editable = findConditionExpression(
  1065. *rung->condition, wire_expression_id);
  1066. *editable = ConditionExpression::fromNode(makeNode(node_id, config));
  1067. std::string validation_error;
  1068. if (!rung->validate(&validation_error))
  1069. {
  1070. project_service_.editProject().controlLogics = std::move(before.logics);
  1071. return failure(LogicEditorError::InvalidNode, validation_error);
  1072. }
  1073. recordHistory(std::move(before));
  1074. return {true, LogicEditorError::None, {}, node_id};
  1075. }
  1076. LogicEditorResult LogicEditorService::addParallelBranch(
  1077. const std::string &logic_id,
  1078. const std::string &rung_id,
  1079. const std::vector<std::string> &selected_node_ids,
  1080. const LogicNodeConfig &config)
  1081. {
  1082. const ControlLogic *logic = findLogic(logic_id);
  1083. if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
  1084. {
  1085. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  1086. }
  1087. if (!isConditionConfig(config) || selected_node_ids.empty())
  1088. {
  1089. return failure(LogicEditorError::InvalidOperation, "建立并联支路前必须选择节点");
  1090. }
  1091. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1092. if (!existing_rung->condition.has_value())
  1093. {
  1094. return failure(LogicEditorError::InvalidOperation, "梯形图网络尚无条件节点");
  1095. }
  1096. NodeIdSet selected_ids(selected_node_ids.cbegin(), selected_node_ids.cend());
  1097. if (selected_ids.size() != selected_node_ids.size())
  1098. {
  1099. return failure(
  1100. LogicEditorError::InvalidOperation,
  1101. "并联选择中存在重复节点");
  1102. }
  1103. const NodeIdSet available_ids = conditionLeafIds(*existing_rung->condition);
  1104. if (!isSubset(selected_ids, available_ids))
  1105. {
  1106. return failure(
  1107. LogicEditorError::NodeNotFound,
  1108. "并联选择中包含未知节点");
  1109. }
  1110. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1111. ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
  1112. const std::string parallel_id = makeUniqueExpressionId(*logic);
  1113. std::string series_id = parallel_id + "-range";
  1114. while (findExpression(logic_id, rung_id, series_id) != nullptr)
  1115. {
  1116. series_id += "-range";
  1117. }
  1118. HistoryState before = captureState();
  1119. Project &project = project_service_.editProject();
  1120. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  1121. if (!addParallelForSelection(
  1122. &*rung->condition,
  1123. selected_ids,
  1124. &leaf,
  1125. parallel_id,
  1126. series_id))
  1127. {
  1128. project.controlLogics = before.logics;
  1129. return failure(
  1130. LogicEditorError::InvalidOperation,
  1131. "并联选择必须是一个连续的逻辑范围");
  1132. }
  1133. std::string validation_error;
  1134. if (!rung->validate(&validation_error))
  1135. {
  1136. project.controlLogics = std::move(before.logics);
  1137. return failure(LogicEditorError::InvalidOperation, validation_error);
  1138. }
  1139. recordHistory(std::move(before));
  1140. return {true, LogicEditorError::None, {}, node_id};
  1141. }
  1142. LogicEditorResult LogicEditorService::addParallelWireBranch(
  1143. const std::string &logic_id,
  1144. const std::string &rung_id,
  1145. const std::vector<std::string> &selected_expression_ids)
  1146. {
  1147. const ControlLogic *logic = findLogic(logic_id);
  1148. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1149. if (logic == nullptr || existing_rung == nullptr
  1150. || !existing_rung->condition.has_value())
  1151. {
  1152. return failure(LogicEditorError::RungNotFound, "未找到梯形图条件网络");
  1153. }
  1154. if (selected_expression_ids.empty())
  1155. {
  1156. return failure(
  1157. LogicEditorError::InvalidOperation,
  1158. "建立竖线连接前必须选择连续的触点或横线");
  1159. }
  1160. NodeIdSet selected_ids(
  1161. selected_expression_ids.cbegin(), selected_expression_ids.cend());
  1162. if (selected_ids.size() != selected_expression_ids.size())
  1163. {
  1164. return failure(LogicEditorError::InvalidOperation, "并联选择中存在重复对象");
  1165. }
  1166. const NodeIdSet available_ids = conditionLeafIds(*existing_rung->condition);
  1167. if (!isSubset(selected_ids, available_ids))
  1168. {
  1169. return failure(LogicEditorError::ExpressionNotFound, "并联选择中包含未知对象");
  1170. }
  1171. const std::optional<int> column_span = selectedExpressionColumns(
  1172. *existing_rung->condition, selected_ids);
  1173. if (!column_span.has_value()
  1174. || *column_span > WireSegment::kMaximumColumnSpan)
  1175. {
  1176. return failure(
  1177. LogicEditorError::InvalidOperation,
  1178. "竖线连接只能围绕同一支路中的连续逻辑范围");
  1179. }
  1180. const std::string wire_id = makeUniqueWireId(*logic);
  1181. ConditionExpression wire = ConditionExpression::fromWire(
  1182. wire_id, *column_span);
  1183. const std::string parallel_id = makeUniqueExpressionId(*logic);
  1184. std::string series_id = parallel_id + "-range";
  1185. while (findExpression(logic_id, rung_id, series_id) != nullptr)
  1186. {
  1187. series_id += "-range";
  1188. }
  1189. HistoryState before = captureState();
  1190. LadderRung *rung = findEditableRung(
  1191. project_service_.editProject(), logic_id, rung_id);
  1192. if (!addParallelForSelection(
  1193. &*rung->condition,
  1194. selected_ids,
  1195. &wire,
  1196. parallel_id,
  1197. series_id))
  1198. {
  1199. project_service_.editProject().controlLogics = before.logics;
  1200. return failure(
  1201. LogicEditorError::InvalidOperation,
  1202. "竖线连接只能围绕同一支路中的连续逻辑范围");
  1203. }
  1204. std::string validation_error;
  1205. if (!rung->validate(&validation_error))
  1206. {
  1207. project_service_.editProject().controlLogics = std::move(before.logics);
  1208. return failure(LogicEditorError::InvalidOperation, validation_error);
  1209. }
  1210. recordHistory(std::move(before));
  1211. return {true, LogicEditorError::None, {}, wire_id};
  1212. }
  1213. LogicEditorResult LogicEditorService::setOutput(
  1214. const std::string &logic_id,
  1215. const std::string &rung_id,
  1216. const LogicNodeConfig &config,
  1217. bool configured)
  1218. {
  1219. const ControlLogic *logic = findLogic(logic_id);
  1220. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1221. if (logic == nullptr || existing_rung == nullptr || !isOutputConfig(config))
  1222. {
  1223. return failure(
  1224. LogicEditorError::InvalidNode,
  1225. "梯形图输出必须使用有效的输出指令");
  1226. }
  1227. const std::string node_id = existing_rung->output.has_value()
  1228. ? existing_rung->output->id : makeUniqueNodeId(*logic, nodePrefix(config));
  1229. LogicNode node = makeNode(node_id, config);
  1230. node.configured = configured;
  1231. std::string validation_error;
  1232. if (!node.validate(&validation_error))
  1233. {
  1234. return failure(LogicEditorError::InvalidNode, validation_error);
  1235. }
  1236. if (existing_rung->output.has_value()
  1237. && nodesEqual(*existing_rung->output, node))
  1238. {
  1239. return {true, LogicEditorError::None, {}, node_id};
  1240. }
  1241. HistoryState before = captureState();
  1242. Project &project = project_service_.editProject();
  1243. findEditableRung(project, logic_id, rung_id)->output = std::move(node);
  1244. recordHistory(std::move(before));
  1245. return {true, LogicEditorError::None, {}, node_id};
  1246. }
  1247. LogicEditorResult LogicEditorService::updateNodeConfig(
  1248. const std::string &logic_id,
  1249. const std::string &node_id,
  1250. const LogicNodeConfig &config)
  1251. {
  1252. const LogicNode *node = findNode(logic_id, node_id);
  1253. if (node == nullptr)
  1254. {
  1255. return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
  1256. }
  1257. if (node->config.index() != config.index())
  1258. {
  1259. return failure(
  1260. LogicEditorError::UnsupportedNodeChange,
  1261. "节点创建后不能修改节点类别");
  1262. }
  1263. LogicNode candidate{node_id, config, true};
  1264. std::string error;
  1265. if (!candidate.validate(&error))
  1266. {
  1267. return failure(LogicEditorError::InvalidNode, error);
  1268. }
  1269. if (nodesEqual(*node, candidate))
  1270. {
  1271. return {true, LogicEditorError::None, {}, node_id};
  1272. }
  1273. HistoryState before = captureState();
  1274. Project &project = project_service_.editProject();
  1275. for (ControlLogic &logic : project.controlLogics)
  1276. {
  1277. if (logic.id != logic_id)
  1278. {
  1279. continue;
  1280. }
  1281. for (LadderRung &rung : logic.rungs)
  1282. {
  1283. if (rung.output.has_value() && rung.output->id == node_id)
  1284. {
  1285. *rung.output = candidate;
  1286. recordHistory(std::move(before));
  1287. return {true, LogicEditorError::None, {}, node_id};
  1288. }
  1289. if (rung.condition.has_value())
  1290. {
  1291. if (LogicNode *editable = findConditionNode(*rung.condition, node_id))
  1292. {
  1293. *editable = candidate;
  1294. recordHistory(std::move(before));
  1295. return {true, LogicEditorError::None, {}, node_id};
  1296. }
  1297. }
  1298. }
  1299. }
  1300. return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
  1301. }
  1302. LogicEditorResult LogicEditorService::removeNode(
  1303. const std::string &logic_id, const std::string &node_id)
  1304. {
  1305. return removeNodes(logic_id, {node_id});
  1306. }
  1307. LogicEditorResult LogicEditorService::removeNodes(
  1308. const std::string &logic_id,
  1309. const std::vector<std::string> &node_ids)
  1310. {
  1311. if (findLogic(logic_id) == nullptr)
  1312. {
  1313. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  1314. }
  1315. if (node_ids.empty())
  1316. {
  1317. return failure(LogicEditorError::InvalidOperation, "请先选择要删除的逻辑节点");
  1318. }
  1319. std::unordered_set<std::string> selected_ids;
  1320. for (const std::string &node_id : node_ids)
  1321. {
  1322. if (!selected_ids.insert(node_id).second)
  1323. {
  1324. return failure(LogicEditorError::InvalidOperation, "删除列表中存在重复节点");
  1325. }
  1326. if (findNode(logic_id, node_id) == nullptr)
  1327. {
  1328. return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
  1329. }
  1330. }
  1331. HistoryState before = captureState();
  1332. Project &project = project_service_.editProject();
  1333. ControlLogic *editable_logic = nullptr;
  1334. for (ControlLogic &candidate : project.controlLogics)
  1335. {
  1336. if (candidate.id == logic_id)
  1337. {
  1338. editable_logic = &candidate;
  1339. break;
  1340. }
  1341. }
  1342. for (LadderRung &rung : editable_logic->rungs)
  1343. {
  1344. if (rung.output.has_value()
  1345. && selected_ids.find(rung.output->id) != selected_ids.end())
  1346. {
  1347. rung.output.reset();
  1348. }
  1349. if (rung.condition.has_value())
  1350. {
  1351. for (const std::string &node_id : node_ids)
  1352. {
  1353. if (rung.condition->kind == ConditionExpressionKind::Node
  1354. && rung.condition->id == node_id)
  1355. {
  1356. rung.condition.reset();
  1357. break;
  1358. }
  1359. removeExpressionRecursive(&*rung.condition, node_id);
  1360. }
  1361. if (rung.condition.has_value())
  1362. {
  1363. normalizeConditionExpression(&rung.condition);
  1364. }
  1365. }
  1366. }
  1367. for (const LadderRung &rung : editable_logic->rungs)
  1368. {
  1369. std::string validation_error;
  1370. if (!rung.validate(&validation_error))
  1371. {
  1372. project.controlLogics = std::move(before.logics);
  1373. return failure(LogicEditorError::InvalidOperation, validation_error);
  1374. }
  1375. }
  1376. recordHistory(std::move(before));
  1377. return {true, LogicEditorError::None, {}, node_ids.front()};
  1378. }
  1379. LogicEditorResult LogicEditorService::removeExpression(
  1380. const std::string &logic_id,
  1381. const std::string &rung_id,
  1382. const std::string &expression_id)
  1383. {
  1384. if (findExpression(logic_id, rung_id, expression_id) == nullptr)
  1385. {
  1386. return failure(LogicEditorError::ExpressionNotFound, "未找到条件支路");
  1387. }
  1388. HistoryState before = captureState();
  1389. Project &project = project_service_.editProject();
  1390. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  1391. if (rung->condition->id == expression_id)
  1392. {
  1393. rung->condition.reset();
  1394. }
  1395. else
  1396. {
  1397. removeExpressionRecursive(&*rung->condition, expression_id);
  1398. normalizeConditionExpression(&rung->condition);
  1399. }
  1400. recordHistory(std::move(before));
  1401. return {true, LogicEditorError::None, {}, expression_id};
  1402. }
  1403. LogicEditorResult LogicEditorService::removeExpressions(
  1404. const std::string &logic_id,
  1405. const std::string &rung_id,
  1406. const std::vector<std::string> &expression_ids)
  1407. {
  1408. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1409. if (existing_rung == nullptr || !existing_rung->condition.has_value())
  1410. {
  1411. return failure(LogicEditorError::RungNotFound, "未找到梯形图条件网络");
  1412. }
  1413. if (expression_ids.empty())
  1414. {
  1415. return failure(LogicEditorError::InvalidOperation, "请先选择要删除的条件或横线");
  1416. }
  1417. NodeIdSet unique_ids;
  1418. for (const std::string &expression_id : expression_ids)
  1419. {
  1420. const ConditionExpression *expression = findExpression(
  1421. logic_id, rung_id, expression_id);
  1422. if (!unique_ids.insert(expression_id).second)
  1423. {
  1424. return failure(LogicEditorError::InvalidOperation, "删除列表中存在重复对象");
  1425. }
  1426. if (expression == nullptr)
  1427. {
  1428. return failure(
  1429. LogicEditorError::ExpressionNotFound,
  1430. "删除列表中包含未知条件表达式");
  1431. }
  1432. }
  1433. HistoryState before = captureState();
  1434. LadderRung *rung = findEditableRung(
  1435. project_service_.editProject(), logic_id, rung_id);
  1436. for (const std::string &expression_id : expression_ids)
  1437. {
  1438. if (!rung->condition.has_value())
  1439. {
  1440. break;
  1441. }
  1442. if (rung->condition->id == expression_id)
  1443. {
  1444. rung->condition.reset();
  1445. break;
  1446. }
  1447. removeExpressionRecursive(&*rung->condition, expression_id);
  1448. }
  1449. normalizeConditionExpression(&rung->condition);
  1450. recordHistory(std::move(before));
  1451. return {true, LogicEditorError::None, {}, expression_ids.front()};
  1452. }
  1453. bool LogicEditorService::canUndo() const
  1454. {
  1455. return history_.canUndo();
  1456. }
  1457. bool LogicEditorService::canRedo() const
  1458. {
  1459. return history_.canRedo();
  1460. }
  1461. LogicEditorResult LogicEditorService::undo()
  1462. {
  1463. const std::optional<HistoryState> target = history_.undo(captureState());
  1464. if (!target.has_value())
  1465. {
  1466. return historyFailure("没有可撤销的梯形图编辑操作");
  1467. }
  1468. project_service_.editProject().controlLogics = target->logics;
  1469. return {true, LogicEditorError::None, {}, {}};
  1470. }
  1471. LogicEditorResult LogicEditorService::redo()
  1472. {
  1473. const std::optional<HistoryState> target = history_.redo(captureState());
  1474. if (!target.has_value())
  1475. {
  1476. return historyFailure("没有可重做的梯形图编辑操作");
  1477. }
  1478. project_service_.editProject().controlLogics = target->logics;
  1479. return {true, LogicEditorError::None, {}, {}};
  1480. }
  1481. void LogicEditorService::clearHistory()
  1482. {
  1483. history_.clear();
  1484. }
  1485. bool LogicEditorService::isConditionConfig(const LogicNodeConfig &config)
  1486. {
  1487. return std::holds_alternative<ContactNodeConfig>(config)
  1488. || std::holds_alternative<EdgeContactNodeConfig>(config)
  1489. || std::holds_alternative<TimerContactNodeConfig>(config)
  1490. || std::holds_alternative<CounterContactNodeConfig>(config)
  1491. || std::holds_alternative<CompareNodeConfig>(config);
  1492. }
  1493. bool LogicEditorService::isOutputConfig(const LogicNodeConfig &config)
  1494. {
  1495. return std::holds_alternative<CoilNodeConfig>(config)
  1496. || std::holds_alternative<TonNodeConfig>(config)
  1497. || std::holds_alternative<CounterNodeConfig>(config)
  1498. || std::holds_alternative<MoveNodeConfig>(config)
  1499. || std::holds_alternative<ArithmeticNodeConfig>(config);
  1500. }
  1501. std::string LogicEditorService::nodePrefix(const LogicNodeConfig &config)
  1502. {
  1503. return std::visit(
  1504. [](const auto &value) -> std::string
  1505. {
  1506. using Config = std::decay_t<decltype(value)>;
  1507. if constexpr (std::is_same_v<Config, ContactNodeConfig>)
  1508. {
  1509. return "contact";
  1510. }
  1511. else if constexpr (std::is_same_v<Config, EdgeContactNodeConfig>)
  1512. {
  1513. return "edge";
  1514. }
  1515. else if constexpr (std::is_same_v<Config, TimerContactNodeConfig>)
  1516. {
  1517. return "timer-contact";
  1518. }
  1519. else if constexpr (std::is_same_v<Config, CounterContactNodeConfig>)
  1520. {
  1521. return "counter-contact";
  1522. }
  1523. else if constexpr (std::is_same_v<Config, CoilNodeConfig>)
  1524. {
  1525. return "coil";
  1526. }
  1527. else if constexpr (std::is_same_v<Config, TonNodeConfig>)
  1528. {
  1529. return "ton";
  1530. }
  1531. else if constexpr (std::is_same_v<Config, CounterNodeConfig>)
  1532. {
  1533. return "counter";
  1534. }
  1535. else if constexpr (std::is_same_v<Config, MoveNodeConfig>)
  1536. {
  1537. return "move";
  1538. }
  1539. else if constexpr (std::is_same_v<Config, ArithmeticNodeConfig>)
  1540. {
  1541. return "arithmetic";
  1542. }
  1543. else
  1544. {
  1545. return "compare";
  1546. }
  1547. },
  1548. config);
  1549. }
  1550. std::string LogicEditorService::makeUniqueNodeId(
  1551. const ControlLogic &logic, const std::string &prefix)
  1552. {
  1553. for (std::size_t index = 1;; ++index)
  1554. {
  1555. const std::string candidate = prefix + '-' + std::to_string(index);
  1556. bool found = false;
  1557. for (const LadderRung &rung : logic.rungs)
  1558. {
  1559. found = found || (rung.output.has_value() && rung.output->id == candidate)
  1560. || (rung.condition.has_value()
  1561. && findConditionNode(*rung.condition, candidate) != nullptr);
  1562. }
  1563. if (!found)
  1564. {
  1565. return candidate;
  1566. }
  1567. }
  1568. }
  1569. std::string LogicEditorService::makeUniqueWireId(const ControlLogic &logic)
  1570. {
  1571. for (std::size_t index = 1;; ++index)
  1572. {
  1573. const std::string candidate = "wire-" + std::to_string(index);
  1574. const bool found = std::any_of(
  1575. logic.rungs.cbegin(), logic.rungs.cend(),
  1576. [&candidate](const LadderRung &rung)
  1577. {
  1578. return rung.condition.has_value()
  1579. && findConditionExpression(*rung.condition, candidate) != nullptr;
  1580. });
  1581. if (!found)
  1582. {
  1583. return candidate;
  1584. }
  1585. }
  1586. }
  1587. std::string LogicEditorService::makeUniqueExpressionId(const ControlLogic &logic)
  1588. {
  1589. for (std::size_t index = 1;; ++index)
  1590. {
  1591. const std::string candidate = "expression-" + std::to_string(index);
  1592. bool found = false;
  1593. for (const LadderRung &rung : logic.rungs)
  1594. {
  1595. found = found || (rung.condition.has_value()
  1596. && findConditionExpression(*rung.condition, candidate) != nullptr);
  1597. }
  1598. if (!found)
  1599. {
  1600. return candidate;
  1601. }
  1602. }
  1603. }
  1604. std::string LogicEditorService::makeUniqueRungId(const ControlLogic &logic)
  1605. {
  1606. for (std::size_t index = 1;; ++index)
  1607. {
  1608. const std::string candidate = "rung-" + std::to_string(index);
  1609. if (std::none_of(
  1610. logic.rungs.cbegin(), logic.rungs.cend(),
  1611. [&candidate](const LadderRung &rung) { return rung.id == candidate; }))
  1612. {
  1613. return candidate;
  1614. }
  1615. }
  1616. }
  1617. LogicEditorResult LogicEditorService::failure(
  1618. LogicEditorError error, const std::string &message)
  1619. {
  1620. return {false, error, message, {}};
  1621. }