综合平台编程器项目的远程存储
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 

2577 rindas
90 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. const ConditionExpression *findParentExpression(
  74. const ConditionExpression &expression, const std::string &child_id)
  75. {
  76. for (const ConditionExpression &child : expression.children)
  77. {
  78. if (child.id == child_id)
  79. {
  80. return &expression;
  81. }
  82. if (const ConditionExpression *parent = findParentExpression(
  83. child, child_id))
  84. {
  85. return parent;
  86. }
  87. }
  88. return nullptr;
  89. }
  90. using NodeIdSet = std::unordered_set<std::string>;
  91. NodeIdSet conditionLeafIds(const ConditionExpression &expression)
  92. {
  93. NodeIdSet ids;
  94. if (expression.kind == ConditionExpressionKind::Node
  95. || expression.kind == ConditionExpressionKind::Wire)
  96. {
  97. ids.insert(expression.id);
  98. return ids;
  99. }
  100. for (const ConditionExpression &child : expression.children)
  101. {
  102. NodeIdSet child_ids = conditionLeafIds(child);
  103. ids.insert(child_ids.cbegin(), child_ids.cend());
  104. }
  105. return ids;
  106. }
  107. int expressionColumns(const ConditionExpression &expression)
  108. {
  109. if (expression.kind == ConditionExpressionKind::Node)
  110. {
  111. return 1;
  112. }
  113. if (expression.kind == ConditionExpressionKind::Wire)
  114. {
  115. return expression.wire->columnSpan;
  116. }
  117. int columns = expression.kind == ConditionExpressionKind::Series ? 0 : 1;
  118. for (const ConditionExpression &child : expression.children)
  119. {
  120. const int child_columns = expressionColumns(child);
  121. columns = expression.kind == ConditionExpressionKind::Series
  122. ? columns + child_columns : std::max(columns, child_columns);
  123. }
  124. return columns;
  125. }
  126. bool isSubset(const NodeIdSet &subset, const NodeIdSet &values)
  127. {
  128. return std::all_of(
  129. subset.cbegin(), subset.cend(),
  130. [&values](const std::string &value) { return values.count(value) != 0U; });
  131. }
  132. bool sameValues(const NodeIdSet &left, const NodeIdSet &right)
  133. {
  134. return left.size() == right.size() && isSubset(left, right);
  135. }
  136. void addParallelSibling(
  137. ConditionExpression *target,
  138. ConditionExpression branch,
  139. const std::string &container_id)
  140. {
  141. if (target->kind == ConditionExpressionKind::Parallel)
  142. {
  143. target->children.push_back(std::move(branch));
  144. return;
  145. }
  146. ConditionExpression original = std::move(*target);
  147. *target = makeContainer(
  148. container_id,
  149. ConditionExpressionKind::Parallel,
  150. std::move(original),
  151. std::move(branch));
  152. }
  153. bool addParallelForSelection(
  154. ConditionExpression *expression,
  155. const NodeIdSet &selected_ids,
  156. ConditionExpression *branch,
  157. const std::string &parallel_id,
  158. const std::string &series_id)
  159. {
  160. const NodeIdSet expression_ids = conditionLeafIds(*expression);
  161. if (sameValues(expression_ids, selected_ids))
  162. {
  163. addParallelSibling(expression, std::move(*branch), parallel_id);
  164. return true;
  165. }
  166. std::vector<NodeIdSet> child_ids;
  167. child_ids.reserve(expression->children.size());
  168. std::vector<std::size_t> matching_children;
  169. for (std::size_t index = 0; index < expression->children.size(); ++index)
  170. {
  171. child_ids.push_back(conditionLeafIds(expression->children[index]));
  172. const bool intersects = std::any_of(
  173. child_ids.back().cbegin(), child_ids.back().cend(),
  174. [&selected_ids](const std::string &id)
  175. {
  176. return selected_ids.count(id) != 0U;
  177. });
  178. if (intersects)
  179. {
  180. matching_children.push_back(index);
  181. }
  182. }
  183. if (matching_children.empty())
  184. {
  185. return false;
  186. }
  187. if (matching_children.size() == 1U)
  188. {
  189. const std::size_t child_index = matching_children.front();
  190. if (expression->kind == ConditionExpressionKind::Parallel
  191. && sameValues(child_ids[child_index], selected_ids))
  192. {
  193. expression->children.push_back(std::move(*branch));
  194. return true;
  195. }
  196. return addParallelForSelection(
  197. &expression->children[child_index],
  198. selected_ids,
  199. branch,
  200. parallel_id,
  201. series_id);
  202. }
  203. if (expression->kind != ConditionExpressionKind::Series)
  204. {
  205. return false;
  206. }
  207. const std::size_t first = matching_children.front();
  208. const std::size_t last = matching_children.back();
  209. if (last - first + 1U != matching_children.size())
  210. {
  211. return false;
  212. }
  213. NodeIdSet range_ids;
  214. for (std::size_t index = first; index <= last; ++index)
  215. {
  216. range_ids.insert(child_ids[index].cbegin(), child_ids[index].cend());
  217. }
  218. if (!sameValues(range_ids, selected_ids))
  219. {
  220. return false;
  221. }
  222. ConditionExpression range;
  223. range.id = series_id;
  224. range.kind = ConditionExpressionKind::Series;
  225. auto range_begin = expression->children.begin() + static_cast<std::ptrdiff_t>(first);
  226. auto range_end = expression->children.begin() + static_cast<std::ptrdiff_t>(last + 1U);
  227. range.children.insert(
  228. range.children.end(),
  229. std::make_move_iterator(range_begin),
  230. std::make_move_iterator(range_end));
  231. range_begin = expression->children.erase(range_begin, range_end);
  232. ConditionExpression parallel = makeContainer(
  233. parallel_id,
  234. ConditionExpressionKind::Parallel,
  235. std::move(range),
  236. std::move(*branch));
  237. expression->children.insert(range_begin, std::move(parallel));
  238. return true;
  239. }
  240. bool addParallelForWireCellRange(
  241. ConditionExpression *expression,
  242. const std::string &wire_id,
  243. int first_column,
  244. int selected_columns,
  245. ConditionExpression *branch,
  246. const std::string &parallel_id,
  247. const std::string &series_id,
  248. const std::string &leading_wire_id,
  249. const std::string &trailing_wire_id)
  250. {
  251. if (expression == nullptr)
  252. {
  253. return false;
  254. }
  255. if (expression->kind == ConditionExpressionKind::Wire
  256. && expression->id == wire_id)
  257. {
  258. const int total_columns = expression->wire->columnSpan;
  259. if (first_column < 0 || selected_columns <= 0
  260. || first_column + selected_columns > total_columns)
  261. {
  262. return false;
  263. }
  264. if (first_column == 0 && selected_columns == total_columns)
  265. {
  266. addParallelSibling(expression, std::move(*branch), parallel_id);
  267. return true;
  268. }
  269. ConditionExpression replacement;
  270. replacement.id = series_id;
  271. replacement.kind = ConditionExpressionKind::Series;
  272. if (first_column > 0)
  273. {
  274. replacement.children.push_back(ConditionExpression::fromWire(
  275. leading_wire_id, first_column));
  276. }
  277. ConditionExpression selected_wire = ConditionExpression::fromWire(
  278. wire_id, selected_columns);
  279. replacement.children.push_back(makeContainer(
  280. parallel_id,
  281. ConditionExpressionKind::Parallel,
  282. std::move(selected_wire),
  283. std::move(*branch)));
  284. const int trailing_columns =
  285. total_columns - first_column - selected_columns;
  286. if (trailing_columns > 0)
  287. {
  288. replacement.children.push_back(ConditionExpression::fromWire(
  289. trailing_wire_id, trailing_columns));
  290. }
  291. *expression = std::move(replacement);
  292. return true;
  293. }
  294. if (expression->kind == ConditionExpressionKind::Node
  295. || expression->kind == ConditionExpressionKind::Wire)
  296. {
  297. return false;
  298. }
  299. for (ConditionExpression &child : expression->children)
  300. {
  301. if (addParallelForWireCellRange(
  302. &child,
  303. wire_id,
  304. first_column,
  305. selected_columns,
  306. branch,
  307. parallel_id,
  308. series_id,
  309. leading_wire_id,
  310. trailing_wire_id))
  311. {
  312. return true;
  313. }
  314. }
  315. return false;
  316. }
  317. struct WireCellLocation
  318. {
  319. const ConditionExpression *series = nullptr;
  320. std::size_t child_index = 0U;
  321. int child_start = 0;
  322. };
  323. bool findWireCellLocation(
  324. const ConditionExpression &expression,
  325. const std::string &wire_id,
  326. WireCellLocation *location)
  327. {
  328. if (expression.kind == ConditionExpressionKind::Node
  329. || expression.kind == ConditionExpressionKind::Wire)
  330. {
  331. return false;
  332. }
  333. if (expression.kind == ConditionExpressionKind::Series)
  334. {
  335. int child_start = 0;
  336. for (std::size_t index = 0; index < expression.children.size(); ++index)
  337. {
  338. const ConditionExpression &child = expression.children[index];
  339. if (child.kind == ConditionExpressionKind::Wire
  340. && child.id == wire_id)
  341. {
  342. if (location != nullptr)
  343. {
  344. *location = {&expression, index, child_start};
  345. }
  346. return true;
  347. }
  348. if (findWireCellLocation(child, wire_id, location))
  349. {
  350. return true;
  351. }
  352. child_start += expressionColumns(child);
  353. }
  354. return false;
  355. }
  356. for (const ConditionExpression &child : expression.children)
  357. {
  358. if (findWireCellLocation(child, wire_id, location))
  359. {
  360. return true;
  361. }
  362. }
  363. return false;
  364. }
  365. bool addParallelForWireCellSeriesRange(
  366. ConditionExpression *root,
  367. const std::string &series_id,
  368. std::size_t first_child,
  369. std::size_t last_child,
  370. int leading_columns,
  371. int selected_columns,
  372. int trailing_columns,
  373. ConditionExpression *branch,
  374. const std::string &selected_wire_id,
  375. const std::string &parallel_id,
  376. const std::string &leading_wire_id,
  377. const std::string &trailing_wire_id)
  378. {
  379. if (root == nullptr || branch == nullptr)
  380. {
  381. return false;
  382. }
  383. ConditionExpression *series = findConditionExpression(*root, series_id);
  384. if (series == nullptr || series->kind != ConditionExpressionKind::Series
  385. || first_child > last_child || last_child >= series->children.size()
  386. || leading_columns < 0 || selected_columns <= 0 || trailing_columns < 0)
  387. {
  388. return false;
  389. }
  390. for (std::size_t index = first_child; index <= last_child; ++index)
  391. {
  392. if (series->children[index].kind != ConditionExpressionKind::Wire)
  393. {
  394. return false;
  395. }
  396. }
  397. ConditionExpression selected_wire = ConditionExpression::fromWire(
  398. selected_wire_id, selected_columns);
  399. ConditionExpression parallel = makeContainer(
  400. parallel_id,
  401. ConditionExpressionKind::Parallel,
  402. std::move(selected_wire),
  403. std::move(*branch));
  404. std::vector<ConditionExpression> replacement;
  405. replacement.reserve(
  406. series->children.size() - (last_child - first_child) + 3U);
  407. for (std::size_t index = 0; index < first_child; ++index)
  408. {
  409. replacement.push_back(std::move(series->children[index]));
  410. }
  411. if (leading_columns > 0)
  412. {
  413. replacement.push_back(ConditionExpression::fromWire(
  414. leading_wire_id, leading_columns));
  415. }
  416. replacement.push_back(std::move(parallel));
  417. if (trailing_columns > 0)
  418. {
  419. replacement.push_back(ConditionExpression::fromWire(
  420. trailing_wire_id, trailing_columns));
  421. }
  422. for (std::size_t index = last_child + 1U;
  423. index < series->children.size();
  424. ++index)
  425. {
  426. replacement.push_back(std::move(series->children[index]));
  427. }
  428. series->children = std::move(replacement);
  429. return true;
  430. }
  431. bool removeExpressionRecursive(
  432. ConditionExpression *expression, const std::string &expression_id)
  433. {
  434. if (expression == nullptr
  435. || expression->kind == ConditionExpressionKind::Node
  436. || expression->kind == ConditionExpressionKind::Wire)
  437. {
  438. return false;
  439. }
  440. const auto removable = std::find_if(
  441. expression->children.begin(),
  442. expression->children.end(),
  443. [&expression_id](const ConditionExpression &child)
  444. {
  445. return child.id == expression_id;
  446. });
  447. if (removable != expression->children.end())
  448. {
  449. expression->children.erase(removable);
  450. return true;
  451. }
  452. for (ConditionExpression &child : expression->children)
  453. {
  454. if (removeExpressionRecursive(&child, expression_id))
  455. {
  456. return true;
  457. }
  458. }
  459. return false;
  460. }
  461. std::optional<int> selectedExpressionColumns(
  462. const ConditionExpression &expression,
  463. const NodeIdSet &selected_ids)
  464. {
  465. const NodeIdSet expression_ids = conditionLeafIds(expression);
  466. if (sameValues(expression_ids, selected_ids))
  467. {
  468. return expressionColumns(expression);
  469. }
  470. if (expression.kind == ConditionExpressionKind::Node
  471. || expression.kind == ConditionExpressionKind::Wire)
  472. {
  473. return std::nullopt;
  474. }
  475. std::vector<NodeIdSet> child_ids;
  476. std::vector<std::size_t> matching_children;
  477. child_ids.reserve(expression.children.size());
  478. for (std::size_t index = 0; index < expression.children.size(); ++index)
  479. {
  480. child_ids.push_back(conditionLeafIds(expression.children[index]));
  481. const bool intersects = std::any_of(
  482. child_ids.back().cbegin(), child_ids.back().cend(),
  483. [&selected_ids](const std::string &id)
  484. {
  485. return selected_ids.count(id) != 0U;
  486. });
  487. if (intersects)
  488. {
  489. matching_children.push_back(index);
  490. }
  491. }
  492. if (matching_children.size() == 1U)
  493. {
  494. return selectedExpressionColumns(
  495. expression.children[matching_children.front()], selected_ids);
  496. }
  497. if (matching_children.empty()
  498. || expression.kind != ConditionExpressionKind::Series)
  499. {
  500. return std::nullopt;
  501. }
  502. const std::size_t first = matching_children.front();
  503. const std::size_t last = matching_children.back();
  504. if (last - first + 1U != matching_children.size())
  505. {
  506. return std::nullopt;
  507. }
  508. NodeIdSet range_ids;
  509. int columns = 0;
  510. for (std::size_t index = first; index <= last; ++index)
  511. {
  512. range_ids.insert(child_ids[index].cbegin(), child_ids[index].cend());
  513. columns += expressionColumns(expression.children[index]);
  514. }
  515. return sameValues(range_ids, selected_ids)
  516. ? std::optional<int>{columns} : std::nullopt;
  517. }
  518. LadderRung *findEditableRung(
  519. Project &project,
  520. const std::string &logic_id,
  521. const std::string &rung_id)
  522. {
  523. const auto logic = std::find_if(
  524. project.controlLogics.begin(), project.controlLogics.end(),
  525. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  526. if (logic == project.controlLogics.end())
  527. {
  528. return nullptr;
  529. }
  530. const auto rung = std::find_if(
  531. logic->rungs.begin(), logic->rungs.end(),
  532. [&rung_id](const LadderRung &candidate) { return candidate.id == rung_id; });
  533. return rung == logic->rungs.end() ? nullptr : &*rung;
  534. }
  535. } // namespace
  536. LogicEditorService::LogicEditorService(ProjectService &project_service)
  537. : project_service_(project_service)
  538. {
  539. }
  540. LogicEditorService::HistoryState LogicEditorService::captureState() const
  541. {
  542. return {project_service_.project().controlLogics};
  543. }
  544. void LogicEditorService::recordHistory(HistoryState before)
  545. {
  546. const HistoryState after = captureState();
  547. history_.record(std::move(before), after, &LogicEditorService::statesEqual);
  548. }
  549. void LogicEditorService::rollbackEdit(
  550. HistoryState before, bool modified_before)
  551. {
  552. project_service_.editProject().controlLogics = std::move(before.logics);
  553. project_service_.restoreModifiedState(modified_before);
  554. }
  555. bool LogicEditorService::statesEqual(
  556. const HistoryState &left, const HistoryState &right)
  557. {
  558. if (left.logics.size() != right.logics.size())
  559. {
  560. return false;
  561. }
  562. for (std::size_t index = 0; index < left.logics.size(); ++index)
  563. {
  564. if (!logicsEqual(left.logics[index], right.logics[index]))
  565. {
  566. return false;
  567. }
  568. }
  569. return true;
  570. }
  571. bool LogicEditorService::logicsEqual(
  572. const ControlLogic &left, const ControlLogic &right)
  573. {
  574. if (left.id != right.id || left.name != right.name
  575. || left.enabled != right.enabled || left.rungs.size() != right.rungs.size())
  576. {
  577. return false;
  578. }
  579. for (std::size_t index = 0; index < left.rungs.size(); ++index)
  580. {
  581. if (!rungsEqual(left.rungs[index], right.rungs[index]))
  582. {
  583. return false;
  584. }
  585. }
  586. return true;
  587. }
  588. bool LogicEditorService::rungsEqual(
  589. const LadderRung &left, const LadderRung &right)
  590. {
  591. if (left.id != right.id || left.name != right.name
  592. || left.comment != right.comment
  593. || left.condition.has_value() != right.condition.has_value()
  594. || left.output.has_value() != right.output.has_value())
  595. {
  596. return false;
  597. }
  598. return (!left.condition.has_value()
  599. || expressionsEqual(*left.condition, *right.condition))
  600. && (!left.output.has_value() || nodesEqual(*left.output, *right.output));
  601. }
  602. bool LogicEditorService::expressionsEqual(
  603. const ConditionExpression &left, const ConditionExpression &right)
  604. {
  605. if (left.id != right.id || left.kind != right.kind
  606. || left.node.has_value() != right.node.has_value()
  607. || left.wire.has_value() != right.wire.has_value()
  608. || left.children.size() != right.children.size())
  609. {
  610. return false;
  611. }
  612. if (left.node.has_value() && !nodesEqual(*left.node, *right.node))
  613. {
  614. return false;
  615. }
  616. if (left.wire.has_value()
  617. && left.wire->columnSpan != right.wire->columnSpan)
  618. {
  619. return false;
  620. }
  621. for (std::size_t index = 0; index < left.children.size(); ++index)
  622. {
  623. if (!expressionsEqual(left.children[index], right.children[index]))
  624. {
  625. return false;
  626. }
  627. }
  628. return true;
  629. }
  630. bool LogicEditorService::nodesEqual(
  631. const LogicNode &left, const LogicNode &right)
  632. {
  633. return left.id == right.id && left.config.index() == right.config.index()
  634. && left.configured == right.configured && configsEqual(left.config, right.config);
  635. }
  636. bool LogicEditorService::configsEqual(
  637. const LogicNodeConfig &left, const LogicNodeConfig &right)
  638. {
  639. return std::visit(
  640. [](const auto &left_config, const auto &right_config)
  641. {
  642. using Left = std::decay_t<decltype(left_config)>;
  643. using Right = std::decay_t<decltype(right_config)>;
  644. if constexpr (!std::is_same_v<Left, Right>)
  645. {
  646. return false;
  647. }
  648. else if constexpr (std::is_same_v<Left, ContactNodeConfig>)
  649. {
  650. return left_config.address == right_config.address
  651. && left_config.mode == right_config.mode;
  652. }
  653. else if constexpr (std::is_same_v<Left, EdgeContactNodeConfig>)
  654. {
  655. return left_config.address == right_config.address
  656. && left_config.mode == right_config.mode;
  657. }
  658. else if constexpr (std::is_same_v<Left, TimerContactNodeConfig>)
  659. {
  660. return left_config.address == right_config.address
  661. && left_config.mode == right_config.mode;
  662. }
  663. else if constexpr (std::is_same_v<Left, CounterContactNodeConfig>)
  664. {
  665. return left_config.address == right_config.address
  666. && left_config.mode == right_config.mode;
  667. }
  668. else if constexpr (std::is_same_v<Left, CoilNodeConfig>)
  669. {
  670. return left_config.address == right_config.address
  671. && left_config.mode == right_config.mode;
  672. }
  673. else if constexpr (std::is_same_v<Left, CompareNodeConfig>)
  674. {
  675. return left_config.address == right_config.address
  676. && left_config.comparison == right_config.comparison
  677. && left_config.value == right_config.value;
  678. }
  679. else if constexpr (std::is_same_v<Left, TonNodeConfig>)
  680. {
  681. return left_config.address == right_config.address
  682. && left_config.presetMs == right_config.presetMs;
  683. }
  684. else if constexpr (std::is_same_v<Left, CounterNodeConfig>)
  685. {
  686. return left_config.address == right_config.address
  687. && left_config.mode == right_config.mode
  688. && left_config.currentValueAddress == right_config.currentValueAddress
  689. && left_config.preset.kind == right_config.preset.kind
  690. && left_config.preset.address == right_config.preset.address
  691. && left_config.preset.constant == right_config.preset.constant
  692. && left_config.resetAddress == right_config.resetAddress;
  693. }
  694. else if constexpr (std::is_same_v<Left, MoveNodeConfig>)
  695. {
  696. return left_config.source.kind == right_config.source.kind
  697. && left_config.source.address == right_config.source.address
  698. && left_config.source.constant == right_config.source.constant
  699. && left_config.destination == right_config.destination;
  700. }
  701. else
  702. {
  703. return left_config.operation == right_config.operation
  704. && left_config.left.kind == right_config.left.kind
  705. && left_config.left.address == right_config.left.address
  706. && left_config.left.constant == right_config.left.constant
  707. && left_config.right.kind == right_config.right.kind
  708. && left_config.right.address == right_config.right.address
  709. && left_config.right.constant == right_config.right.constant
  710. && left_config.destination == right_config.destination;
  711. }
  712. },
  713. left,
  714. right);
  715. }
  716. LogicEditorResult LogicEditorService::historyFailure(const std::string &message)
  717. {
  718. return {false, LogicEditorError::InvalidOperation, message, {}};
  719. }
  720. const ControlLogic *LogicEditorService::findLogic(const std::string &logic_id) const
  721. {
  722. const auto &logics = project_service_.project().controlLogics;
  723. const auto logic = std::find_if(
  724. logics.cbegin(), logics.cend(),
  725. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  726. return logic == logics.cend() ? nullptr : &*logic;
  727. }
  728. const LadderRung *LogicEditorService::findRung(
  729. const std::string &logic_id, const std::string &rung_id) const
  730. {
  731. const ControlLogic *logic = findLogic(logic_id);
  732. if (logic == nullptr)
  733. {
  734. return nullptr;
  735. }
  736. const auto rung = std::find_if(
  737. logic->rungs.cbegin(), logic->rungs.cend(),
  738. [&rung_id](const LadderRung &candidate) { return candidate.id == rung_id; });
  739. return rung == logic->rungs.cend() ? nullptr : &*rung;
  740. }
  741. const LogicNode *LogicEditorService::findNode(
  742. const std::string &logic_id, const std::string &node_id) const
  743. {
  744. const ControlLogic *logic = findLogic(logic_id);
  745. if (logic == nullptr)
  746. {
  747. return nullptr;
  748. }
  749. for (const LadderRung &rung : logic->rungs)
  750. {
  751. if (rung.output.has_value() && rung.output->id == node_id)
  752. {
  753. return &*rung.output;
  754. }
  755. if (rung.condition.has_value())
  756. {
  757. if (const LogicNode *node = findConditionNode(*rung.condition, node_id))
  758. {
  759. return node;
  760. }
  761. }
  762. }
  763. return nullptr;
  764. }
  765. const ConditionExpression *LogicEditorService::findExpression(
  766. const std::string &logic_id,
  767. const std::string &rung_id,
  768. const std::string &expression_id) const
  769. {
  770. const LadderRung *rung = findRung(logic_id, rung_id);
  771. return rung == nullptr || !rung->condition.has_value()
  772. ? nullptr : findConditionExpression(*rung->condition, expression_id);
  773. }
  774. std::string LogicEditorService::firstLogicId() const
  775. {
  776. const auto &logics = project_service_.project().controlLogics;
  777. return logics.empty() ? std::string{} : logics.front().id;
  778. }
  779. std::string LogicEditorService::firstRungId(const std::string &logic_id) const
  780. {
  781. const ControlLogic *logic = findLogic(logic_id);
  782. return logic == nullptr || logic->rungs.empty() ? std::string{} : logic->rungs.front().id;
  783. }
  784. std::string LogicEditorService::rungIdForNode(
  785. const std::string &logic_id, const std::string &node_id) const
  786. {
  787. const ControlLogic *logic = findLogic(logic_id);
  788. if (logic == nullptr)
  789. {
  790. return {};
  791. }
  792. for (const LadderRung &rung : logic->rungs)
  793. {
  794. if ((rung.output.has_value() && rung.output->id == node_id)
  795. || (rung.condition.has_value()
  796. && findConditionNode(*rung.condition, node_id) != nullptr))
  797. {
  798. return rung.id;
  799. }
  800. }
  801. return {};
  802. }
  803. std::string LogicEditorService::registerCommentFor(
  804. const RegisterAddress &address) const
  805. {
  806. const RegisterComment *comment = project_service_.project().findRegisterComment(address);
  807. return comment == nullptr ? std::string{} : comment->text;
  808. }
  809. LogicEditorResult LogicEditorService::ensureDefaultLogic()
  810. {
  811. if (!project_service_.project().controlLogics.empty())
  812. {
  813. return {true, LogicEditorError::None, {}, firstLogicId()};
  814. }
  815. ControlLogic logic;
  816. logic.id = "logic-1";
  817. logic.name = "控制逻辑 1";
  818. Project &project = project_service_.editProject();
  819. project.controlLogics.push_back(std::move(logic));
  820. return {true, LogicEditorError::None, {}, project.controlLogics.back().id};
  821. }
  822. LogicEditorResult LogicEditorService::addLogic(const std::string &name)
  823. {
  824. if (isBlank(name))
  825. {
  826. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能为空");
  827. }
  828. const Project &current = project_service_.project();
  829. if (current.controlLogics.size() >= ProjectLimits::kMaximumControlLogics)
  830. {
  831. return failure(LogicEditorError::InvalidOperation, "单个工程最多包含 128 组控制逻辑");
  832. }
  833. if (name.size() > ProjectLimits::kMaximumTextBytes)
  834. {
  835. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能超过 4096 个 UTF-8 字节");
  836. }
  837. const bool duplicate = std::any_of(
  838. current.controlLogics.cbegin(), current.controlLogics.cend(),
  839. [&name](const ControlLogic &logic) { return logic.name == name; });
  840. if (duplicate)
  841. {
  842. return failure(LogicEditorError::DuplicateName, "控制逻辑名称必须唯一");
  843. }
  844. ControlLogic logic;
  845. logic.id = makeUniqueLogicId(current);
  846. logic.name = name;
  847. HistoryState before = captureState();
  848. Project &project = project_service_.editProject();
  849. project.controlLogics.push_back(std::move(logic));
  850. recordHistory(std::move(before));
  851. return {true, LogicEditorError::None, {}, project.controlLogics.back().id};
  852. }
  853. LogicEditorResult LogicEditorService::renameLogic(
  854. const std::string &logic_id, const std::string &name)
  855. {
  856. if (isBlank(name))
  857. {
  858. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能为空");
  859. }
  860. if (name.size() > ProjectLimits::kMaximumTextBytes)
  861. {
  862. return failure(LogicEditorError::InvalidOperation, "控制逻辑名称不能超过 4096 个 UTF-8 字节");
  863. }
  864. const Project &current = project_service_.project();
  865. const auto logic = std::find_if(
  866. current.controlLogics.cbegin(), current.controlLogics.cend(),
  867. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  868. if (logic == current.controlLogics.cend())
  869. {
  870. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  871. }
  872. const bool duplicate = std::any_of(
  873. current.controlLogics.cbegin(), current.controlLogics.cend(),
  874. [&logic_id, &name](const ControlLogic &candidate)
  875. {
  876. return candidate.id != logic_id && candidate.name == name;
  877. });
  878. if (duplicate)
  879. {
  880. return failure(LogicEditorError::DuplicateName, "控制逻辑名称必须唯一");
  881. }
  882. if (logic->name == name)
  883. {
  884. return {true, LogicEditorError::None, {}, logic_id};
  885. }
  886. HistoryState before = captureState();
  887. Project &project = project_service_.editProject();
  888. auto editable = std::find_if(
  889. project.controlLogics.begin(), project.controlLogics.end(),
  890. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  891. editable->name = name;
  892. recordHistory(std::move(before));
  893. return {true, LogicEditorError::None, {}, logic_id};
  894. }
  895. LogicEditorResult LogicEditorService::removeLogic(const std::string &logic_id)
  896. {
  897. const Project &current = project_service_.project();
  898. const auto logic = std::find_if(
  899. current.controlLogics.cbegin(), current.controlLogics.cend(),
  900. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  901. if (logic == current.controlLogics.cend())
  902. {
  903. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  904. }
  905. if (current.controlLogics.size() <= 1)
  906. {
  907. return failure(LogicEditorError::LastLogicRequired, "工程至少需要保留一个控制逻辑");
  908. }
  909. HistoryState before = captureState();
  910. Project &project = project_service_.editProject();
  911. project.controlLogics.erase(
  912. std::remove_if(
  913. project.controlLogics.begin(), project.controlLogics.end(),
  914. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; }),
  915. project.controlLogics.end());
  916. recordHistory(std::move(before));
  917. return {true, LogicEditorError::None, {}, logic_id};
  918. }
  919. LogicEditorResult LogicEditorService::moveLogic(const std::string &logic_id, int offset)
  920. {
  921. if (offset != -1 && offset != 1)
  922. {
  923. return failure(LogicEditorError::InvalidOperation, "控制逻辑每次只能上移或下移一位");
  924. }
  925. const Project &current = project_service_.project();
  926. const auto logic = std::find_if(
  927. current.controlLogics.cbegin(), current.controlLogics.cend(),
  928. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  929. if (logic == current.controlLogics.cend())
  930. {
  931. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  932. }
  933. const auto index = std::distance(current.controlLogics.cbegin(), logic);
  934. const std::ptrdiff_t target_index = index + offset;
  935. if (target_index < 0
  936. || target_index
  937. >= static_cast<std::ptrdiff_t>(current.controlLogics.size()))
  938. {
  939. return failure(LogicEditorError::InvalidOperation, "控制逻辑已经位于目标边界");
  940. }
  941. HistoryState before = captureState();
  942. Project &project = project_service_.editProject();
  943. std::iter_swap(
  944. project.controlLogics.begin() + index,
  945. project.controlLogics.begin() + target_index);
  946. recordHistory(std::move(before));
  947. return {true, LogicEditorError::None, {}, logic_id};
  948. }
  949. LogicEditorResult LogicEditorService::setLogicEnabled(
  950. const std::string &logic_id, bool enabled)
  951. {
  952. if (findLogic(logic_id) == nullptr)
  953. {
  954. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  955. }
  956. const ControlLogic *existing = findLogic(logic_id);
  957. if (existing->enabled == enabled)
  958. {
  959. return {true, LogicEditorError::None, {}, logic_id};
  960. }
  961. HistoryState before = captureState();
  962. Project &project = project_service_.editProject();
  963. auto logic = std::find_if(
  964. project.controlLogics.begin(), project.controlLogics.end(),
  965. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  966. logic->enabled = enabled;
  967. recordHistory(std::move(before));
  968. return {true, LogicEditorError::None, {}, logic_id};
  969. }
  970. LogicEditorResult LogicEditorService::addRung(const std::string &logic_id)
  971. {
  972. const ControlLogic *logic = findLogic(logic_id);
  973. if (logic == nullptr)
  974. {
  975. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  976. }
  977. if (logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
  978. {
  979. return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
  980. }
  981. LadderRung rung;
  982. rung.id = makeUniqueRungId(*logic);
  983. rung.name = "网络 " + std::to_string(logic->rungs.size() + 1U);
  984. HistoryState before = captureState();
  985. Project &project = project_service_.editProject();
  986. auto target = std::find_if(
  987. project.controlLogics.begin(), project.controlLogics.end(),
  988. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  989. target->rungs.push_back(std::move(rung));
  990. recordHistory(std::move(before));
  991. return {true, LogicEditorError::None, {}, target->rungs.back().id};
  992. }
  993. LogicEditorResult LogicEditorService::removeRung(
  994. const std::string &logic_id, const std::string &rung_id)
  995. {
  996. const ControlLogic *logic = findLogic(logic_id);
  997. if (logic == nullptr)
  998. {
  999. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  1000. }
  1001. if (findRung(logic_id, rung_id) == nullptr)
  1002. {
  1003. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  1004. }
  1005. HistoryState before = captureState();
  1006. Project &project = project_service_.editProject();
  1007. auto target = std::find_if(
  1008. project.controlLogics.begin(), project.controlLogics.end(),
  1009. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  1010. target->rungs.erase(
  1011. std::remove_if(
  1012. target->rungs.begin(), target->rungs.end(),
  1013. [&rung_id](const LadderRung &rung) { return rung.id == rung_id; }),
  1014. target->rungs.end());
  1015. recordHistory(std::move(before));
  1016. return {true, LogicEditorError::None, {}, rung_id};
  1017. }
  1018. LogicEditorResult LogicEditorService::updateRungComment(
  1019. const std::string &logic_id,
  1020. const std::string &rung_id,
  1021. const std::string &comment)
  1022. {
  1023. const LadderRung *rung = findRung(logic_id, rung_id);
  1024. if (rung == nullptr)
  1025. {
  1026. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  1027. }
  1028. if (containsLineBreak(comment))
  1029. {
  1030. return failure(
  1031. LogicEditorError::InvalidOperation,
  1032. "梯形图网络注释只能使用单行文本");
  1033. }
  1034. if (comment.size() > ProjectLimits::kMaximumRungCommentBytes)
  1035. {
  1036. return failure(
  1037. LogicEditorError::InvalidOperation,
  1038. "梯形图网络注释不能超过 128 个 UTF-8 字节");
  1039. }
  1040. if (rung->comment == comment)
  1041. {
  1042. return {true, LogicEditorError::None, {}, rung_id};
  1043. }
  1044. HistoryState before = captureState();
  1045. LadderRung &editable = *findEditableRung(
  1046. project_service_.editProject(), logic_id, rung_id);
  1047. editable.comment = comment;
  1048. recordHistory(std::move(before));
  1049. return {true, LogicEditorError::None, {}, rung_id};
  1050. }
  1051. LogicEditorResult LogicEditorService::appendCondition(
  1052. const std::string &logic_id,
  1053. const std::string &rung_id,
  1054. const LogicNodeConfig &config)
  1055. {
  1056. const ControlLogic *logic = findLogic(logic_id);
  1057. if (logic == nullptr)
  1058. {
  1059. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  1060. }
  1061. const bool create_rung = rung_id.empty();
  1062. if (!create_rung && findRung(logic_id, rung_id) == nullptr)
  1063. {
  1064. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  1065. }
  1066. if (create_rung
  1067. && logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
  1068. {
  1069. return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
  1070. }
  1071. if (!isConditionConfig(config))
  1072. {
  1073. return failure(LogicEditorError::InvalidNode, "梯形图条件不能使用线圈节点");
  1074. }
  1075. // 先生成稳定节点 ID,再把新节点接到已有表达式的串联末尾
  1076. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1077. const std::string target_rung_id = create_rung
  1078. ? makeUniqueRungId(*logic) : rung_id;
  1079. ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
  1080. HistoryState before = captureState();
  1081. const bool modified_before = project_service_.isModified();
  1082. Project &project = project_service_.editProject();
  1083. auto editable_logic = std::find_if(
  1084. project.controlLogics.begin(), project.controlLogics.end(),
  1085. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  1086. if (create_rung)
  1087. {
  1088. editable_logic->rungs.push_back({
  1089. target_rung_id,
  1090. "网络 " + std::to_string(editable_logic->rungs.size() + 1U),
  1091. {},
  1092. std::nullopt,
  1093. std::nullopt});
  1094. }
  1095. LadderRung *rung = findEditableRung(project, logic_id, target_rung_id);
  1096. if (!rung->condition.has_value())
  1097. {
  1098. rung->condition = std::move(leaf);
  1099. }
  1100. else if (rung->condition->kind == ConditionExpressionKind::Series)
  1101. {
  1102. rung->condition->children.push_back(std::move(leaf));
  1103. }
  1104. else
  1105. {
  1106. rung->condition = makeContainer(
  1107. makeUniqueExpressionId(*logic),
  1108. ConditionExpressionKind::Series,
  1109. std::move(*rung->condition),
  1110. std::move(leaf));
  1111. }
  1112. std::string validation_error;
  1113. // 编辑服务修改后立即做整条网络校验;失败就恢复包括脏标记在内的完整快照
  1114. if (!rung->validate(&validation_error))
  1115. {
  1116. rollbackEdit(std::move(before), modified_before);
  1117. return failure(LogicEditorError::InvalidOperation, validation_error);
  1118. }
  1119. recordHistory(std::move(before));
  1120. return {true, LogicEditorError::None, {}, node_id};
  1121. }
  1122. LogicEditorResult LogicEditorService::insertConditionAtColumn(
  1123. const std::string &logic_id,
  1124. const std::string &rung_id,
  1125. int column,
  1126. const LogicNodeConfig &config)
  1127. {
  1128. const ControlLogic *logic = findLogic(logic_id);
  1129. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1130. if (logic == nullptr || existing_rung == nullptr)
  1131. {
  1132. return failure(
  1133. logic == nullptr ? LogicEditorError::LogicNotFound
  1134. : LogicEditorError::RungNotFound,
  1135. logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
  1136. }
  1137. if (!isConditionConfig(config))
  1138. {
  1139. return failure(LogicEditorError::InvalidNode, "梯形图条件不能使用输出节点");
  1140. }
  1141. if (column < 0 || column >= ProjectLimits::kMaximumConditionColumns)
  1142. {
  1143. return failure(LogicEditorError::InvalidOperation, "条件插入列必须位于第 1~10 列");
  1144. }
  1145. const int occupied_columns = existing_rung->condition.has_value()
  1146. ? expressionColumns(*existing_rung->condition) : 0;
  1147. if (column < occupied_columns)
  1148. {
  1149. return failure(LogicEditorError::InvalidOperation, "所选网格已被条件或横线占用");
  1150. }
  1151. // 点击远端空网格时,用持久化横线填充前置空档,再放入真实条件节点
  1152. const int leading_wire_columns = column - occupied_columns;
  1153. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1154. ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
  1155. HistoryState before = captureState();
  1156. const bool modified_before = project_service_.isModified();
  1157. Project &project = project_service_.editProject();
  1158. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  1159. if (leading_wire_columns > 0)
  1160. {
  1161. ConditionExpression wire = ConditionExpression::fromWire(
  1162. makeUniqueWireId(*logic), leading_wire_columns);
  1163. if (!rung->condition.has_value())
  1164. {
  1165. rung->condition = makeContainer(
  1166. makeUniqueExpressionId(*logic),
  1167. ConditionExpressionKind::Series,
  1168. std::move(wire),
  1169. std::move(leaf));
  1170. }
  1171. else if (rung->condition->kind == ConditionExpressionKind::Series)
  1172. {
  1173. rung->condition->children.push_back(std::move(wire));
  1174. rung->condition->children.push_back(std::move(leaf));
  1175. }
  1176. else
  1177. {
  1178. ConditionExpression series;
  1179. series.id = makeUniqueExpressionId(*logic);
  1180. series.kind = ConditionExpressionKind::Series;
  1181. series.children.push_back(std::move(*rung->condition));
  1182. series.children.push_back(std::move(wire));
  1183. series.children.push_back(std::move(leaf));
  1184. rung->condition = std::move(series);
  1185. }
  1186. }
  1187. else if (!rung->condition.has_value())
  1188. {
  1189. rung->condition = std::move(leaf);
  1190. }
  1191. else if (rung->condition->kind == ConditionExpressionKind::Series)
  1192. {
  1193. rung->condition->children.push_back(std::move(leaf));
  1194. }
  1195. else
  1196. {
  1197. rung->condition = makeContainer(
  1198. makeUniqueExpressionId(*logic),
  1199. ConditionExpressionKind::Series,
  1200. std::move(*rung->condition),
  1201. std::move(leaf));
  1202. }
  1203. std::string validation_error;
  1204. // 原子提交要求候选表达式整体通过校验,否则撤销本次所有局部拼接
  1205. if (!rung->validate(&validation_error))
  1206. {
  1207. rollbackEdit(std::move(before), modified_before);
  1208. return failure(LogicEditorError::InvalidOperation, validation_error);
  1209. }
  1210. recordHistory(std::move(before));
  1211. return {true, LogicEditorError::None, {}, node_id};
  1212. }
  1213. LogicEditorResult LogicEditorService::insertConditionInBranchAtColumn(
  1214. const std::string &logic_id,
  1215. const std::string &rung_id,
  1216. const std::string &branch_expression_id,
  1217. int column,
  1218. const LogicNodeConfig &config)
  1219. {
  1220. const ControlLogic *logic = findLogic(logic_id);
  1221. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1222. if (logic == nullptr || existing_rung == nullptr
  1223. || !existing_rung->condition.has_value())
  1224. {
  1225. return failure(
  1226. logic == nullptr ? LogicEditorError::LogicNotFound
  1227. : LogicEditorError::RungNotFound,
  1228. logic == nullptr ? "未找到控制逻辑" : "未找到梯形图网络");
  1229. }
  1230. if (!isConditionConfig(config))
  1231. {
  1232. return failure(LogicEditorError::InvalidNode, "并联空网格只能插入条件节点");
  1233. }
  1234. const ConditionExpression *branch = findExpression(
  1235. logic_id, rung_id, branch_expression_id);
  1236. const ConditionExpression *branch_parent = findParentExpression(
  1237. *existing_rung->condition, branch_expression_id);
  1238. if (branch == nullptr || branch_parent == nullptr
  1239. || branch_parent->kind != ConditionExpressionKind::Parallel)
  1240. {
  1241. return failure(
  1242. LogicEditorError::ExpressionNotFound,
  1243. "未找到并联分支空网格");
  1244. }
  1245. const int branch_columns = expressionColumns(*branch);
  1246. const int parallel_columns = expressionColumns(*branch_parent);
  1247. if (column < branch_columns || column >= parallel_columns)
  1248. {
  1249. return failure(
  1250. LogicEditorError::InvalidOperation,
  1251. "所选位置不是并联分支中的可用空网格");
  1252. }
  1253. const int leading_wire_columns = column - branch_columns;
  1254. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1255. const std::string wire_id = leading_wire_columns > 0
  1256. ? makeUniqueWireId(*logic) : std::string{};
  1257. const std::string series_id = makeUniqueExpressionId(*logic);
  1258. ConditionExpression leaf = ConditionExpression::fromNode(
  1259. makeNode(node_id, config));
  1260. HistoryState before = captureState();
  1261. const bool modified_before = project_service_.isModified();
  1262. LadderRung *rung = findEditableRung(
  1263. project_service_.editProject(), logic_id, rung_id);
  1264. ConditionExpression *editable_branch = findConditionExpression(
  1265. *rung->condition, branch_expression_id);
  1266. if (editable_branch->kind == ConditionExpressionKind::Series)
  1267. {
  1268. if (leading_wire_columns > 0)
  1269. {
  1270. editable_branch->children.push_back(ConditionExpression::fromWire(
  1271. wire_id, leading_wire_columns));
  1272. }
  1273. editable_branch->children.push_back(std::move(leaf));
  1274. }
  1275. else
  1276. {
  1277. ConditionExpression series;
  1278. series.id = series_id;
  1279. series.kind = ConditionExpressionKind::Series;
  1280. series.children.push_back(std::move(*editable_branch));
  1281. if (leading_wire_columns > 0)
  1282. {
  1283. series.children.push_back(ConditionExpression::fromWire(
  1284. wire_id, leading_wire_columns));
  1285. }
  1286. series.children.push_back(std::move(leaf));
  1287. *editable_branch = std::move(series);
  1288. }
  1289. std::string validation_error;
  1290. if (!rung->validate(&validation_error))
  1291. {
  1292. rollbackEdit(std::move(before), modified_before);
  1293. return failure(LogicEditorError::InvalidOperation, validation_error);
  1294. }
  1295. recordHistory(std::move(before));
  1296. return {true, LogicEditorError::None, {}, node_id};
  1297. }
  1298. LogicEditorResult LogicEditorService::appendWire(
  1299. const std::string &logic_id,
  1300. const std::string &rung_id,
  1301. int column_span)
  1302. {
  1303. const ControlLogic *logic = findLogic(logic_id);
  1304. if (logic == nullptr)
  1305. {
  1306. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  1307. }
  1308. WireSegment wire{column_span};
  1309. std::string error;
  1310. if (!wire.validate(&error))
  1311. {
  1312. return failure(LogicEditorError::InvalidOperation, error);
  1313. }
  1314. const LadderRung *existing_rung = rung_id.empty()
  1315. ? nullptr : findRung(logic_id, rung_id);
  1316. if (!rung_id.empty() && existing_rung == nullptr)
  1317. {
  1318. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  1319. }
  1320. bool create_rung = rung_id.empty();
  1321. if (!create_rung && column_span == 1 && existing_rung->condition.has_value()
  1322. && expressionColumns(*existing_rung->condition)
  1323. >= ProjectLimits::kMaximumConditionColumns)
  1324. {
  1325. // 无选中目标的连续追加达到十列后,原子切换到下一个网络
  1326. create_rung = true;
  1327. }
  1328. if (create_rung
  1329. && logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
  1330. {
  1331. return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
  1332. }
  1333. const std::string target_rung_id = create_rung
  1334. ? makeUniqueRungId(*logic) : rung_id;
  1335. const std::string wire_id = makeUniqueWireId(*logic);
  1336. ConditionExpression leaf = ConditionExpression::fromWire(wire_id, column_span);
  1337. HistoryState before = captureState();
  1338. const bool modified_before = project_service_.isModified();
  1339. Project &project = project_service_.editProject();
  1340. auto editable_logic = std::find_if(
  1341. project.controlLogics.begin(), project.controlLogics.end(),
  1342. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  1343. if (create_rung)
  1344. {
  1345. editable_logic->rungs.push_back({
  1346. target_rung_id,
  1347. "网络 " + std::to_string(editable_logic->rungs.size() + 1U),
  1348. {},
  1349. std::nullopt,
  1350. std::nullopt});
  1351. }
  1352. LadderRung *rung = findEditableRung(project, logic_id, target_rung_id);
  1353. if (!rung->condition.has_value())
  1354. {
  1355. rung->condition = std::move(leaf);
  1356. }
  1357. else if (rung->condition->kind == ConditionExpressionKind::Series)
  1358. {
  1359. rung->condition->children.push_back(std::move(leaf));
  1360. }
  1361. else
  1362. {
  1363. rung->condition = makeContainer(
  1364. makeUniqueExpressionId(*logic),
  1365. ConditionExpressionKind::Series,
  1366. std::move(*rung->condition),
  1367. std::move(leaf));
  1368. }
  1369. std::string validation_error;
  1370. if (!rung->validate(&validation_error))
  1371. {
  1372. rollbackEdit(std::move(before), modified_before);
  1373. return failure(LogicEditorError::InvalidOperation, validation_error);
  1374. }
  1375. recordHistory(std::move(before));
  1376. return {true, LogicEditorError::None, {}, wire_id};
  1377. }
  1378. LogicEditorResult LogicEditorService::insertConditionAfter(
  1379. const std::string &logic_id,
  1380. const std::string &rung_id,
  1381. const std::string &target_node_id,
  1382. const LogicNodeConfig &config)
  1383. {
  1384. const ControlLogic *logic = findLogic(logic_id);
  1385. if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
  1386. {
  1387. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  1388. }
  1389. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1390. if (!isConditionConfig(config)
  1391. || existing_rung == nullptr
  1392. || !existing_rung->condition.has_value()
  1393. || findConditionNode(*existing_rung->condition, target_node_id) == nullptr)
  1394. {
  1395. return failure(LogicEditorError::NodeNotFound, "未找到串联插入目标");
  1396. }
  1397. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1398. ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
  1399. HistoryState before = captureState();
  1400. const bool modified_before = project_service_.isModified();
  1401. Project &project = project_service_.editProject();
  1402. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  1403. ConditionExpression *target = findConditionExpression(*rung->condition, target_node_id);
  1404. ConditionExpression *parent = findParentExpression(*rung->condition, target_node_id);
  1405. if (parent != nullptr && parent->kind == ConditionExpressionKind::Series)
  1406. {
  1407. const auto target_iterator = std::find_if(
  1408. parent->children.begin(), parent->children.end(),
  1409. [&target_node_id](const ConditionExpression &child)
  1410. {
  1411. return child.id == target_node_id;
  1412. });
  1413. const auto insertion_position = target_iterator + 1;
  1414. if (insertion_position != parent->children.end()
  1415. && insertion_position->kind == ConditionExpressionKind::Wire)
  1416. {
  1417. // 横线代表已占用的网格位置,连续插入时先消费一格而不是扩展网络
  1418. if (insertion_position->wire->columnSpan == 1)
  1419. {
  1420. *insertion_position = std::move(leaf);
  1421. }
  1422. else
  1423. {
  1424. --insertion_position->wire->columnSpan;
  1425. parent->children.insert(insertion_position, std::move(leaf));
  1426. }
  1427. }
  1428. else
  1429. {
  1430. parent->children.insert(insertion_position, std::move(leaf));
  1431. }
  1432. }
  1433. else
  1434. {
  1435. ConditionExpression original = std::move(*target);
  1436. *target = makeContainer(
  1437. makeUniqueExpressionId(*logic),
  1438. ConditionExpressionKind::Series,
  1439. std::move(original),
  1440. std::move(leaf));
  1441. }
  1442. std::string validation_error;
  1443. if (!rung->validate(&validation_error))
  1444. {
  1445. rollbackEdit(std::move(before), modified_before);
  1446. return failure(LogicEditorError::InvalidOperation, validation_error);
  1447. }
  1448. recordHistory(std::move(before));
  1449. return {true, LogicEditorError::None, {}, node_id};
  1450. }
  1451. LogicEditorResult LogicEditorService::insertWireAfter(
  1452. const std::string &logic_id,
  1453. const std::string &rung_id,
  1454. const std::string &target_expression_id,
  1455. int column_span)
  1456. {
  1457. const ControlLogic *logic = findLogic(logic_id);
  1458. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1459. const ConditionExpression *target = findExpression(
  1460. logic_id, rung_id, target_expression_id);
  1461. WireSegment wire{column_span};
  1462. std::string error;
  1463. if (logic == nullptr || existing_rung == nullptr || target == nullptr)
  1464. {
  1465. return failure(LogicEditorError::ExpressionNotFound, "未找到横线插入位置");
  1466. }
  1467. if ((target->kind != ConditionExpressionKind::Node
  1468. && target->kind != ConditionExpressionKind::Wire)
  1469. || !wire.validate(&error))
  1470. {
  1471. return failure(
  1472. LogicEditorError::InvalidOperation,
  1473. error.empty() ? "只能在触点或横线后插入横线" : error);
  1474. }
  1475. const std::string wire_id = makeUniqueWireId(*logic);
  1476. ConditionExpression leaf = ConditionExpression::fromWire(wire_id, column_span);
  1477. HistoryState before = captureState();
  1478. const bool modified_before = project_service_.isModified();
  1479. LadderRung *rung = findEditableRung(
  1480. project_service_.editProject(), logic_id, rung_id);
  1481. ConditionExpression *editable_target = findConditionExpression(
  1482. *rung->condition, target_expression_id);
  1483. ConditionExpression *parent = findParentExpression(
  1484. *rung->condition, target_expression_id);
  1485. if (parent != nullptr && parent->kind == ConditionExpressionKind::Series)
  1486. {
  1487. const auto target_iterator = std::find_if(
  1488. parent->children.begin(), parent->children.end(),
  1489. [&target_expression_id](const ConditionExpression &child)
  1490. {
  1491. return child.id == target_expression_id;
  1492. });
  1493. parent->children.insert(target_iterator + 1, std::move(leaf));
  1494. }
  1495. else
  1496. {
  1497. ConditionExpression original = std::move(*editable_target);
  1498. *editable_target = makeContainer(
  1499. makeUniqueExpressionId(*logic),
  1500. ConditionExpressionKind::Series,
  1501. std::move(original),
  1502. std::move(leaf));
  1503. }
  1504. std::string validation_error;
  1505. if (!rung->validate(&validation_error))
  1506. {
  1507. rollbackEdit(std::move(before), modified_before);
  1508. return failure(LogicEditorError::InvalidOperation, validation_error);
  1509. }
  1510. recordHistory(std::move(before));
  1511. return {true, LogicEditorError::None, {}, wire_id};
  1512. }
  1513. LogicEditorResult LogicEditorService::replaceWireWithCondition(
  1514. const std::string &logic_id,
  1515. const std::string &rung_id,
  1516. const std::string &wire_expression_id,
  1517. const LogicNodeConfig &config)
  1518. {
  1519. const ControlLogic *logic = findLogic(logic_id);
  1520. const ConditionExpression *wire = findExpression(
  1521. logic_id, rung_id, wire_expression_id);
  1522. if (logic == nullptr || wire == nullptr
  1523. || wire->kind != ConditionExpressionKind::Wire)
  1524. {
  1525. return failure(LogicEditorError::ExpressionNotFound, "未找到要替换的横线");
  1526. }
  1527. if (!isConditionConfig(config))
  1528. {
  1529. return failure(LogicEditorError::InvalidNode, "横线只能替换为条件节点");
  1530. }
  1531. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1532. HistoryState before = captureState();
  1533. const bool modified_before = project_service_.isModified();
  1534. LadderRung *rung = findEditableRung(
  1535. project_service_.editProject(), logic_id, rung_id);
  1536. ConditionExpression *editable = findConditionExpression(
  1537. *rung->condition, wire_expression_id);
  1538. *editable = ConditionExpression::fromNode(makeNode(node_id, config));
  1539. std::string validation_error;
  1540. if (!rung->validate(&validation_error))
  1541. {
  1542. rollbackEdit(std::move(before), modified_before);
  1543. return failure(LogicEditorError::InvalidNode, validation_error);
  1544. }
  1545. recordHistory(std::move(before));
  1546. return {true, LogicEditorError::None, {}, node_id};
  1547. }
  1548. LogicEditorResult LogicEditorService::replaceWireColumnWithCondition(
  1549. const std::string &logic_id,
  1550. const std::string &rung_id,
  1551. const std::string &wire_expression_id,
  1552. int column_offset,
  1553. const LogicNodeConfig &config)
  1554. {
  1555. const ControlLogic *logic = findLogic(logic_id);
  1556. const ConditionExpression *wire = findExpression(
  1557. logic_id, rung_id, wire_expression_id);
  1558. if (logic == nullptr || wire == nullptr
  1559. || wire->kind != ConditionExpressionKind::Wire)
  1560. {
  1561. return failure(LogicEditorError::ExpressionNotFound, "未找到要替换的横线");
  1562. }
  1563. if (!isConditionConfig(config))
  1564. {
  1565. return failure(LogicEditorError::InvalidNode, "横线只能替换为条件节点");
  1566. }
  1567. if (column_offset < 0 || column_offset >= wire->wire->columnSpan)
  1568. {
  1569. return failure(LogicEditorError::InvalidOperation, "横线网格偏移超出有效范围");
  1570. }
  1571. if (wire->wire->columnSpan == 1)
  1572. {
  1573. return replaceWireWithCondition(
  1574. logic_id, rung_id, wire_expression_id, config);
  1575. }
  1576. const int leading_columns = column_offset;
  1577. const int trailing_columns = wire->wire->columnSpan - column_offset - 1;
  1578. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1579. ConditionExpression replacement;
  1580. replacement.id = makeUniqueExpressionId(*logic);
  1581. replacement.kind = ConditionExpressionKind::Series;
  1582. if (leading_columns > 0)
  1583. {
  1584. replacement.children.push_back(ConditionExpression::fromWire(
  1585. wire_expression_id, leading_columns));
  1586. }
  1587. replacement.children.push_back(
  1588. ConditionExpression::fromNode(makeNode(node_id, config)));
  1589. if (trailing_columns > 0)
  1590. {
  1591. replacement.children.push_back(ConditionExpression::fromWire(
  1592. leading_columns > 0 ? makeUniqueWireId(*logic) : wire_expression_id,
  1593. trailing_columns));
  1594. }
  1595. HistoryState before = captureState();
  1596. const bool modified_before = project_service_.isModified();
  1597. Project &project = project_service_.editProject();
  1598. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  1599. ConditionExpression *editable = findConditionExpression(
  1600. *rung->condition, wire_expression_id);
  1601. *editable = std::move(replacement);
  1602. normalizeConditionExpression(&rung->condition);
  1603. std::string validation_error;
  1604. if (!rung->validate(&validation_error))
  1605. {
  1606. rollbackEdit(std::move(before), modified_before);
  1607. return failure(LogicEditorError::InvalidNode, validation_error);
  1608. }
  1609. recordHistory(std::move(before));
  1610. return {true, LogicEditorError::None, {}, node_id};
  1611. }
  1612. LogicEditorResult LogicEditorService::addParallelBranch(
  1613. const std::string &logic_id,
  1614. const std::string &rung_id,
  1615. const std::vector<std::string> &selected_node_ids,
  1616. const LogicNodeConfig &config)
  1617. {
  1618. const ControlLogic *logic = findLogic(logic_id);
  1619. if (logic == nullptr || findRung(logic_id, rung_id) == nullptr)
  1620. {
  1621. return failure(LogicEditorError::RungNotFound, "未找到梯形图网络");
  1622. }
  1623. if (!isConditionConfig(config) || selected_node_ids.empty())
  1624. {
  1625. return failure(LogicEditorError::InvalidOperation, "建立并联支路前必须选择节点");
  1626. }
  1627. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1628. if (!existing_rung->condition.has_value())
  1629. {
  1630. return failure(LogicEditorError::InvalidOperation, "梯形图网络尚无条件节点");
  1631. }
  1632. NodeIdSet selected_ids(selected_node_ids.cbegin(), selected_node_ids.cend());
  1633. if (selected_ids.size() != selected_node_ids.size())
  1634. {
  1635. return failure(
  1636. LogicEditorError::InvalidOperation,
  1637. "并联选择中存在重复节点");
  1638. }
  1639. const NodeIdSet available_ids = conditionLeafIds(*existing_rung->condition);
  1640. if (!isSubset(selected_ids, available_ids))
  1641. {
  1642. return failure(
  1643. LogicEditorError::NodeNotFound,
  1644. "并联选择中包含未知节点");
  1645. }
  1646. const std::string node_id = makeUniqueNodeId(*logic, nodePrefix(config));
  1647. ConditionExpression leaf = ConditionExpression::fromNode(makeNode(node_id, config));
  1648. const std::string parallel_id = makeUniqueExpressionId(*logic);
  1649. std::string series_id = parallel_id + "-range";
  1650. while (findExpression(logic_id, rung_id, series_id) != nullptr)
  1651. {
  1652. series_id += "-range";
  1653. }
  1654. HistoryState before = captureState();
  1655. const bool modified_before = project_service_.isModified();
  1656. Project &project = project_service_.editProject();
  1657. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  1658. if (!addParallelForSelection(
  1659. &*rung->condition,
  1660. selected_ids,
  1661. &leaf,
  1662. parallel_id,
  1663. series_id))
  1664. {
  1665. rollbackEdit(std::move(before), modified_before);
  1666. return failure(
  1667. LogicEditorError::InvalidOperation,
  1668. "并联选择必须是一个连续的逻辑范围");
  1669. }
  1670. std::string validation_error;
  1671. if (!rung->validate(&validation_error))
  1672. {
  1673. rollbackEdit(std::move(before), modified_before);
  1674. return failure(LogicEditorError::InvalidOperation, validation_error);
  1675. }
  1676. recordHistory(std::move(before));
  1677. return {true, LogicEditorError::None, {}, node_id};
  1678. }
  1679. LogicEditorResult LogicEditorService::addParallelWireBranch(
  1680. const std::string &logic_id,
  1681. const std::string &rung_id,
  1682. const std::vector<std::string> &selected_expression_ids)
  1683. {
  1684. const ControlLogic *logic = findLogic(logic_id);
  1685. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1686. if (logic == nullptr || existing_rung == nullptr
  1687. || !existing_rung->condition.has_value())
  1688. {
  1689. return failure(LogicEditorError::RungNotFound, "未找到梯形图条件网络");
  1690. }
  1691. if (selected_expression_ids.empty())
  1692. {
  1693. return failure(
  1694. LogicEditorError::InvalidOperation,
  1695. "建立竖线连接前必须选择连续的触点或横线");
  1696. }
  1697. NodeIdSet selected_ids(
  1698. selected_expression_ids.cbegin(), selected_expression_ids.cend());
  1699. if (selected_ids.size() != selected_expression_ids.size())
  1700. {
  1701. return failure(LogicEditorError::InvalidOperation, "并联选择中存在重复对象");
  1702. }
  1703. const NodeIdSet available_ids = conditionLeafIds(*existing_rung->condition);
  1704. if (!isSubset(selected_ids, available_ids))
  1705. {
  1706. return failure(LogicEditorError::ExpressionNotFound, "并联选择中包含未知对象");
  1707. }
  1708. const std::optional<int> column_span = selectedExpressionColumns(
  1709. *existing_rung->condition, selected_ids);
  1710. if (!column_span.has_value()
  1711. || *column_span > WireSegment::kMaximumColumnSpan)
  1712. {
  1713. return failure(
  1714. LogicEditorError::InvalidOperation,
  1715. "竖线连接只能围绕同一支路中的连续逻辑范围");
  1716. }
  1717. const std::string wire_id = makeUniqueWireId(*logic);
  1718. ConditionExpression wire = ConditionExpression::fromWire(
  1719. wire_id, *column_span);
  1720. const std::string parallel_id = makeUniqueExpressionId(*logic);
  1721. std::string series_id = parallel_id + "-range";
  1722. while (findExpression(logic_id, rung_id, series_id) != nullptr)
  1723. {
  1724. series_id += "-range";
  1725. }
  1726. HistoryState before = captureState();
  1727. const bool modified_before = project_service_.isModified();
  1728. LadderRung *rung = findEditableRung(
  1729. project_service_.editProject(), logic_id, rung_id);
  1730. if (!addParallelForSelection(
  1731. &*rung->condition,
  1732. selected_ids,
  1733. &wire,
  1734. parallel_id,
  1735. series_id))
  1736. {
  1737. rollbackEdit(std::move(before), modified_before);
  1738. return failure(
  1739. LogicEditorError::InvalidOperation,
  1740. "竖线连接只能围绕同一支路中的连续逻辑范围");
  1741. }
  1742. std::string validation_error;
  1743. if (!rung->validate(&validation_error))
  1744. {
  1745. rollbackEdit(std::move(before), modified_before);
  1746. return failure(LogicEditorError::InvalidOperation, validation_error);
  1747. }
  1748. recordHistory(std::move(before));
  1749. return {true, LogicEditorError::None, {}, wire_id};
  1750. }
  1751. LogicEditorResult LogicEditorService::addParallelWireBranchAtCells(
  1752. const std::string &logic_id,
  1753. const std::string &rung_id,
  1754. const std::vector<std::pair<std::string, int>> &selected_wire_cells)
  1755. {
  1756. const ControlLogic *logic = findLogic(logic_id);
  1757. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  1758. if (logic == nullptr || existing_rung == nullptr
  1759. || !existing_rung->condition.has_value())
  1760. {
  1761. return failure(LogicEditorError::RungNotFound, "未找到梯形图条件网络");
  1762. }
  1763. if (selected_wire_cells.empty())
  1764. {
  1765. return failure(
  1766. LogicEditorError::InvalidOperation,
  1767. "建立竖线连接前必须选择横线网格");
  1768. }
  1769. struct SelectedWireCell
  1770. {
  1771. std::string wire_id;
  1772. int offset = 0;
  1773. int absolute_column = 0;
  1774. };
  1775. std::vector<SelectedWireCell> cells;
  1776. cells.reserve(selected_wire_cells.size());
  1777. for (const auto &cell : selected_wire_cells)
  1778. {
  1779. if (cell.first.empty())
  1780. {
  1781. return failure(
  1782. LogicEditorError::ExpressionNotFound,
  1783. "未找到选中的横线");
  1784. }
  1785. const ConditionExpression *wire = findExpression(
  1786. logic_id, rung_id, cell.first);
  1787. if (wire == nullptr || wire->kind != ConditionExpressionKind::Wire
  1788. || !wire->wire.has_value()
  1789. || cell.second < 0 || cell.second >= wire->wire->columnSpan)
  1790. {
  1791. return failure(
  1792. LogicEditorError::InvalidOperation,
  1793. "竖线网格列偏移超出横线范围");
  1794. }
  1795. cells.push_back({cell.first, cell.second, 0});
  1796. }
  1797. const bool same_wire = std::all_of(
  1798. cells.cbegin(), cells.cend(),
  1799. [&cells](const SelectedWireCell &cell)
  1800. {
  1801. return cell.wire_id == cells.front().wire_id;
  1802. });
  1803. const std::string wire_expression_id = cells.front().wire_id;
  1804. const ConditionExpression *selected_wire = findExpression(
  1805. logic_id, rung_id, wire_expression_id);
  1806. if (selected_wire == nullptr || !selected_wire->wire.has_value())
  1807. {
  1808. return failure(LogicEditorError::ExpressionNotFound, "未找到选中的横线");
  1809. }
  1810. int leading_columns = 0;
  1811. int trailing_columns = 0;
  1812. std::string selected_series_id;
  1813. std::size_t selected_first_child = 0U;
  1814. std::size_t selected_last_child = 0U;
  1815. if (same_wire)
  1816. {
  1817. std::vector<int> offsets;
  1818. offsets.reserve(cells.size());
  1819. for (const SelectedWireCell &cell : cells)
  1820. {
  1821. offsets.push_back(cell.offset);
  1822. }
  1823. std::sort(offsets.begin(), offsets.end());
  1824. if (std::adjacent_find(offsets.cbegin(), offsets.cend())
  1825. != offsets.cend()
  1826. || offsets.back() - offsets.front() + 1
  1827. != static_cast<int>(offsets.size()))
  1828. {
  1829. return failure(
  1830. LogicEditorError::InvalidOperation,
  1831. "竖线网格必须是连续列");
  1832. }
  1833. for (SelectedWireCell &cell : cells)
  1834. {
  1835. cell.absolute_column = cell.offset;
  1836. }
  1837. leading_columns = offsets.front();
  1838. trailing_columns = selected_wire->wire->columnSpan
  1839. - offsets.front() - static_cast<int>(offsets.size());
  1840. std::sort(
  1841. cells.begin(), cells.end(),
  1842. [](const SelectedWireCell &left, const SelectedWireCell &right)
  1843. {
  1844. return left.absolute_column < right.absolute_column;
  1845. });
  1846. }
  1847. else
  1848. {
  1849. const ConditionExpression *source_series = nullptr;
  1850. for (SelectedWireCell &cell : cells)
  1851. {
  1852. WireCellLocation location;
  1853. if (!findWireCellLocation(
  1854. *existing_rung->condition, cell.wire_id, &location)
  1855. || location.series == nullptr)
  1856. {
  1857. return failure(
  1858. LogicEditorError::InvalidOperation,
  1859. "竖线网格必须位于同一条连续串联路径");
  1860. }
  1861. if (source_series == nullptr)
  1862. {
  1863. source_series = location.series;
  1864. }
  1865. else if (source_series != location.series)
  1866. {
  1867. return failure(
  1868. LogicEditorError::InvalidOperation,
  1869. "竖线网格必须位于同一条连续串联路径");
  1870. }
  1871. cell.absolute_column = location.child_start + cell.offset;
  1872. }
  1873. std::sort(
  1874. cells.begin(), cells.end(),
  1875. [](const SelectedWireCell &left, const SelectedWireCell &right)
  1876. {
  1877. return left.absolute_column < right.absolute_column;
  1878. });
  1879. if (std::adjacent_find(
  1880. cells.cbegin(), cells.cend(),
  1881. [](const SelectedWireCell &left, const SelectedWireCell &right)
  1882. {
  1883. return left.absolute_column == right.absolute_column;
  1884. }) != cells.cend()
  1885. || cells.back().absolute_column - cells.front().absolute_column + 1
  1886. != static_cast<int>(cells.size()))
  1887. {
  1888. return failure(
  1889. LogicEditorError::InvalidOperation,
  1890. "竖线网格必须是连续列");
  1891. }
  1892. int child_start = 0;
  1893. std::size_t first_child = source_series->children.size();
  1894. std::size_t last_child = source_series->children.size();
  1895. const int range_begin = cells.front().absolute_column;
  1896. const int range_end = cells.back().absolute_column + 1;
  1897. for (std::size_t index = 0; index < source_series->children.size(); ++index)
  1898. {
  1899. const ConditionExpression &child = source_series->children[index];
  1900. const int child_end = child_start + expressionColumns(child);
  1901. if (child_end > range_begin && child_start < range_end)
  1902. {
  1903. if (child.kind != ConditionExpressionKind::Wire)
  1904. {
  1905. return failure(
  1906. LogicEditorError::InvalidOperation,
  1907. "竖线网格不能跨越触点或并联支路");
  1908. }
  1909. if (first_child == source_series->children.size())
  1910. {
  1911. first_child = index;
  1912. }
  1913. last_child = index;
  1914. }
  1915. child_start = child_end;
  1916. }
  1917. if (first_child == source_series->children.size())
  1918. {
  1919. return failure(
  1920. LogicEditorError::InvalidOperation,
  1921. "未找到连续的横线网格范围");
  1922. }
  1923. selected_series_id = source_series->id;
  1924. selected_first_child = first_child;
  1925. selected_last_child = last_child;
  1926. int layout_start = 0;
  1927. for (std::size_t index = 0; index < first_child; ++index)
  1928. {
  1929. layout_start += expressionColumns(source_series->children[index]);
  1930. }
  1931. int selected_end = layout_start;
  1932. for (std::size_t index = first_child;
  1933. index <= last_child;
  1934. ++index)
  1935. {
  1936. selected_end += expressionColumns(source_series->children[index]);
  1937. }
  1938. leading_columns = cells.front().absolute_column - layout_start;
  1939. trailing_columns = selected_end - (cells.back().absolute_column + 1);
  1940. }
  1941. const int first_column = cells.front().absolute_column;
  1942. const int selected_columns = static_cast<int>(cells.size());
  1943. std::unordered_set<std::string> generated_wire_ids;
  1944. const auto makeGeneratedWireId = [&]()
  1945. {
  1946. std::string candidate = makeUniqueWireId(*logic);
  1947. while (generated_wire_ids.count(candidate) != 0U
  1948. || findExpression(logic_id, rung_id, candidate) != nullptr)
  1949. {
  1950. candidate += "-split";
  1951. }
  1952. generated_wire_ids.insert(candidate);
  1953. return candidate;
  1954. };
  1955. const std::string branch_wire_id = makeGeneratedWireId();
  1956. const std::string selected_wire_id = same_wire
  1957. ? wire_expression_id : cells.front().wire_id;
  1958. const std::string leading_wire_id = leading_columns > 0
  1959. ? makeGeneratedWireId() : std::string{};
  1960. const std::string trailing_wire_id = trailing_columns > 0
  1961. ? makeGeneratedWireId() : std::string{};
  1962. ConditionExpression branch = ConditionExpression::fromWire(
  1963. branch_wire_id, selected_columns);
  1964. const std::string parallel_id = makeUniqueExpressionId(*logic);
  1965. std::string series_id = parallel_id + "-range";
  1966. while (findExpression(logic_id, rung_id, series_id) != nullptr)
  1967. {
  1968. series_id += "-range";
  1969. }
  1970. HistoryState before = captureState();
  1971. const bool modified_before = project_service_.isModified();
  1972. LadderRung *rung = findEditableRung(
  1973. project_service_.editProject(), logic_id, rung_id);
  1974. bool inserted = false;
  1975. if (same_wire)
  1976. {
  1977. inserted = addParallelForWireCellRange(
  1978. &*rung->condition,
  1979. wire_expression_id,
  1980. first_column,
  1981. selected_columns,
  1982. &branch,
  1983. parallel_id,
  1984. series_id,
  1985. leading_wire_id,
  1986. trailing_wire_id);
  1987. }
  1988. else
  1989. {
  1990. inserted = addParallelForWireCellSeriesRange(
  1991. &*rung->condition,
  1992. selected_series_id,
  1993. selected_first_child,
  1994. selected_last_child,
  1995. leading_columns,
  1996. selected_columns,
  1997. trailing_columns,
  1998. &branch,
  1999. selected_wire_id,
  2000. parallel_id,
  2001. leading_wire_id,
  2002. trailing_wire_id);
  2003. }
  2004. if (!inserted)
  2005. {
  2006. rollbackEdit(std::move(before), modified_before);
  2007. return failure(
  2008. LogicEditorError::InvalidOperation,
  2009. "竖线连接只能围绕同一条横线中的连续网格建立");
  2010. }
  2011. normalizeConditionExpression(&rung->condition);
  2012. std::string validation_error;
  2013. if (!rung->validate(&validation_error))
  2014. {
  2015. rollbackEdit(std::move(before), modified_before);
  2016. return failure(LogicEditorError::InvalidOperation, validation_error);
  2017. }
  2018. recordHistory(std::move(before));
  2019. return {true, LogicEditorError::None, {}, branch_wire_id};
  2020. }
  2021. LogicEditorResult LogicEditorService::setOutput(
  2022. const std::string &logic_id,
  2023. const std::string &rung_id,
  2024. const LogicNodeConfig &config,
  2025. bool configured)
  2026. {
  2027. const ControlLogic *logic = findLogic(logic_id);
  2028. const LadderRung *existing_rung = rung_id.empty()
  2029. ? nullptr : findRung(logic_id, rung_id);
  2030. if (logic == nullptr || (!rung_id.empty() && existing_rung == nullptr)
  2031. || !isOutputConfig(config))
  2032. {
  2033. return failure(
  2034. logic == nullptr ? LogicEditorError::LogicNotFound
  2035. : existing_rung == nullptr && !rung_id.empty()
  2036. ? LogicEditorError::RungNotFound
  2037. : LogicEditorError::InvalidNode,
  2038. logic == nullptr ? "未找到控制逻辑"
  2039. : existing_rung == nullptr && !rung_id.empty()
  2040. ? "未找到梯形图网络"
  2041. : "梯形图输出必须使用有效的输出指令");
  2042. }
  2043. if (rung_id.empty()
  2044. && logic->rungs.size() >= ProjectLimits::kMaximumRungsPerLogic)
  2045. {
  2046. return failure(LogicEditorError::InvalidOperation, "单组控制逻辑最多包含 1024 个网络");
  2047. }
  2048. const bool create_rung = rung_id.empty();
  2049. const std::string target_rung_id = create_rung
  2050. ? makeUniqueRungId(*logic) : rung_id;
  2051. const std::string node_id = existing_rung != nullptr
  2052. && existing_rung->output.has_value()
  2053. ? existing_rung->output->id : makeUniqueNodeId(*logic, nodePrefix(config));
  2054. LogicNode node = makeNode(node_id, config);
  2055. node.configured = configured;
  2056. std::string validation_error;
  2057. if (!node.validate(&validation_error))
  2058. {
  2059. return failure(LogicEditorError::InvalidNode, validation_error);
  2060. }
  2061. if (existing_rung != nullptr && existing_rung->output.has_value()
  2062. && nodesEqual(*existing_rung->output, node))
  2063. {
  2064. return {true, LogicEditorError::None, {}, node_id};
  2065. }
  2066. HistoryState before = captureState();
  2067. Project &project = project_service_.editProject();
  2068. auto editable_logic = std::find_if(
  2069. project.controlLogics.begin(), project.controlLogics.end(),
  2070. [&logic_id](const ControlLogic &candidate) { return candidate.id == logic_id; });
  2071. if (create_rung)
  2072. {
  2073. editable_logic->rungs.push_back({
  2074. target_rung_id,
  2075. "网络 " + std::to_string(editable_logic->rungs.size() + 1U),
  2076. {},
  2077. std::nullopt,
  2078. std::nullopt});
  2079. }
  2080. findEditableRung(project, logic_id, target_rung_id)->output = std::move(node);
  2081. recordHistory(std::move(before));
  2082. return {true, LogicEditorError::None, {}, node_id};
  2083. }
  2084. LogicEditorResult LogicEditorService::updateNodeConfig(
  2085. const std::string &logic_id,
  2086. const std::string &node_id,
  2087. const LogicNodeConfig &config)
  2088. {
  2089. const LogicNode *node = findNode(logic_id, node_id);
  2090. if (node == nullptr)
  2091. {
  2092. return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
  2093. }
  2094. if (node->config.index() != config.index())
  2095. {
  2096. return failure(
  2097. LogicEditorError::UnsupportedNodeChange,
  2098. "节点创建后不能修改节点类别");
  2099. }
  2100. LogicNode candidate{node_id, config, true};
  2101. std::string error;
  2102. if (!candidate.validate(&error))
  2103. {
  2104. return failure(LogicEditorError::InvalidNode, error);
  2105. }
  2106. if (nodesEqual(*node, candidate))
  2107. {
  2108. return {true, LogicEditorError::None, {}, node_id};
  2109. }
  2110. HistoryState before = captureState();
  2111. const bool modified_before = project_service_.isModified();
  2112. Project &project = project_service_.editProject();
  2113. for (ControlLogic &logic : project.controlLogics)
  2114. {
  2115. if (logic.id != logic_id)
  2116. {
  2117. continue;
  2118. }
  2119. for (LadderRung &rung : logic.rungs)
  2120. {
  2121. if (rung.output.has_value() && rung.output->id == node_id)
  2122. {
  2123. *rung.output = candidate;
  2124. recordHistory(std::move(before));
  2125. return {true, LogicEditorError::None, {}, node_id};
  2126. }
  2127. if (rung.condition.has_value())
  2128. {
  2129. if (LogicNode *editable = findConditionNode(*rung.condition, node_id))
  2130. {
  2131. *editable = candidate;
  2132. recordHistory(std::move(before));
  2133. return {true, LogicEditorError::None, {}, node_id};
  2134. }
  2135. }
  2136. }
  2137. }
  2138. project_service_.restoreModifiedState(modified_before);
  2139. return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
  2140. }
  2141. LogicEditorResult LogicEditorService::removeNode(
  2142. const std::string &logic_id, const std::string &node_id)
  2143. {
  2144. return removeNodes(logic_id, {node_id});
  2145. }
  2146. LogicEditorResult LogicEditorService::removeNodes(
  2147. const std::string &logic_id,
  2148. const std::vector<std::string> &node_ids)
  2149. {
  2150. if (findLogic(logic_id) == nullptr)
  2151. {
  2152. return failure(LogicEditorError::LogicNotFound, "未找到控制逻辑");
  2153. }
  2154. if (node_ids.empty())
  2155. {
  2156. return failure(LogicEditorError::InvalidOperation, "请先选择要删除的逻辑节点");
  2157. }
  2158. std::unordered_set<std::string> selected_ids;
  2159. for (const std::string &node_id : node_ids)
  2160. {
  2161. if (!selected_ids.insert(node_id).second)
  2162. {
  2163. return failure(LogicEditorError::InvalidOperation, "删除列表中存在重复节点");
  2164. }
  2165. if (findNode(logic_id, node_id) == nullptr)
  2166. {
  2167. return failure(LogicEditorError::NodeNotFound, "未找到逻辑节点");
  2168. }
  2169. }
  2170. HistoryState before = captureState();
  2171. const bool modified_before = project_service_.isModified();
  2172. Project &project = project_service_.editProject();
  2173. ControlLogic *editable_logic = nullptr;
  2174. for (ControlLogic &candidate : project.controlLogics)
  2175. {
  2176. if (candidate.id == logic_id)
  2177. {
  2178. editable_logic = &candidate;
  2179. break;
  2180. }
  2181. }
  2182. for (LadderRung &rung : editable_logic->rungs)
  2183. {
  2184. if (rung.output.has_value()
  2185. && selected_ids.find(rung.output->id) != selected_ids.end())
  2186. {
  2187. rung.output.reset();
  2188. }
  2189. if (rung.condition.has_value())
  2190. {
  2191. for (const std::string &node_id : node_ids)
  2192. {
  2193. if (rung.condition->kind == ConditionExpressionKind::Node
  2194. && rung.condition->id == node_id)
  2195. {
  2196. rung.condition.reset();
  2197. break;
  2198. }
  2199. removeExpressionRecursive(&*rung.condition, node_id);
  2200. }
  2201. if (rung.condition.has_value())
  2202. {
  2203. normalizeConditionExpression(&rung.condition);
  2204. }
  2205. }
  2206. }
  2207. for (const LadderRung &rung : editable_logic->rungs)
  2208. {
  2209. std::string validation_error;
  2210. if (!rung.validate(&validation_error))
  2211. {
  2212. rollbackEdit(std::move(before), modified_before);
  2213. return failure(LogicEditorError::InvalidOperation, validation_error);
  2214. }
  2215. }
  2216. recordHistory(std::move(before));
  2217. return {true, LogicEditorError::None, {}, node_ids.front()};
  2218. }
  2219. LogicEditorResult LogicEditorService::removeExpression(
  2220. const std::string &logic_id,
  2221. const std::string &rung_id,
  2222. const std::string &expression_id)
  2223. {
  2224. if (findExpression(logic_id, rung_id, expression_id) == nullptr)
  2225. {
  2226. return failure(LogicEditorError::ExpressionNotFound, "未找到条件支路");
  2227. }
  2228. HistoryState before = captureState();
  2229. Project &project = project_service_.editProject();
  2230. LadderRung *rung = findEditableRung(project, logic_id, rung_id);
  2231. if (rung->condition->id == expression_id)
  2232. {
  2233. rung->condition.reset();
  2234. }
  2235. else
  2236. {
  2237. removeExpressionRecursive(&*rung->condition, expression_id);
  2238. normalizeConditionExpression(&rung->condition);
  2239. }
  2240. recordHistory(std::move(before));
  2241. return {true, LogicEditorError::None, {}, expression_id};
  2242. }
  2243. LogicEditorResult LogicEditorService::removeExpressions(
  2244. const std::string &logic_id,
  2245. const std::string &rung_id,
  2246. const std::vector<std::string> &expression_ids)
  2247. {
  2248. const LadderRung *existing_rung = findRung(logic_id, rung_id);
  2249. if (existing_rung == nullptr || !existing_rung->condition.has_value())
  2250. {
  2251. return failure(LogicEditorError::RungNotFound, "未找到梯形图条件网络");
  2252. }
  2253. if (expression_ids.empty())
  2254. {
  2255. return failure(LogicEditorError::InvalidOperation, "请先选择要删除的条件或横线");
  2256. }
  2257. NodeIdSet unique_ids;
  2258. for (const std::string &expression_id : expression_ids)
  2259. {
  2260. const ConditionExpression *expression = findExpression(
  2261. logic_id, rung_id, expression_id);
  2262. if (!unique_ids.insert(expression_id).second)
  2263. {
  2264. return failure(LogicEditorError::InvalidOperation, "删除列表中存在重复对象");
  2265. }
  2266. if (expression == nullptr)
  2267. {
  2268. return failure(
  2269. LogicEditorError::ExpressionNotFound,
  2270. "删除列表中包含未知条件表达式");
  2271. }
  2272. }
  2273. HistoryState before = captureState();
  2274. LadderRung *rung = findEditableRung(
  2275. project_service_.editProject(), logic_id, rung_id);
  2276. for (const std::string &expression_id : expression_ids)
  2277. {
  2278. if (!rung->condition.has_value())
  2279. {
  2280. break;
  2281. }
  2282. if (rung->condition->id == expression_id)
  2283. {
  2284. rung->condition.reset();
  2285. break;
  2286. }
  2287. removeExpressionRecursive(&*rung->condition, expression_id);
  2288. }
  2289. normalizeConditionExpression(&rung->condition);
  2290. recordHistory(std::move(before));
  2291. return {true, LogicEditorError::None, {}, expression_ids.front()};
  2292. }
  2293. bool LogicEditorService::canUndo() const
  2294. {
  2295. return history_.canUndo();
  2296. }
  2297. bool LogicEditorService::canRedo() const
  2298. {
  2299. return history_.canRedo();
  2300. }
  2301. LogicEditorResult LogicEditorService::undo()
  2302. {
  2303. const std::optional<HistoryState> target = history_.undo(captureState());
  2304. if (!target.has_value())
  2305. {
  2306. return historyFailure("没有可撤销的梯形图编辑操作");
  2307. }
  2308. project_service_.editProject().controlLogics = target->logics;
  2309. return {true, LogicEditorError::None, {}, {}};
  2310. }
  2311. LogicEditorResult LogicEditorService::redo()
  2312. {
  2313. const std::optional<HistoryState> target = history_.redo(captureState());
  2314. if (!target.has_value())
  2315. {
  2316. return historyFailure("没有可重做的梯形图编辑操作");
  2317. }
  2318. project_service_.editProject().controlLogics = target->logics;
  2319. return {true, LogicEditorError::None, {}, {}};
  2320. }
  2321. void LogicEditorService::clearHistory()
  2322. {
  2323. history_.clear();
  2324. }
  2325. bool LogicEditorService::isConditionConfig(const LogicNodeConfig &config)
  2326. {
  2327. return std::holds_alternative<ContactNodeConfig>(config)
  2328. || std::holds_alternative<EdgeContactNodeConfig>(config)
  2329. || std::holds_alternative<TimerContactNodeConfig>(config)
  2330. || std::holds_alternative<CounterContactNodeConfig>(config)
  2331. || std::holds_alternative<CompareNodeConfig>(config);
  2332. }
  2333. bool LogicEditorService::isOutputConfig(const LogicNodeConfig &config)
  2334. {
  2335. return std::holds_alternative<CoilNodeConfig>(config)
  2336. || std::holds_alternative<TonNodeConfig>(config)
  2337. || std::holds_alternative<CounterNodeConfig>(config)
  2338. || std::holds_alternative<MoveNodeConfig>(config)
  2339. || std::holds_alternative<ArithmeticNodeConfig>(config);
  2340. }
  2341. std::string LogicEditorService::nodePrefix(const LogicNodeConfig &config)
  2342. {
  2343. return std::visit(
  2344. [](const auto &value) -> std::string
  2345. {
  2346. using Config = std::decay_t<decltype(value)>;
  2347. if constexpr (std::is_same_v<Config, ContactNodeConfig>)
  2348. {
  2349. return "contact";
  2350. }
  2351. else if constexpr (std::is_same_v<Config, EdgeContactNodeConfig>)
  2352. {
  2353. return "edge";
  2354. }
  2355. else if constexpr (std::is_same_v<Config, TimerContactNodeConfig>)
  2356. {
  2357. return "timer-contact";
  2358. }
  2359. else if constexpr (std::is_same_v<Config, CounterContactNodeConfig>)
  2360. {
  2361. return "counter-contact";
  2362. }
  2363. else if constexpr (std::is_same_v<Config, CoilNodeConfig>)
  2364. {
  2365. return "coil";
  2366. }
  2367. else if constexpr (std::is_same_v<Config, TonNodeConfig>)
  2368. {
  2369. return "ton";
  2370. }
  2371. else if constexpr (std::is_same_v<Config, CounterNodeConfig>)
  2372. {
  2373. return "counter";
  2374. }
  2375. else if constexpr (std::is_same_v<Config, MoveNodeConfig>)
  2376. {
  2377. return "move";
  2378. }
  2379. else if constexpr (std::is_same_v<Config, ArithmeticNodeConfig>)
  2380. {
  2381. return "arithmetic";
  2382. }
  2383. else
  2384. {
  2385. return "compare";
  2386. }
  2387. },
  2388. config);
  2389. }
  2390. std::string LogicEditorService::makeUniqueNodeId(
  2391. const ControlLogic &logic, const std::string &prefix)
  2392. {
  2393. for (std::size_t index = 1;; ++index)
  2394. {
  2395. const std::string candidate = prefix + '-' + std::to_string(index);
  2396. bool found = false;
  2397. for (const LadderRung &rung : logic.rungs)
  2398. {
  2399. found = found || (rung.output.has_value() && rung.output->id == candidate)
  2400. || (rung.condition.has_value()
  2401. && findConditionNode(*rung.condition, candidate) != nullptr);
  2402. }
  2403. if (!found)
  2404. {
  2405. return candidate;
  2406. }
  2407. }
  2408. }
  2409. std::string LogicEditorService::makeUniqueWireId(const ControlLogic &logic)
  2410. {
  2411. for (std::size_t index = 1;; ++index)
  2412. {
  2413. const std::string candidate = "wire-" + std::to_string(index);
  2414. const bool found = std::any_of(
  2415. logic.rungs.cbegin(), logic.rungs.cend(),
  2416. [&candidate](const LadderRung &rung)
  2417. {
  2418. return rung.condition.has_value()
  2419. && findConditionExpression(*rung.condition, candidate) != nullptr;
  2420. });
  2421. if (!found)
  2422. {
  2423. return candidate;
  2424. }
  2425. }
  2426. }
  2427. std::string LogicEditorService::makeUniqueExpressionId(const ControlLogic &logic)
  2428. {
  2429. for (std::size_t index = 1;; ++index)
  2430. {
  2431. const std::string candidate = "expression-" + std::to_string(index);
  2432. bool found = false;
  2433. for (const LadderRung &rung : logic.rungs)
  2434. {
  2435. found = found || (rung.condition.has_value()
  2436. && findConditionExpression(*rung.condition, candidate) != nullptr);
  2437. }
  2438. if (!found)
  2439. {
  2440. return candidate;
  2441. }
  2442. }
  2443. }
  2444. std::string LogicEditorService::makeUniqueRungId(const ControlLogic &logic)
  2445. {
  2446. for (std::size_t index = 1;; ++index)
  2447. {
  2448. const std::string candidate = "rung-" + std::to_string(index);
  2449. if (std::none_of(
  2450. logic.rungs.cbegin(), logic.rungs.cend(),
  2451. [&candidate](const LadderRung &rung) { return rung.id == candidate; }))
  2452. {
  2453. return candidate;
  2454. }
  2455. }
  2456. }
  2457. LogicEditorResult LogicEditorService::failure(
  2458. LogicEditorError error, const std::string &message)
  2459. {
  2460. return {false, error, message, {}};
  2461. }