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

591 rivejä
20 KiB

  1. #include "logic_command_service.h"
  2. #include "logic_editor_service.h"
  3. #include <algorithm>
  4. #include <cctype>
  5. #include <limits>
  6. #include <sstream>
  7. #include <unordered_map>
  8. namespace {
  9. std::vector<std::string> splitTokens(const std::string &text)
  10. {
  11. std::istringstream stream(text);
  12. std::vector<std::string> tokens;
  13. std::string token;
  14. while (stream >> token)
  15. {
  16. tokens.push_back(std::move(token));
  17. }
  18. return tokens;
  19. }
  20. std::string upperAscii(std::string value)
  21. {
  22. std::transform(
  23. value.begin(), value.end(), value.begin(),
  24. [](unsigned char character)
  25. {
  26. return static_cast<char>(std::toupper(character));
  27. });
  28. return value;
  29. }
  30. LogicCommandParseResult parseFailure(const std::string &message)
  31. {
  32. return {false, message, {}};
  33. }
  34. bool requiresOperands(
  35. const std::vector<std::string> &tokens,
  36. std::size_t count,
  37. const std::string &example,
  38. LogicCommandParseResult *failure)
  39. {
  40. if (tokens.size() == count + 1U)
  41. {
  42. return true;
  43. }
  44. *failure = parseFailure(
  45. tokens.front() + " 需要 " + std::to_string(count)
  46. + " 个操作数,例如 " + example);
  47. return false;
  48. }
  49. bool parseAddress(
  50. const std::string &text,
  51. RegisterArea expected_area,
  52. const std::string &instruction,
  53. RegisterAddress *address,
  54. LogicCommandParseResult *failure)
  55. {
  56. const RegisterAddressParseResult parsed = parseRegisterAddress(text);
  57. const char expected_prefix = expected_area == RegisterArea::M ? 'M' : 'D';
  58. if (!parsed.succeeded)
  59. {
  60. const std::string range = std::string(1, expected_prefix) + "0~"
  61. + expected_prefix + std::to_string(RegisterAddress::kMaximumIndex);
  62. *failure = parseFailure(
  63. parsed.error == RegisterAddressParseError::OutOfRange
  64. ? std::string(1, expected_prefix) + " 地址超出范围,应为 " + range
  65. : "地址格式错误,应为 " + range);
  66. return false;
  67. }
  68. if (parsed.address.area() != expected_area)
  69. {
  70. *failure = parseFailure(
  71. instruction + " 只支持 " + std::string(1, expected_prefix) + " 地址");
  72. return false;
  73. }
  74. *address = parsed.address;
  75. return true;
  76. }
  77. bool parseInt16(
  78. const std::string &text,
  79. const std::string &instruction,
  80. std::int16_t *value,
  81. LogicCommandParseResult *failure);
  82. bool parseWordOperand(
  83. const std::string &text,
  84. const std::string &instruction,
  85. WordOperand *operand,
  86. LogicCommandParseResult *failure)
  87. {
  88. const RegisterAddressParseResult parsed = parseRegisterAddress(text);
  89. if (parsed.succeeded)
  90. {
  91. if (parsed.address.area() != RegisterArea::D)
  92. {
  93. *failure = parseFailure(instruction + " 的寄存器操作数必须使用 D 地址");
  94. return false;
  95. }
  96. *operand = WordOperand{
  97. WordOperandKind::Register, parsed.address, 0};
  98. return true;
  99. }
  100. if (!text.empty()
  101. && (text.front() == 'D' || text.front() == 'd'
  102. || text.front() == 'M' || text.front() == 'm'))
  103. {
  104. RegisterAddress address{RegisterArea::D, 0};
  105. return parseAddress(
  106. text, RegisterArea::D, instruction, &address, failure)
  107. && ((*operand = WordOperand{
  108. WordOperandKind::Register, address, 0}), true);
  109. }
  110. std::int16_t constant = 0;
  111. if (!parseInt16(text, instruction, &constant, failure))
  112. {
  113. return false;
  114. }
  115. *operand = WordOperand{
  116. WordOperandKind::Constant,
  117. RegisterAddress{RegisterArea::D, 0},
  118. constant};
  119. return true;
  120. }
  121. bool isLoad(LogicCommandOpcode opcode)
  122. {
  123. return opcode == LogicCommandOpcode::Load
  124. || opcode == LogicCommandOpcode::LoadInverse
  125. || opcode == LogicCommandOpcode::LoadRising
  126. || opcode == LogicCommandOpcode::LoadFalling
  127. || opcode == LogicCommandOpcode::CompareEqual
  128. || opcode == LogicCommandOpcode::CompareNotEqual
  129. || opcode == LogicCommandOpcode::CompareLessThan
  130. || opcode == LogicCommandOpcode::CompareLessThanOrEqual
  131. || opcode == LogicCommandOpcode::CompareGreaterThan
  132. || opcode == LogicCommandOpcode::CompareGreaterThanOrEqual;
  133. }
  134. bool isOr(LogicCommandOpcode opcode)
  135. {
  136. return opcode == LogicCommandOpcode::Or
  137. || opcode == LogicCommandOpcode::OrInverse;
  138. }
  139. bool isCondition(LogicCommandOpcode opcode)
  140. {
  141. return isLoad(opcode) || isOr(opcode)
  142. || opcode == LogicCommandOpcode::And
  143. || opcode == LogicCommandOpcode::AndInverse;
  144. }
  145. bool isConditionTarget(LogicCommandTargetKind kind)
  146. {
  147. return kind != LogicCommandTargetKind::Output;
  148. }
  149. bool isOutput(LogicCommandOpcode opcode)
  150. {
  151. return opcode == LogicCommandOpcode::Output
  152. || opcode == LogicCommandOpcode::Set
  153. || opcode == LogicCommandOpcode::Reset
  154. || opcode == LogicCommandOpcode::Move
  155. || opcode == LogicCommandOpcode::Add
  156. || opcode == LogicCommandOpcode::Subtract;
  157. }
  158. bool parseInt16(
  159. const std::string &text,
  160. const std::string &instruction,
  161. std::int16_t *value,
  162. LogicCommandParseResult *failure)
  163. {
  164. try
  165. {
  166. std::size_t consumed = 0;
  167. const long parsed = std::stol(text, &consumed, 10);
  168. if (consumed != text.size()
  169. || parsed < std::numeric_limits<std::int16_t>::min()
  170. || parsed > std::numeric_limits<std::int16_t>::max())
  171. {
  172. throw std::out_of_range("int16");
  173. }
  174. *value = static_cast<std::int16_t>(parsed);
  175. return true;
  176. }
  177. catch (const std::exception &)
  178. {
  179. *failure = parseFailure(
  180. instruction + " 的第二个操作数应为 -32768~32767 的常量");
  181. return false;
  182. }
  183. }
  184. LogicCommandResult executionFailure(
  185. const std::string &message, LogicCommandOpcode opcode)
  186. {
  187. return {false, message, {}, {}, opcode};
  188. }
  189. } // namespace
  190. LogicCommandService::LogicCommandService(LogicEditorService &editor_service)
  191. : editor_service_(editor_service)
  192. {
  193. }
  194. LogicCommandParseResult LogicCommandService::parse(const std::string &text)
  195. {
  196. std::vector<std::string> tokens = splitTokens(text);
  197. if (tokens.empty())
  198. {
  199. return parseFailure("请输入 PLC 指令");
  200. }
  201. tokens.front() = upperAscii(tokens.front());
  202. const std::string &mnemonic = tokens.front();
  203. LogicCommandParseResult failure;
  204. const std::unordered_map<std::string, LogicCommandOpcode> condition_opcodes{
  205. {"LD", LogicCommandOpcode::Load},
  206. {"LDI", LogicCommandOpcode::LoadInverse},
  207. {"LDP", LogicCommandOpcode::LoadRising},
  208. {"LDF", LogicCommandOpcode::LoadFalling},
  209. {"AND", LogicCommandOpcode::And},
  210. {"ANI", LogicCommandOpcode::AndInverse},
  211. {"OR", LogicCommandOpcode::Or},
  212. {"ORI", LogicCommandOpcode::OrInverse}};
  213. const std::unordered_map<std::string, LogicCommandOpcode> compare_opcodes{
  214. {"LD=", LogicCommandOpcode::CompareEqual},
  215. {"LD<>", LogicCommandOpcode::CompareNotEqual},
  216. {"LD<", LogicCommandOpcode::CompareLessThan},
  217. {"LD<=", LogicCommandOpcode::CompareLessThanOrEqual},
  218. {"LD>", LogicCommandOpcode::CompareGreaterThan},
  219. {"LD>=", LogicCommandOpcode::CompareGreaterThanOrEqual}};
  220. const auto compare = compare_opcodes.find(mnemonic);
  221. if (compare != compare_opcodes.end())
  222. {
  223. if (!requiresOperands(tokens, 2U, mnemonic + " D0 0", &failure))
  224. {
  225. return failure;
  226. }
  227. RegisterAddress address{RegisterArea::D, 0};
  228. if (!parseAddress(tokens[1], RegisterArea::D, mnemonic, &address, &failure))
  229. {
  230. return failure;
  231. }
  232. std::int16_t value = 0;
  233. if (!parseInt16(tokens[2], mnemonic, &value, &failure))
  234. {
  235. return failure;
  236. }
  237. ComparisonOperator operation = ComparisonOperator::Equal;
  238. switch (compare->second)
  239. {
  240. case LogicCommandOpcode::CompareEqual:
  241. operation = ComparisonOperator::Equal;
  242. break;
  243. case LogicCommandOpcode::CompareNotEqual:
  244. operation = ComparisonOperator::NotEqual;
  245. break;
  246. case LogicCommandOpcode::CompareLessThan:
  247. operation = ComparisonOperator::LessThan;
  248. break;
  249. case LogicCommandOpcode::CompareLessThanOrEqual:
  250. operation = ComparisonOperator::LessThanOrEqual;
  251. break;
  252. case LogicCommandOpcode::CompareGreaterThan:
  253. operation = ComparisonOperator::GreaterThan;
  254. break;
  255. case LogicCommandOpcode::CompareGreaterThanOrEqual:
  256. operation = ComparisonOperator::GreaterThanOrEqual;
  257. break;
  258. default:
  259. break;
  260. }
  261. return {true, {}, {compare->second,
  262. CompareNodeConfig{address, operation, value}}};
  263. }
  264. const auto condition = condition_opcodes.find(mnemonic);
  265. if (condition != condition_opcodes.end())
  266. {
  267. if (!requiresOperands(tokens, 1U, mnemonic + " M0", &failure))
  268. {
  269. return failure;
  270. }
  271. RegisterAddress address{RegisterArea::M, 0};
  272. if (!parseAddress(tokens[1], RegisterArea::M, mnemonic, &address, &failure))
  273. {
  274. return failure;
  275. }
  276. LogicNodeConfig config;
  277. if (condition->second == LogicCommandOpcode::LoadRising
  278. || condition->second == LogicCommandOpcode::LoadFalling)
  279. {
  280. config = EdgeContactNodeConfig{
  281. address,
  282. condition->second == LogicCommandOpcode::LoadRising
  283. ? EdgeMode::Rising : EdgeMode::Falling};
  284. }
  285. else
  286. {
  287. const bool inverse = condition->second == LogicCommandOpcode::LoadInverse
  288. || condition->second == LogicCommandOpcode::AndInverse
  289. || condition->second == LogicCommandOpcode::OrInverse;
  290. config = ContactNodeConfig{
  291. address,
  292. inverse ? ContactMode::NormallyClosed
  293. : ContactMode::NormallyOpen};
  294. }
  295. return {true, {}, {condition->second, std::move(config)}};
  296. }
  297. const std::unordered_map<std::string, LogicCommandOpcode> coil_opcodes{
  298. {"OUT", LogicCommandOpcode::Output},
  299. {"SET", LogicCommandOpcode::Set},
  300. {"RST", LogicCommandOpcode::Reset}};
  301. const auto coil = coil_opcodes.find(mnemonic);
  302. if (coil != coil_opcodes.end())
  303. {
  304. if (!requiresOperands(tokens, 1U, mnemonic + " M0", &failure))
  305. {
  306. return failure;
  307. }
  308. RegisterAddress address{RegisterArea::M, 0};
  309. if (!parseAddress(tokens[1], RegisterArea::M, mnemonic, &address, &failure))
  310. {
  311. return failure;
  312. }
  313. const CoilMode mode = coil->second == LogicCommandOpcode::Set
  314. ? CoilMode::Set
  315. : coil->second == LogicCommandOpcode::Reset
  316. ? CoilMode::Reset : CoilMode::Normal;
  317. return {true, {}, {coil->second, CoilNodeConfig{address, mode}}};
  318. }
  319. if (mnemonic == "MOV")
  320. {
  321. if (!requiresOperands(tokens, 2U, "MOV D0 D1", &failure))
  322. {
  323. return failure;
  324. }
  325. RegisterAddress destination{RegisterArea::D, 0};
  326. WordOperand source;
  327. if (!parseWordOperand(tokens[1], mnemonic, &source, &failure)
  328. || !parseAddress(tokens[2], RegisterArea::D, mnemonic, &destination, &failure))
  329. {
  330. return failure;
  331. }
  332. return {
  333. true, {},
  334. {LogicCommandOpcode::Move,
  335. MoveNodeConfig{source, destination}}};
  336. }
  337. if (mnemonic == "ADD" || mnemonic == "SUB")
  338. {
  339. if (!requiresOperands(tokens, 3U, mnemonic + " D0 D1 D2", &failure))
  340. {
  341. return failure;
  342. }
  343. WordOperand left;
  344. WordOperand right;
  345. RegisterAddress destination{RegisterArea::D, 0};
  346. if (!parseWordOperand(tokens[1], mnemonic, &left, &failure)
  347. || !parseWordOperand(tokens[2], mnemonic, &right, &failure)
  348. || !parseAddress(tokens[3], RegisterArea::D, mnemonic, &destination, &failure))
  349. {
  350. return failure;
  351. }
  352. const LogicCommandOpcode opcode = mnemonic == "ADD"
  353. ? LogicCommandOpcode::Add : LogicCommandOpcode::Subtract;
  354. return {
  355. true, {},
  356. {opcode,
  357. ArithmeticNodeConfig{
  358. opcode == LogicCommandOpcode::Add
  359. ? ArithmeticOperation::Add : ArithmeticOperation::Subtract,
  360. left,
  361. right,
  362. destination}}};
  363. }
  364. return parseFailure("当前指令暂不支持:" + mnemonic);
  365. }
  366. const std::vector<LogicCommandSuggestion> &LogicCommandService::suggestions()
  367. {
  368. static const std::vector<LogicCommandSuggestion> values{
  369. {"LD", "常开触点", "M 地址"},
  370. {"LDI", "常闭触点", "M 地址"},
  371. {"LDP", "上升沿触点", "M 地址"},
  372. {"LDF", "下降沿触点", "M 地址"},
  373. {"LD=", "D 值等于常量", "D 地址 + 常量"},
  374. {"LD<>", "D 值不等于常量", "D 地址 + 常量"},
  375. {"LD<", "D 值小于常量", "D 地址 + 常量"},
  376. {"LD<=", "D 值小于等于常量", "D 地址 + 常量"},
  377. {"LD>", "D 值大于常量", "D 地址 + 常量"},
  378. {"LD>=", "D 值大于等于常量", "D 地址 + 常量"},
  379. {"AND", "串联常开触点", "M 地址"},
  380. {"ANI", "串联常闭触点", "M 地址"},
  381. {"OR", "并联常开触点", "M 地址"},
  382. {"ORI", "并联常闭触点", "M 地址"},
  383. {"OUT", "普通线圈", "M 地址"},
  384. {"SET", "置位线圈", "M 地址"},
  385. {"RST", "复位线圈", "M 地址"},
  386. {"MOV", "数据传送", "D 地址 -> D 地址"},
  387. {"ADD", "加法", "D 地址 + D 地址 -> D 地址"},
  388. {"SUB", "减法", "D 地址 - D 地址 -> D 地址"},
  389. {"TON", "暂不支持", "T 地址", false},
  390. {"CTU", "暂不支持", "C 地址", false}};
  391. return values;
  392. }
  393. LogicCommandResult LogicCommandService::execute(
  394. const LogicCommandRequest &request)
  395. {
  396. const LogicCommandParseResult parsed = parse(request.text);
  397. if (!parsed.succeeded)
  398. {
  399. return executionFailure(parsed.message, parsed.command.opcode);
  400. }
  401. const LogicCommandOpcode opcode = parsed.command.opcode;
  402. LogicEditorResult edited;
  403. if (request.target.kind == LogicCommandTargetKind::ExistingNode)
  404. {
  405. const LogicNode *existing = editor_service_.findNode(
  406. request.logicId, request.target.expressionId);
  407. if (existing == nullptr)
  408. {
  409. return executionFailure("未找到要编辑的逻辑节点", opcode);
  410. }
  411. if (existing->isCondition())
  412. {
  413. if (!isLoad(opcode))
  414. {
  415. return executionFailure(
  416. "已有条件节点只能替换为 LD/LDI/LDP/LDF 指令", opcode);
  417. }
  418. }
  419. else if (existing->isOutput())
  420. {
  421. if (!isOutput(opcode))
  422. {
  423. return executionFailure(
  424. "已有输出节点只能替换为 OUT/SET/RST/MOV/ADD/SUB 指令",
  425. opcode);
  426. }
  427. }
  428. else
  429. {
  430. return executionFailure("当前节点类型不支持命令替换", opcode);
  431. }
  432. edited = editor_service_.updateNodeConfig(
  433. request.logicId, request.target.expressionId,
  434. parsed.command.config);
  435. }
  436. else if (isCondition(opcode))
  437. {
  438. if (isLoad(opcode) && request.continuing)
  439. {
  440. edited = editor_service_.appendCondition(
  441. request.logicId, {}, parsed.command.config, true);
  442. }
  443. else if (isOr(opcode))
  444. {
  445. const std::string rung_id = request.continuing
  446. ? request.currentRungId : request.target.rungId;
  447. if (request.continuing)
  448. {
  449. edited = editor_service_.addParallelToWholeCondition(
  450. request.logicId, rung_id, parsed.command.config, true);
  451. }
  452. else if (request.parallelNodeIds.empty())
  453. {
  454. return executionFailure(
  455. "OR/ORI 需要先选中同一网络中的连续条件", opcode);
  456. }
  457. else
  458. {
  459. edited = editor_service_.addParallelBranch(
  460. request.logicId,
  461. rung_id,
  462. request.parallelNodeIds,
  463. parsed.command.config,
  464. true);
  465. }
  466. }
  467. else if (request.continuing)
  468. {
  469. const LadderRung *rung = editor_service_.findRung(
  470. request.logicId, request.currentRungId);
  471. if (rung != nullptr && rung->output.has_value())
  472. {
  473. return executionFailure(
  474. "当前网络已有输出,下一条只能输入 LD/LDI/LDP/LDF 新建网络",
  475. opcode);
  476. }
  477. edited = editor_service_.appendCondition(
  478. request.logicId, request.currentRungId,
  479. parsed.command.config, true);
  480. }
  481. else
  482. {
  483. if (!isConditionTarget(request.target.kind))
  484. {
  485. return executionFailure(
  486. "条件指令只能输入在第 1~10 列条件区", opcode);
  487. }
  488. if ((opcode == LogicCommandOpcode::And
  489. || opcode == LogicCommandOpcode::AndInverse)
  490. && (!request.target.rungId.empty()
  491. && (editor_service_.findRung(
  492. request.logicId, request.target.rungId) == nullptr
  493. || !editor_service_.findRung(
  494. request.logicId, request.target.rungId)
  495. ->condition.has_value())))
  496. {
  497. return executionFailure(
  498. "AND/ANI 前必须先输入 LD/LDI/LDP/LDF", opcode);
  499. }
  500. switch (request.target.kind)
  501. {
  502. case LogicCommandTargetKind::EmptyColumn:
  503. edited = editor_service_.insertConditionAtColumn(
  504. request.logicId, request.target.rungId,
  505. request.target.column, parsed.command.config, true);
  506. break;
  507. case LogicCommandTargetKind::BranchEmptyColumn:
  508. edited = editor_service_.insertConditionInBranchAtColumn(
  509. request.logicId, request.target.rungId,
  510. request.target.expressionId, request.target.column,
  511. parsed.command.config, true);
  512. break;
  513. case LogicCommandTargetKind::WireColumn:
  514. edited = editor_service_.replaceWireColumnWithCondition(
  515. request.logicId, request.target.rungId,
  516. request.target.expressionId, request.target.column,
  517. parsed.command.config, true);
  518. break;
  519. case LogicCommandTargetKind::GapColumn:
  520. edited = editor_service_.replaceGapColumnWithCondition(
  521. request.logicId, request.target.rungId,
  522. request.target.expressionId, request.target.column,
  523. parsed.command.config, true);
  524. break;
  525. case LogicCommandTargetKind::Output:
  526. break;
  527. case LogicCommandTargetKind::ExistingNode:
  528. break;
  529. }
  530. }
  531. }
  532. else
  533. {
  534. const std::string rung_id = request.continuing
  535. ? request.currentRungId : request.target.rungId;
  536. if (!request.continuing
  537. && request.target.kind != LogicCommandTargetKind::Output)
  538. {
  539. return executionFailure(
  540. "输出指令只能输入在第 11 列输出区", opcode);
  541. }
  542. const LadderRung *rung = editor_service_.findRung(
  543. request.logicId, rung_id);
  544. if (request.continuing && rung != nullptr && rung->output.has_value())
  545. {
  546. return executionFailure(
  547. "当前网络已经有输出,下一条请输入 LD/LDI/LDP/LDF 新建网络",
  548. opcode);
  549. }
  550. edited = editor_service_.setOutput(
  551. request.logicId, rung_id, parsed.command.config, true);
  552. }
  553. if (!edited.succeeded)
  554. {
  555. return executionFailure(edited.message, opcode);
  556. }
  557. return {
  558. true, {}, edited.id,
  559. editor_service_.rungIdForNode(request.logicId, edited.id),
  560. opcode};
  561. }