→ 创建 HW_OUTPUT 节点 → 连线从同层 Gate
链级别 Comb 处理 (第二遍布线):
1. Comb 的 Gate 输出 → 链级 Gate 输入
2. 链级 Input → 链级 Gate
3. 链级 Gate → 链级 Output
```
**核心结论: Comb 是 XML 中天然存在的嵌套结构,C 引擎自动递归解析。前端应该:**
- 加载 XML 时自动识别 Comb 嵌套关系
- 渲染为视觉分组容器 (矩形框)
- 允许用户通过拖拽节点入框来创建 Comb
- 不允许手动添加空 Comb
---
## 三、原工程 PLC Config 前端功能详细清单
### 3.1 导航与入口
- Hash 路由: `#plc_config`
- 导航标签: "PLC 配置" / "PLC Config"
- 导航图标: 原工程用 emoji 🧩, 当前用 SVG cpu
### 3.2 多 Chain 管理
| 功能 | 描述 | 实现方式 |
|------|------|---------|
| **Chain 标签切换** | 顶部标签栏,每个 Chain 一个标签 | 点击切换当前编辑的 Chain |
| **Chain 标签内容** | 显示 "Chain N (节点数)" | 实时更新 |
| **添加 Chain** | +Chain 按钮 | 创建空 Chain, 自动切换 |
| **删除 Chain** | -Chain 按钮 | 删除当前 Chain (≥2个时可用) |
### 3.3 节点类型工具栏
每种节点类型一个按钮,点击后在 Canvas 随机位置添加:
```
[+INPUT] [+OUTPUT] [+OR] [+AND] [+NOT] [+T62P] [+T62D] [+RISING] [+SR] [+RS] [+FALLING]
```
### 3.4 Canvas 画布编辑器
| 功能 | 描述 | 交互 |
|------|------|------|
| **节点渲染** | 圆角矩形 + 类型名 + ID号 + 颜色区分 | 11种颜色方案 |
| **引脚圆点** | 输入引脚(左, 彩色), 输出引脚(右, 绿色) | 5px 半径 |
| **连线渲染** | 节点间实线, 从输出引脚→输入引脚 | 深灰 #2c3e50 |
| **拖拽节点** | 按住节点拖动 | 20px 网格吸附 |
| **连线操作** | 从输出引脚拖出虚线→目标输入引脚释放 | 红色虚线 |
| **选中节点** | 单击节点高亮 | 白色填充 + 红色边框 |
| **删除节点** | 选中 + 删除按钮 | 同时删除关联连线 + 重编号 Gate ID |
| **平移画布** | 拖拽空白区域 | 平移 |
| **缩放** | 滚轮缩放 0.5x-2.0x | 缩放栏显示百分比 |
| **缩放栏** | [放大] [缩小] [适应] 100% | 适应 = zoom=1, pan=0 |
| **双击编辑** | 双击节点弹出属性 Modal | 编辑 saddr/desc/delayMs/spin/rpin |
### 3.5 Comb 组合块
| 功能 | 描述 |
|------|------|
| **自动识别** | 加载 XML 时自动解析 Comb 嵌套结构 |
| **视觉渲染** | 虚线圆角矩形,标签 "Comb #N" |
| **嵌套 Comb** | 视觉上子 Comb 在父 Comb 内部 |
| **节点归属** | Comb 内节点在视觉上位于 Comb 矩形内 |
| **创建 Comb** | 选中多个节点 → "成组为 Comb" 创建 |
| **解散 Comb** | 选中 Comb → "解散 Comb" → 节点回到链级别 |
### 3.6 XML 查看与持久化
| 功能 | 描述 |
|------|------|
| **XML 实时预览** | 只读 `` 标签, 随编辑实时更新 |
| **加载 XML** | 从 RTU 读取 `cat config/PLC/Reclose_logic.xml` |
| **保存 XML** | 通过 WS `set_file` 写回 RTU |
| **checksum** | 根节点自动计算 XOR checksum |
### 3.7 操作按钮栏
```
[加载 XML] [保存 XML] [清空] [删除选中节点]
```
---
## 四、原工程 UI 设计参考
### 4.1 设计风格
| 参数 | 原设计值 | 当前值 |
|------|---------|--------|
| 风格 | 暗色工业主题 (Dark Industrial Dashboard) | 暗色工业主题 |
| 主色调 | Cyan/Teal (#00E5FF / #1DE9B6) | Teal (#00d4aa) |
| 背景 | #0F1923 (deep navy) | #0a0f16 (更深) |
| 卡片背景 | #1A2332 | #111922 |
| 字体 | Inter + JetBrains Mono | 同 |
| 图标 | Emoji (原工程) → SVG Lucide (重构目标) | SVG 内联 Lucide |
| Canvas 背景 | 浅色网格 (#f0f2f5) | 同 |
### 4.2 导航图标规格
| 页面 | 原 Emoji | Lucide SVG 图标名 | 说明 |
|------|---------|------------------|------|
| 仪表盘 | 📊 | dashboard | 四象限网格 |
| 开出信号 | 📡 | signal | 信号波形 |
| 开入信号 | 🔗 | link | 链接 |
| 遥控信号 | ⚡ | zap | 闪电 |
| 参数设置 | 🎚️ | sliders | 滑块 |
| 定值管理 | ⚙️ | settings | 齿轮 |
| 数据中心 | 📋 | database | 数据库 |
| PLC 调试 | 🔌 | bug | Bug/调试 |
| PLC 配置 | 🧩 | cpu | CPU/芯片 |
| 实时监控 | 📈 | activity | 心电图 |
| 命令终端 | 💻 | terminal | 终端 |
---
## 五、重构目标与要求
### 5.1 必须保留/增强的功能
- [x] 多 Chain 标签切换
- [x] 11 种节点类型完整支持
- [x] Canvas 拖拽、连线、缩放、平移
- [x] 节点属性双击编辑 (含 SR/RS R/S 引脚, T62P/D delayMs)
- [x] XML 实时预览
- [x] 从 RTU 加载/保存
### 5.2 必须修复的问题
- [ ] **Comb 改为自动识别** — 加载 XML 时检测,不允许手动添加空 Comb
- [ ] **Comb 创建方式** — 选中多个节点 → "成组为 Comb"
- [ ] **导航图标风格** — 恢复工业风格,尺寸与文字匹配
- [ ] **set_file 后端实现** — 确保保存功能真正可用
### 5.3 必须优于原工程的新功能
- [ ] Comb 视觉边界框正确嵌套
- [ ] SR/RS 引脚 S/R 标签显示
- [ ] 节点颜色方案保持一致性
- [ ] 更好的错误处理 (XML 解析失败提示)
- [ ] 键盘快捷键 (Delete 删除, Ctrl+S 保存)
---
## 六、Comb 自动检测算法 (前端)
```javascript
// 加载 XML 时的 Comb 自动检测伪代码
function detectCombsFromXml(xmlDoc):
for each in :
chain.nodes = [] // 扁平存储所有节点
chain.combs = [] // 存储 Comb 容器定义
chain.links = [] // 存储连线
// 1. 解析顶层元素
parseChainElements(chainEl, chain, parentId=-1)
function parseChainElements(parentEl, chain, parentId):
for each child in parentEl.children:
if child.tag == 'Inputs' or 'Outputs':
parseSignals(child, chain)
if child.tag == 'Gate':
parseGate(child, chain)
if child.tag == 'Comb':
combId = child.getAttribute('id')
// 创建 Comb 容器 (位置由内部节点包围盒计算)
comb = { id: combId, parentId: parentId }
chain.combs.push(comb)
// 递归解析 Comb 内部元素
parseChainElements(child, chain, combId)
// 2. Comb 位置自动计算 (后处理)
for each comb in chain.combs:
var nodesInComb = chain.nodes.filter(n => 位于 comb 区域)
// 包围盒 = min/max of nodesInComb 的 x,y
comb.x = minX - margin
comb.y = minY - margin
comb.w = maxX - minX + 2*margin
comb.h = maxY - minY + 2*margin
```
### Comb 归属判断
节点属于哪个 Comb 由以下规则确定:
1. **来自 内 //** → 属于该 Comb
2. **来自 内子 的元素** → 属于子 Comb (嵌套)
3. **来自 顶层元素** → 不属于任何 Comb (parentId=-1)
---
## 七、节点样式与颜色方案
| 节点 | 填充色 | 描边色 | 尺寸 |
|------|--------|--------|------|
| INPUT | #e8f5e9 浅绿 | #27ae60 绿 | 110×34 |
| OUTPUT | #fce4ec 浅粉 | #e74c3c 红 | 110×34 |
| OR | #fff3e0 浅橙 | #e67e22 橙 | 76×46 |
| AND | #e3f2fd 浅蓝 | #2980b9 蓝 | 76×46 |
| NOT | #f3e5f5 浅紫 | #8e44ad 紫 | 76×46 |
| T62P | #fce4ec 浅粉 | #c0392b 深红 | 86×46 |
| T62D | #fff3e0 浅橙 | #d35400 深橙 | 86×46 |
| RISING | #e8f5e9 浅绿 | #1abc9c 青绿 | 86×46 |
| SR | #e3f2fd 浅蓝 | #3498db 亮蓝 | 86×52 |
| RS | #fce4ec 浅粉 | #9b59b6 紫 | 86×52 |
| FALLING | #fff3e0 浅橙 | #16a085 青 | 86×46 |
引脚颜色: 输入=同节点描边色, 输出=#27ae60 绿
### Comb 视觉
- 描边: #95a5a6 灰色虚线 (8px dash, 4px gap)
- 填充: rgba(149,165,166,0.05) 极浅灰
- 标签: 左上角 "Comb #N"
- 圆角: 6px
---
## 八、文件结构
```
web/js/pages/plc.js ← PLC Debug + PLC Config (重构目标)
web/css/style.css ← PLC 相关 CSS (.chain-tabs, .plc-canvas-wrap, etc.)
web/js/i18n.js ← PLC 相关翻译 key
config/PLC/Reclose_logic.xml ← XML 配置文件 (数据源)
```