OpenPLC Editor v4 梯形图编辑器

发布时间:2026/7/12 1:30:21
OpenPLC Editor v4 梯形图编辑器 梯形图LD编辑器是 OpenPLC Editor 中用于编写 IEC 61131-3 梯形图逻辑的主要图形编程界面。该编辑器基于xyflow/react(React Flow) 构建可渲染基于梯级的画布其中触点、线圈、功能块和并联支路共同构成可执行的 PLC 程序。该编辑器的架构将节点模型构建纯数据、视觉渲染React 组件和状态管理Zustand slice分离为三个协调层——使得相同的节点数据既能驱动画布又能驱动馈送至编译器的序列化 XML。架构概述LD 编辑器遵循与项目 Atomic Design 组织方式相契合的三层组合架构Atom 层负责节点类型定义、构建器工厂、SVG 视觉效果及手柄系统。Molecule 层将 Atom 组合为梯级结构——即梯形图程序的基础水平逻辑单元。Organism 层则将完整的梯级实例与其头部、主体及创建交互编排在一起。ladder Zustand slice持有所有梯级状态节点、边、流元数据是画布的唯一数据源。节点类型系统LD 画布上的每个元素都是以 React Flow 的NodeT类型为根的区分联合成员。所有梯形图元素的共享基类是BasicNodeData每种节点类型均通过其特定的变体字段扩展该基类。BasicNodeData — 契约BasicNodeData确立了每个梯形图节点必须满足的接口用于图连接的手柄数组、用于边布线的输入/输出连接器引用、用于编译器序列化的numericId、variable绑定、executionOrder索引以及交互标志draggable、selectable、deletable。字段类型用途handlesCustomHandleProps[]此节点上的所有手柄inputHandlesCustomHandleProps[]左侧目标手柄outputHandlesCustomHandleProps[]右侧源手柄inputConnectorCustomHandleProps | undefined主输入手柄outputConnectorCustomHandleProps | undefined主输出手柄numericIdstring用于编译器输出的整数 UUIDvariable{ id?: string; name: string } | PLCVariable绑定的 PLC 变量executionOrdernumber扫描周期执行索引draggableboolean用户是否可拖动节点selectableboolean用户是否可选择节点deletableboolean用户是否可删除节点节点类型目录节点类型继承自变体判别器关键扩展字段拖动选择删除BlockBasicNodeDataBlockVariant(泛型T)executionControl,lockExecutionControl,connectedVariables,hasDivergence✅✅✅CoilBasicNodeDatadefault | negated | risingEdge | fallingEdge | set | reset—✅✅✅ContactBasicNodeDatadefault | negated | risingEdge | fallingEdge—✅✅✅ParallelBasicNodeDataopen | closeparallelInputConnector,parallelOutputConnector,parallelOpenReference,parallelCloseReference❌❌❌PlaceholderBasicNodeDatadefault | parallelrelatedNode,position(left | right | bottom)❌✅✅PowerRailBasicNodeDataleft | right—❌❌❌VariableBasicNodeDatainput | outputblock: { id, handleId, variableType }❌✅❌MockNode{ label, handles }textlabel: string❌✅✅每种节点类型上的变体判别器决定了渲染哪种 SVG 视觉效果以及对绑定变量应用何种验证规则。Block是最复杂的节点——它通过表示功能块变体的泛型T进行参数化其高度/宽度根据该变体中声明的输入/输出变量数量动态计算。自定义手柄系统React Flow 的默认Handle组件被CustomHandle封装后者通过全局和相对位置坐标扩展了标准的HandleProps。这种双坐标系对 LD 编辑器的布局算法至关重要全局位置glbX、glbY用于在流面板的坐标空间中定位手柄用于边布线和自动布局而相对位置relX、relY用于在其父节点内定位手柄用于 CSS 定位。所有手柄在默认情况下均以opacity-0渲染——它们作为 React Flow 边连接系统的点击目标而存在但在视觉上是不可见的。梯形图编辑器不使用自由形式的边绘制边是根据手柄位置通过编程方式计算的因此不可见的手柄充当锚点而不是交互式连接端点。节点构建器工厂buildNodes.tsx模块为每种节点类型提供了一个工厂函数。这些构建器接受BuilderBasicPropsid、posX、posY、handleX、handleY以及变体特定的参数并返回准备插入 store 的完全构造的 React FlowNode对象。这种工厂模式确保画布上的每个节点都通过相同的验证路径创建——在这些函数之外不可能进行临时的节点构造。构建器函数参考构建器输入变体动态尺寸关键构建逻辑buildBlockNodeBlockVariant✅ 宽/高根据变量数量计算将OUT重排至前端若需执行控制则注入EN/ENObuildCoilNodeCoilBuilderProps[variant]固定 28×24左侧目标 右侧源手柄buildContactNodeContactBuilderProps[variant]固定 24×24左侧目标 右侧源手柄buildParallelopen | close固定 4×24 个手柄左、右、下方向取决于开/闭、上对应侧builderPlaceholderNodedefault | parallel固定 10×10隐藏手柄通过relatedNode关联到同级节点buildPowerRailNodeleft | right固定 3×40朝向梯级内侧的单个手柄buildVariableNodeinput | output固定 (常量)条件性手柄创建——output块仅获取左侧手柄input块仅获取右侧手柄node-builders.ts中的nodesBuilder映射提供了一个从节点类型名称到构建器函数的字符串键分发表使 store 层和工具箱能够通过类型字符串实例化节点而无需直接导入每个构建器。这个重导出文件是刻意从 index.ts 中提取出来的以避免 store 层导入构建器函数时出现循环依赖。添加新的梯形图节点类型时你必须将其注册到三个位置(1)utils/types.ts中的类型定义(2)buildNodes.tsx中的构建器函数(3)node-builders.ts中的customNodeTypes以及nodesBuilder/defaultCustomNodesStyles映射。缺少任何注册都会导致静默渲染失败——节点将被插入 store 中但 React Flow 将不知道如何渲染它。Block 节点 — 深入剖析Block节点在架构上区别于所有其他节点类型因为其尺寸和手柄布局是从其变体定义派生的而非在编译时固定。BlockVariant描述了功能块的接口type BlockVariant { name: string type: string variables: { id?: string; name: string; class: string; type: { definition: string; value: string } }[] documentation: string extensible: boolean }variables数组驱动着布局和验证。每个变量声明了一个classinput、output或inOut以及一个可能是基本类型BOOL、INT或泛型类型ANY_INT、ANY的type。getBlockSize函数通过以下方式计算 Block 尺寸(1) 计算输入/输出变量数量以确定高度(2) 测量变量名字符串宽度以确定宽度上限为DEFAULT_BLOCK_WIDTH 216(3) 为每个变量引脚生成定位手柄。执行控制 (EN/ENO)getBlockVariantAndExecutionControl函数实现了 IEC 61131-3 的EN/ENO机制。当 Block 的第一个输入不是BOOL或其第一个输出不是BOOL时执行控制变为强制性lockExecutionControl true并且EN/ENO引脚会被注入到该变体的变量列表中。对于执行控制为可选的 Block用户也可以选择启用。buildBlockNode函数会在计算布局之前重新排列变体的变量将OUT放置在索引 0 处。这确保了主输出始终显示在右侧引脚列的顶部符合 IEC 61131-3 的视觉惯例。如果你手动构造BlockVariant请注意构建器会修改变量顺序。变量绑定与验证LD 编辑器在变量绑定上强制执行IEC 61131-3 类型约束。getLadderPouVariablesRungNodeAndEdges工具函数解析任意节点的 POU、梯级和变量上下文并根据节点类型应用不同的限制节点类型允许的变量类型理由Contact/Coil仅基本类型BOOL、INT等——排除derived触点和线圈代表布尔信号流Block所有类型包括derived用户自定义功能块可接受结构化类型Variable(Block 引脚)所有类型包括derivedBlock I/O 引脚可承载任何 IEC 类型validateVariableType函数实现了三层类型匹配算法(1) 针对特定类型如BOOL的精确匹配(2) 使用genericTypeSchemaZod schema 对ANY_*族进行的泛型类型组匹配(3) 接受所有类型的ANY通配符。伴随的getVariableRestrictionType函数则反转了此逻辑——给定预期的类型字符串它返回允许的具体类型集合以及定义是base-type还是derived从而驱动自动补全下拉列表的过滤。数组变量访问例如Sensor[0]通过resolveArrayVariableByName处理该函数解析方括号表示法并根据 POU 的变量声明进行解析。梯级组合模型梯级是梯形图的基础组织单元——一条从左电源轨延伸到右电源轨的水平带。在组件层级中梯级的组合方式如下Rung Organism(_organisms/graphical-editor/ladder/rung/index.tsx)顶层容器Rung Header(rung/header.tsx)显示梯级元数据和控件Rung Body(rung/body.tsx)该梯级节点和边的 React Flow 画布Create Rung(rung/create-rung.tsx)添加新梯级的交互入口Rung Utilities(rung/ladder-utils/)特定于梯级布局的边和节点构建辅助工具ladder-utils子目录包含特定于梯级的边构建器edges.ts和节点构建辅助工具nodes.ts它们了解如何通过适当的边布线将梯级的左电源轨 → 触点/Block → 右电源轨连接起来。elements/子目录提供了用于组合这些部分的元素级工具。梯形图工具箱 — 元素创建工作区活动栏中的Ladder Toolbox为四种用户可放置的元素类型提供了拖拽创建的交互入口工具箱项文件创建内容节点类型Contactladder/contact.tsx常开/取反/边沿触点contactCoilladder/coil.tsx输出线圈变体coilBlockladder/block.tsx功能块实例blockParallelladder/parallel.tsx并联支路开/闭parallel每个工具箱项调用相应的buildNodes工厂函数来创建节点数据随后将其插入 ladder store slice 中。nodesBuilder分发表确保工具箱无需导入单个构建器函数——它通过类型字符串查找构建器。尺寸常量与布局所有梯形图节点尺寸均集中在utils/constants.tsx中。布局引擎使用这些常量来计算间距、手柄位置和间隙节点类型宽度高度水平间隙垂直间隙手柄 Y手柄 Y 偏移Block216 (最大)动态120803640Coil28244580120Contact24244580120Parallel420010Placeholder1010151550Power Rail340200200Variable(常量)(常量)300(常量)(常量)node-builders.ts中的defaultCustomNodesStyles对象将这些尺寸与手柄位置元数据打包到单个查找表中。该查找表由 ladder store slice 的自动布局算法使用以在无需渲染的情况下计算节点位置——从而实现纯粹基于数据的确定性布局计算。状态管理 — Ladder Slice梯形图编辑器的状态存在于store/slices/ladderZustand slice 中它持有包含梯级每个梯级都有自己的nodes和edges数组、流元数据以及当前选定的 POU 绑定的LadderFlowType对象。该 slice 从node-builders.ts重导出通过 store 安全的index.ts而不是直接从组件层导入从而维护了项目从 store → 组件的严格单向依赖图。Ladder slice 的工具目录ladder/utils/包含自动布局算法和梯级构建逻辑它们将扁平的节点/边数据转换为间距适当的梯级可视化。这种分离确保了纯数学的布局计算在无需挂载 React 组件的情况下依然可测试。视觉渲染与调试集成每种节点类型将渲染拆分为视觉组件和数据组件。视觉组件如contact-visual.tsx、coil-visual.tsx、block-visual.tsx、variable-visual.tsx渲染 IEC 61131-3 标准 SVG 符号并接受一个覆盖运行时信号状态的debuggerColor参数。当调试器激活时手柄和节点符号会改变颜色以指示TRUE/FALSE信号状态而block-output-debug-badges和debug-value-badgeAtom 组件则在其相应引脚旁显示实时变量值。SVG 渲染器上的wrongVariable布尔参数会在绑定变量类型与预期类型不匹配时触发红色高亮——在编译前为类型约束违规提供即时的视觉反馈。自动补全系统LD 编辑器中的变量绑定使用专用的自动补全组件ladder/autocomplete/index.tsx它根据从getVariableRestrictionType派生的类型约束来过滤当前 POU 的变量声明。自动补全以不区分大小写的方式解析变量名并支持数组元素表示法确保输入Sensor[0]时能正确解析为基础数组变量Sensor及相应的索引。差异与变更追踪_atoms/graphical-editor/diff/目录提供了比较两个梯形图状态的差异工具——用于撤销/重做通过 history slice以及当工作副本与已保存版本产生分歧时的视觉变更指示。这与项目更广泛的 Store Slice 模式 相集成用于跨 slice 的状态协调。