/** * @file logic_editor_widget.h * @brief 定义结构化梯形图编辑和运行轨迹画布 * @version 1.0.0 * @author suyu * @date 2026-08-22 */ #pragma once #include "domain/control_logic_model.h" #include "services/logic_command_service.h" #include "services/logic_editor_service.h" #include "services/software_logic_executor.h" #include #include #include #include class QGraphicsScene; class QCompleter; class QEvent; class QLineEdit; class QMouseEvent; class QRubberBand; class QResizeEvent; /** 将结构化梯形图表达式投影成网格图元;不保存自由线段,编辑通过服务提交 */ class LogicEditorWidget final : public QGraphicsView { Q_OBJECT public: /** 梯形图中的条件、输出、横线和辅助选择图元类型 */ class NodeItem; class WireItem; class WireCellItem; class GapCellItem; class VerticalConnectorItem; class RungItem; class EmptySlotItem; class OutputSlotItem; /** 创建梯形图画布并绑定编辑服务 */ explicit LogicEditorWidget( LogicEditorService &editor_service, QWidget *parent = nullptr); /** 切换当前显示的控制逻辑;不存在时显示空场景 */ void setLogicId(const std::string &logic_id); /** 设置是否允许编辑梯形图 */ void setEditingEnabled(bool enabled); /** 设置离线运行轨迹;真机模式应传入空轨迹,避免显示本地伪轨迹 */ void setRuntimeTrace( const LogicTraceSnapshot &trace, const std::string &fault_node_id = {}); /** 清除当前运行轨迹和故障节点标记 */ void clearRuntimeTrace(); /** 返回当前是否正在显示运行轨迹 */ bool runtimeTraceEnabled() const; /** 根据当前逻辑模型重建梯形图场景 */ void reloadLogic(); /** 选中指定节点并滚动到可见区域 */ void selectNode(const std::string &node_id); /** 返回当前选中的单个节点标识 */ std::string selectedNodeId() const; /** 返回当前选中的多个节点标识 */ std::vector selectedNodeIds() const; /** 返回当前选中的网络标识 */ std::string selectedRungId() const; /** 新增一个空的梯形图网络 */ LogicEditorResult addRung(); /** 将当前选择转换为服务调用并新增串联条件 */ LogicEditorResult addCondition(const LogicNodeConfig &config); /** 将当前选择转换为服务调用并新增并联支路 */ LogicEditorResult addParallelBranch(const LogicNodeConfig &config); /** 在当前选择位置新增横线 */ LogicEditorResult addHorizontalWire(); /** 在当前选择位置新增竖线 */ LogicEditorResult addVerticalWire(); /** 删除当前选择位置的横线 */ LogicEditorResult deleteHorizontalWire(); /** 删除当前选择位置的竖线 */ LogicEditorResult deleteVerticalWire(); /** 设置当前网络的输出线圈 */ LogicEditorResult setOutput( const LogicNodeConfig &config, bool configured = false); /** 按当前画布选择位置粘贴一组连续条件 */ LogicEditorResult pasteConditionNodes(const std::vector &nodes); /** 删除当前选中的节点、线段或网络 */ LogicEditorResult deleteSelected(); signals: /** 当前选中的节点发生变化时发出 */ void nodeSelected(const QString &node_id); /** 梯形图模型成功发生变化时发出 */ void graphChanged(); /** 编辑服务操作失败时发出可显示的错误信息 */ void editorError(const QString &message); protected: /** 在任意网络区域开始鼠标框选 */ void mousePressEvent(QMouseEvent *event) override; /** 更新鼠标框选区域 */ void mouseMoveEvent(QMouseEvent *event) override; /** 完成鼠标框选并提交场景选择 */ void mouseReleaseEvent(QMouseEvent *event) override; /** 在条件网格或输出槽双击时打开命令输入框 */ void mouseDoubleClickEvent(QMouseEvent *event) override; /** 在窗口大小变化后重新布局梯形图场景 */ void resizeEvent(QResizeEvent *event) override; /** 滚动画布时同步命令输入框位置 */ void scrollContentsBy(int dx, int dy) override; /** 处理命令输入框的回车、取消和按键事件 */ bool eventFilter(QObject *watched, QEvent *event) override; private: /** 处理图形场景选择变化 */ void handleSelectionChanged(); /** 把编辑服务结果转换为错误信号 */ void reportFailure(const LogicEditorResult &result); /** 返回当前选中的网络标识 */ std::string currentRungId() const; /** 选中指定表达式图元 */ void selectExpression(const std::string &expression_id); /** 选中指定横线图元 */ void selectWire(const std::string &wire_id); /** 收集当前选中的表达式标识 */ std::vector selectedExpressionIds() const; /** 收集当前选中的横线标识 */ std::vector selectedWireIds() const; /** 收集当前选中的并联支路标识 */ std::vector selectedBranchIds() const; /** 收集当前选中的空网格位置 */ std::vector> selectedEmptySlots() const; /** 收集当前选中的横线网格位置 */ std::vector> selectedWireCells() const; /** 收集当前选中的显式断路网格位置 */ std::vector> selectedGapCells() const; /** 判断当前是否选中了网络内的图元 */ bool hasSelectedRungItem() const; /** 收集当前明确选中的网络图元 */ std::vector selectedRungItemIds() const; /** 打开指定目标位置的命令输入框 */ void beginCommandInput( const LogicCommandTarget &target, const QPointF &scene_center); /** 提交当前命令输入;失败时保留输入框并标红 */ void commitCommandInput(); /** 关闭命令输入框并清理临时状态 */ void cancelCommandInput(); /** 根据网络、列和支路标识查找下一输入位置 */ bool beginNextCommandInput(); /** 按目标定位输入框的屏幕矩形 */ void positionCommandInput(const QPointF &scene_center); /** 返回当前场景目标的中心位置 */ bool findCommandTargetCenter( const LogicCommandTarget &target, QPointF *scene_center) const; /** 将成功命令选中并刷新属性面板 */ void selectCommandResult(const LogicCommandResult &result); /** 梯形图编辑服务,不由控件拥有 */ LogicEditorService &editor_service_; /** 命令语解析和结构化编辑适配服务 */ LogicCommandService command_service_; /** 承载梯形图图元的场景 */ QGraphicsScene *scene_ = nullptr; /** 当前显示的控制逻辑标识 */ std::string logic_id_; /** 当前选中的网络标识 */ std::string current_rung_id_; /** 最近一次收到的运行轨迹 */ LogicTraceSnapshot trace_; /** 最近一次运行故障节点标识 */ std::string fault_node_id_; /** 是否显示运行轨迹 */ bool runtime_trace_enabled_ = false; /** 是否允许编辑梯形图 */ bool editing_enabled_ = true; /** 鼠标框选覆盖层 */ QRubberBand *selection_band_ = nullptr; /** 框选起点(视口坐标) */ QPoint selection_origin_; /** 框选时是否已超过拖拽阈值 */ bool selection_dragging_ = false; /** 框选起始时的键盘修饰键 */ Qt::KeyboardModifiers selection_modifiers_ = Qt::NoModifier; /** 命令输入框,仅在编辑态临时创建 */ QLineEdit *command_editor_ = nullptr; /** 命令输入补全器 */ QCompleter *command_completer_ = nullptr; /** 当前命令输入目标 */ LogicCommandTarget command_target_; /** 是否沿当前命令序列继续输入 */ bool command_continuing_ = false; /** OR/ORI 只增加支路,不推进主表达式列 */ bool command_keep_column_ = false; /** 连续输入对应的当前网络 */ std::string command_current_rung_id_; /** 编辑已有网络 OR 时保留的选择范围 */ std::vector command_parallel_node_ids_; };