#include "logic_command_service.h" #include "logic_editor_service.h" #include #include #include #include #include namespace { std::vector splitTokens(const std::string &text) { std::istringstream stream(text); std::vector 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(std::toupper(character)); }); return value; } LogicCommandParseResult parseFailure(const std::string &message) { return {false, message, {}}; } bool requiresOperands( const std::vector &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::min() || parsed > std::numeric_limits::max()) { throw std::out_of_range("int16"); } *value = static_cast(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 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 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 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 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 &LogicCommandService::suggestions() { static const std::vector 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}; }