综合平台编程器项目的远程存储
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

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