|
- #include "logic_command_service.h"
-
- #include "logic_editor_service.h"
-
- #include <algorithm>
- #include <cctype>
- #include <limits>
- #include <sstream>
- #include <unordered_map>
-
- namespace {
-
- std::vector<std::string> splitTokens(const std::string &text)
- {
- std::istringstream stream(text);
- std::vector<std::string> tokens;
- std::string token;
- while (stream >> token)
- {
- tokens.push_back(std::move(token));
- }
- return tokens;
- }
-
- std::string upperAscii(std::string value)
- {
- std::transform(
- value.begin(), value.end(), value.begin(),
- [](unsigned char character)
- {
- return static_cast<char>(std::toupper(character));
- });
- return value;
- }
-
- LogicCommandParseResult parseFailure(const std::string &message)
- {
- return {false, message, {}};
- }
-
- bool requiresOperands(
- const std::vector<std::string> &tokens,
- std::size_t count,
- const std::string &example,
- LogicCommandParseResult *failure)
- {
- if (tokens.size() == count + 1U)
- {
- return true;
- }
- *failure = parseFailure(
- tokens.front() + " 需要 " + std::to_string(count)
- + " 个操作数,例如 " + example);
- return false;
- }
-
- bool parseAddress(
- const std::string &text,
- RegisterArea expected_area,
- const std::string &instruction,
- RegisterAddress *address,
- LogicCommandParseResult *failure)
- {
- const RegisterAddressParseResult parsed = parseRegisterAddress(text);
- const char expected_prefix = expected_area == RegisterArea::M ? 'M' : 'D';
- if (!parsed.succeeded)
- {
- const std::string range = std::string(1, expected_prefix) + "0~"
- + expected_prefix + std::to_string(RegisterAddress::kMaximumIndex);
- *failure = parseFailure(
- parsed.error == RegisterAddressParseError::OutOfRange
- ? std::string(1, expected_prefix) + " 地址超出范围,应为 " + range
- : "地址格式错误,应为 " + range);
- return false;
- }
- if (parsed.address.area() != expected_area)
- {
- *failure = parseFailure(
- instruction + " 只支持 " + std::string(1, expected_prefix) + " 地址");
- return false;
- }
- *address = parsed.address;
- return true;
- }
-
- bool parseInt16(
- const std::string &text,
- const std::string &instruction,
- std::int16_t *value,
- LogicCommandParseResult *failure);
-
- bool parseWordOperand(
- const std::string &text,
- const std::string &instruction,
- WordOperand *operand,
- LogicCommandParseResult *failure)
- {
- const RegisterAddressParseResult parsed = parseRegisterAddress(text);
- if (parsed.succeeded)
- {
- if (parsed.address.area() != RegisterArea::D)
- {
- *failure = parseFailure(instruction + " 的寄存器操作数必须使用 D 地址");
- return false;
- }
- *operand = WordOperand{
- WordOperandKind::Register, parsed.address, 0};
- return true;
- }
- if (!text.empty()
- && (text.front() == 'D' || text.front() == 'd'
- || text.front() == 'M' || text.front() == 'm'))
- {
- RegisterAddress address{RegisterArea::D, 0};
- return parseAddress(
- text, RegisterArea::D, instruction, &address, failure)
- && ((*operand = WordOperand{
- WordOperandKind::Register, address, 0}), true);
- }
- std::int16_t constant = 0;
- if (!parseInt16(text, instruction, &constant, failure))
- {
- return false;
- }
- *operand = WordOperand{
- WordOperandKind::Constant,
- RegisterAddress{RegisterArea::D, 0},
- constant};
- return true;
- }
-
- bool isLoad(LogicCommandOpcode opcode)
- {
- return opcode == LogicCommandOpcode::Load
- || opcode == LogicCommandOpcode::LoadInverse
- || opcode == LogicCommandOpcode::LoadRising
- || opcode == LogicCommandOpcode::LoadFalling
- || opcode == LogicCommandOpcode::CompareEqual
- || opcode == LogicCommandOpcode::CompareNotEqual
- || opcode == LogicCommandOpcode::CompareLessThan
- || opcode == LogicCommandOpcode::CompareLessThanOrEqual
- || opcode == LogicCommandOpcode::CompareGreaterThan
- || opcode == LogicCommandOpcode::CompareGreaterThanOrEqual;
- }
-
- bool isOr(LogicCommandOpcode opcode)
- {
- return opcode == LogicCommandOpcode::Or
- || opcode == LogicCommandOpcode::OrInverse;
- }
-
- bool isCondition(LogicCommandOpcode opcode)
- {
- return isLoad(opcode) || isOr(opcode)
- || opcode == LogicCommandOpcode::And
- || opcode == LogicCommandOpcode::AndInverse;
- }
-
- bool isConditionTarget(LogicCommandTargetKind kind)
- {
- return kind != LogicCommandTargetKind::Output;
- }
-
- bool isOutput(LogicCommandOpcode opcode)
- {
- return opcode == LogicCommandOpcode::Output
- || opcode == LogicCommandOpcode::Set
- || opcode == LogicCommandOpcode::Reset
- || opcode == LogicCommandOpcode::Move
- || opcode == LogicCommandOpcode::Add
- || opcode == LogicCommandOpcode::Subtract;
- }
-
- bool parseInt16(
- const std::string &text,
- const std::string &instruction,
- std::int16_t *value,
- LogicCommandParseResult *failure)
- {
- try
- {
- std::size_t consumed = 0;
- const long parsed = std::stol(text, &consumed, 10);
- if (consumed != text.size()
- || parsed < std::numeric_limits<std::int16_t>::min()
- || parsed > std::numeric_limits<std::int16_t>::max())
- {
- throw std::out_of_range("int16");
- }
- *value = static_cast<std::int16_t>(parsed);
- return true;
- }
- catch (const std::exception &)
- {
- *failure = parseFailure(
- instruction + " 的第二个操作数应为 -32768~32767 的常量");
- return false;
- }
- }
-
- LogicCommandResult executionFailure(
- const std::string &message, LogicCommandOpcode opcode)
- {
- return {false, message, {}, {}, opcode};
- }
-
- } // namespace
-
- LogicCommandService::LogicCommandService(LogicEditorService &editor_service)
- : editor_service_(editor_service)
- {
- }
-
- LogicCommandParseResult LogicCommandService::parse(const std::string &text)
- {
- std::vector<std::string> tokens = splitTokens(text);
- if (tokens.empty())
- {
- return parseFailure("请输入 PLC 指令");
- }
- tokens.front() = upperAscii(tokens.front());
- const std::string &mnemonic = tokens.front();
- LogicCommandParseResult failure;
-
- const std::unordered_map<std::string, LogicCommandOpcode> condition_opcodes{
- {"LD", LogicCommandOpcode::Load},
- {"LDI", LogicCommandOpcode::LoadInverse},
- {"LDP", LogicCommandOpcode::LoadRising},
- {"LDF", LogicCommandOpcode::LoadFalling},
- {"AND", LogicCommandOpcode::And},
- {"ANI", LogicCommandOpcode::AndInverse},
- {"OR", LogicCommandOpcode::Or},
- {"ORI", LogicCommandOpcode::OrInverse}};
- const std::unordered_map<std::string, LogicCommandOpcode> compare_opcodes{
- {"LD=", LogicCommandOpcode::CompareEqual},
- {"LD<>", LogicCommandOpcode::CompareNotEqual},
- {"LD<", LogicCommandOpcode::CompareLessThan},
- {"LD<=", LogicCommandOpcode::CompareLessThanOrEqual},
- {"LD>", LogicCommandOpcode::CompareGreaterThan},
- {"LD>=", LogicCommandOpcode::CompareGreaterThanOrEqual}};
- const auto compare = compare_opcodes.find(mnemonic);
- if (compare != compare_opcodes.end())
- {
- if (!requiresOperands(tokens, 2U, mnemonic + " D0 0", &failure))
- {
- return failure;
- }
- RegisterAddress address{RegisterArea::D, 0};
- if (!parseAddress(tokens[1], RegisterArea::D, mnemonic, &address, &failure))
- {
- return failure;
- }
- std::int16_t value = 0;
- if (!parseInt16(tokens[2], mnemonic, &value, &failure))
- {
- return failure;
- }
- ComparisonOperator operation = ComparisonOperator::Equal;
- switch (compare->second)
- {
- case LogicCommandOpcode::CompareEqual:
- operation = ComparisonOperator::Equal;
- break;
- case LogicCommandOpcode::CompareNotEqual:
- operation = ComparisonOperator::NotEqual;
- break;
- case LogicCommandOpcode::CompareLessThan:
- operation = ComparisonOperator::LessThan;
- break;
- case LogicCommandOpcode::CompareLessThanOrEqual:
- operation = ComparisonOperator::LessThanOrEqual;
- break;
- case LogicCommandOpcode::CompareGreaterThan:
- operation = ComparisonOperator::GreaterThan;
- break;
- case LogicCommandOpcode::CompareGreaterThanOrEqual:
- operation = ComparisonOperator::GreaterThanOrEqual;
- break;
- default:
- break;
- }
- return {true, {}, {compare->second,
- CompareNodeConfig{address, operation, value}}};
- }
- const auto condition = condition_opcodes.find(mnemonic);
- if (condition != condition_opcodes.end())
- {
- if (!requiresOperands(tokens, 1U, mnemonic + " M0", &failure))
- {
- return failure;
- }
- RegisterAddress address{RegisterArea::M, 0};
- if (!parseAddress(tokens[1], RegisterArea::M, mnemonic, &address, &failure))
- {
- return failure;
- }
- LogicNodeConfig config;
- if (condition->second == LogicCommandOpcode::LoadRising
- || condition->second == LogicCommandOpcode::LoadFalling)
- {
- config = EdgeContactNodeConfig{
- address,
- condition->second == LogicCommandOpcode::LoadRising
- ? EdgeMode::Rising : EdgeMode::Falling};
- }
- else
- {
- const bool inverse = condition->second == LogicCommandOpcode::LoadInverse
- || condition->second == LogicCommandOpcode::AndInverse
- || condition->second == LogicCommandOpcode::OrInverse;
- config = ContactNodeConfig{
- address,
- inverse ? ContactMode::NormallyClosed
- : ContactMode::NormallyOpen};
- }
- return {true, {}, {condition->second, std::move(config)}};
- }
-
- const std::unordered_map<std::string, LogicCommandOpcode> coil_opcodes{
- {"OUT", LogicCommandOpcode::Output},
- {"SET", LogicCommandOpcode::Set},
- {"RST", LogicCommandOpcode::Reset}};
- const auto coil = coil_opcodes.find(mnemonic);
- if (coil != coil_opcodes.end())
- {
- if (!requiresOperands(tokens, 1U, mnemonic + " M0", &failure))
- {
- return failure;
- }
- RegisterAddress address{RegisterArea::M, 0};
- if (!parseAddress(tokens[1], RegisterArea::M, mnemonic, &address, &failure))
- {
- return failure;
- }
- const CoilMode mode = coil->second == LogicCommandOpcode::Set
- ? CoilMode::Set
- : coil->second == LogicCommandOpcode::Reset
- ? CoilMode::Reset : CoilMode::Normal;
- return {true, {}, {coil->second, CoilNodeConfig{address, mode}}};
- }
-
- if (mnemonic == "MOV")
- {
- if (!requiresOperands(tokens, 2U, "MOV D0 D1", &failure))
- {
- return failure;
- }
- RegisterAddress destination{RegisterArea::D, 0};
- WordOperand source;
- if (!parseWordOperand(tokens[1], mnemonic, &source, &failure)
- || !parseAddress(tokens[2], RegisterArea::D, mnemonic, &destination, &failure))
- {
- return failure;
- }
- return {
- true, {},
- {LogicCommandOpcode::Move,
- MoveNodeConfig{source, destination}}};
- }
-
- if (mnemonic == "ADD" || mnemonic == "SUB")
- {
- if (!requiresOperands(tokens, 3U, mnemonic + " D0 D1 D2", &failure))
- {
- return failure;
- }
- WordOperand left;
- WordOperand right;
- RegisterAddress destination{RegisterArea::D, 0};
- if (!parseWordOperand(tokens[1], mnemonic, &left, &failure)
- || !parseWordOperand(tokens[2], mnemonic, &right, &failure)
- || !parseAddress(tokens[3], RegisterArea::D, mnemonic, &destination, &failure))
- {
- return failure;
- }
- const LogicCommandOpcode opcode = mnemonic == "ADD"
- ? LogicCommandOpcode::Add : LogicCommandOpcode::Subtract;
- return {
- true, {},
- {opcode,
- ArithmeticNodeConfig{
- opcode == LogicCommandOpcode::Add
- ? ArithmeticOperation::Add : ArithmeticOperation::Subtract,
- left,
- right,
- destination}}};
- }
-
- return parseFailure("当前指令暂不支持:" + mnemonic);
- }
-
- const std::vector<LogicCommandSuggestion> &LogicCommandService::suggestions()
- {
- static const std::vector<LogicCommandSuggestion> values{
- {"LD", "常开触点", "M 地址"},
- {"LDI", "常闭触点", "M 地址"},
- {"LDP", "上升沿触点", "M 地址"},
- {"LDF", "下降沿触点", "M 地址"},
- {"LD=", "D 值等于常量", "D 地址 + 常量"},
- {"LD<>", "D 值不等于常量", "D 地址 + 常量"},
- {"LD<", "D 值小于常量", "D 地址 + 常量"},
- {"LD<=", "D 值小于等于常量", "D 地址 + 常量"},
- {"LD>", "D 值大于常量", "D 地址 + 常量"},
- {"LD>=", "D 值大于等于常量", "D 地址 + 常量"},
- {"AND", "串联常开触点", "M 地址"},
- {"ANI", "串联常闭触点", "M 地址"},
- {"OR", "并联常开触点", "M 地址"},
- {"ORI", "并联常闭触点", "M 地址"},
- {"OUT", "普通线圈", "M 地址"},
- {"SET", "置位线圈", "M 地址"},
- {"RST", "复位线圈", "M 地址"},
- {"MOV", "数据传送", "D 地址 -> D 地址"},
- {"ADD", "加法", "D 地址 + D 地址 -> D 地址"},
- {"SUB", "减法", "D 地址 - D 地址 -> D 地址"},
- {"TON", "暂不支持", "T 地址", false},
- {"CTU", "暂不支持", "C 地址", false}};
- return values;
- }
-
- LogicCommandResult LogicCommandService::execute(
- const LogicCommandRequest &request)
- {
- const LogicCommandParseResult parsed = parse(request.text);
- if (!parsed.succeeded)
- {
- return executionFailure(parsed.message, parsed.command.opcode);
- }
- const LogicCommandOpcode opcode = parsed.command.opcode;
- LogicEditorResult edited;
-
- if (request.target.kind == LogicCommandTargetKind::ExistingNode)
- {
- const LogicNode *existing = editor_service_.findNode(
- request.logicId, request.target.expressionId);
- if (existing == nullptr)
- {
- return executionFailure("未找到要编辑的逻辑节点", opcode);
- }
- if (existing->isCondition())
- {
- if (!isLoad(opcode))
- {
- return executionFailure(
- "已有条件节点只能替换为 LD/LDI/LDP/LDF 指令", opcode);
- }
- }
- else if (existing->isOutput())
- {
- if (!isOutput(opcode))
- {
- return executionFailure(
- "已有输出节点只能替换为 OUT/SET/RST/MOV/ADD/SUB 指令",
- opcode);
- }
- }
- else
- {
- return executionFailure("当前节点类型不支持命令替换", opcode);
- }
- edited = editor_service_.updateNodeConfig(
- request.logicId, request.target.expressionId,
- parsed.command.config);
- }
- else if (isCondition(opcode))
- {
- if (isLoad(opcode) && request.continuing)
- {
- edited = editor_service_.appendCondition(
- request.logicId, {}, parsed.command.config, true);
- }
- else if (isOr(opcode))
- {
- const std::string rung_id = request.continuing
- ? request.currentRungId : request.target.rungId;
- if (request.continuing)
- {
- edited = editor_service_.addParallelToWholeCondition(
- request.logicId, rung_id, parsed.command.config, true);
- }
- else if (request.parallelNodeIds.empty())
- {
- return executionFailure(
- "OR/ORI 需要先选中同一网络中的连续条件", opcode);
- }
- else
- {
- edited = editor_service_.addParallelBranch(
- request.logicId,
- rung_id,
- request.parallelNodeIds,
- parsed.command.config,
- true);
- }
- }
- else if (request.continuing)
- {
- const LadderRung *rung = editor_service_.findRung(
- request.logicId, request.currentRungId);
- if (rung != nullptr && rung->output.has_value())
- {
- return executionFailure(
- "当前网络已有输出,下一条只能输入 LD/LDI/LDP/LDF 新建网络",
- opcode);
- }
- edited = editor_service_.appendCondition(
- request.logicId, request.currentRungId,
- parsed.command.config, true);
- }
- else
- {
- if (!isConditionTarget(request.target.kind))
- {
- return executionFailure(
- "条件指令只能输入在第 1~10 列条件区", opcode);
- }
- if ((opcode == LogicCommandOpcode::And
- || opcode == LogicCommandOpcode::AndInverse)
- && (!request.target.rungId.empty()
- && (editor_service_.findRung(
- request.logicId, request.target.rungId) == nullptr
- || !editor_service_.findRung(
- request.logicId, request.target.rungId)
- ->condition.has_value())))
- {
- return executionFailure(
- "AND/ANI 前必须先输入 LD/LDI/LDP/LDF", opcode);
- }
- switch (request.target.kind)
- {
- case LogicCommandTargetKind::EmptyColumn:
- edited = editor_service_.insertConditionAtColumn(
- request.logicId, request.target.rungId,
- request.target.column, parsed.command.config, true);
- break;
- case LogicCommandTargetKind::BranchEmptyColumn:
- edited = editor_service_.insertConditionInBranchAtColumn(
- request.logicId, request.target.rungId,
- request.target.expressionId, request.target.column,
- parsed.command.config, true);
- break;
- case LogicCommandTargetKind::WireColumn:
- edited = editor_service_.replaceWireColumnWithCondition(
- request.logicId, request.target.rungId,
- request.target.expressionId, request.target.column,
- parsed.command.config, true);
- break;
- case LogicCommandTargetKind::GapColumn:
- edited = editor_service_.replaceGapColumnWithCondition(
- request.logicId, request.target.rungId,
- request.target.expressionId, request.target.column,
- parsed.command.config, true);
- break;
- case LogicCommandTargetKind::Output:
- break;
- case LogicCommandTargetKind::ExistingNode:
- break;
- }
- }
- }
- else
- {
- const std::string rung_id = request.continuing
- ? request.currentRungId : request.target.rungId;
- if (!request.continuing
- && request.target.kind != LogicCommandTargetKind::Output)
- {
- return executionFailure(
- "输出指令只能输入在第 11 列输出区", opcode);
- }
- const LadderRung *rung = editor_service_.findRung(
- request.logicId, rung_id);
- if (request.continuing && rung != nullptr && rung->output.has_value())
- {
- return executionFailure(
- "当前网络已经有输出,下一条请输入 LD/LDI/LDP/LDF 新建网络",
- opcode);
- }
- edited = editor_service_.setOutput(
- request.logicId, rung_id, parsed.command.config, true);
- }
-
- if (!edited.succeeded)
- {
- return executionFailure(edited.message, opcode);
- }
- return {
- true, {}, edited.id,
- editor_service_.rungIdForNode(request.logicId, edited.id),
- opcode};
- }
|