Convert2ModuleNameTreeNode讲解
先看问题:为什么需要这个东西?
程序里有一个ModuleList,它是一份平铺的、顺序的模块清单。比如用户拖出来的流程可能是这样的:
[拍照] [如果 检测到瑕疵] [打标模块] [否则] [忽略模块] [结束] [保存结果]但在代码里,这个ModuleList存的是一维数组,没有父子关系:
索引0: "拍照" 索引1: "如果 检测到瑕疵" 索引2: "打标模块" 索引3: "否则" 索引4: "忽略模块" 索引5: "结束" 索引6: "保存结果"如果只是从头到尾跑一遍,那很简单。但问题来了——执行时需要根据结果跳转。比如"如果 检测到瑕疵"结果是 false,就要跳过"打标模块"和"否则"和"忽略模块",直接跳到"结束"后面的"保存结果"。这时候系统需要知道:
- "打标模块"的父级是谁?(“如果”)
- "打标模块"的同级兄弟有哪些?(“打标模块”、"否则"等)
- “结束"之后是什么?(跳到父级的兄弟"保存结果”)
Convert2ModuleNameTreeNode()就是把平铺列表变成一棵树,让程序能回答上面这些问题。
核心数据结构
就两个东西:
Stack<ModuleNameTreeNode>— 一个栈,栈顶永远指向"当前我属于哪个父级"
ModuleNameTreeNode— 每个节点只存三样:名字、父节点引用、子节点列表
算法:一步步模拟
假设输入(一维列表)是:
[拍照] [如果瑕疵] [标记] [否则] [忽略] [结束] [保存]栈初始为空[]。逐个遍历:
| 步骤 | 当前模块 | 栈(之前) | 操作 | 栈(之后) |
|---|---|---|---|---|
| 1 | 拍照 | [] | 不是"开始"类型,栈不变。父级=BaseTreeNode(根) | [] |
| 2 | 如果瑕疵 | [] | 是"如果"→压栈。父级=BaseTreeNode | [如果] |
| 3 | 标记 | [如果] | 不是开始类型。父级=栈顶即"如果" | [如果] |
| 4 | 否则 | [如果] | 是"否则"→先弹栈(结束"如果"分支),再压栈。父级=[]即根 | [否则] |
| 5 | 忽略 | [否则] | 不是开始类型。父级=栈顶即"否则" | [否则] |
| 6 | 结束 | [否则] | 是"结束"→弹栈。父级=[]即根 | [] |
| 7 | 保存 | [] | 不是开始类型。父级=根 | [] |
最终构建出的树结构:
(根 BaseTreeNode) ├── 拍照 (Parent=根) ├── 如果瑕疵 (Parent=根, ChildList=[标记]) │ └── 标记 (Parent=如果) ├── 否则 (Parent=根, ChildList=[忽略]) │ └── 忽略 (Parent=否则) ├── 结束 (Parent=根) └── 保存 (Parent=根)这东西实际怎么被使用的
在项目执行的主循环中,ModuleTreeNodeMap被频繁查询:
条件分支跳转— [Project.cs:595](/D:\JGTechVision\GJTechVisionV1.0.0\01_Sourse - 0604-2\VM\01Main\VM.Start\Services\Project.cs:595)
ModuleTreeNodeMap["打标模块"].Parent.ChildList → 得到 ["标记", "否则", "忽略", "结束"]条件失败时,遍历这个列表跳过所有兄弟,找到"结束"之后继续。
循环跳转— [Project.cs:620](/D:\JGTechVision\GJTechVisionV1.0.0\01_Sourse - 0604-2\VM\01Main\VM.Start\Services\Project.cs:620)
同理,停止循环模块通过ModuleTreeNodeMap[name].Parent.Parent.ChildList找到"循环结束"的位置然后跳出。
一句话总结:把用户可视化的嵌套流程(如果/否则/循环/文件夹),翻译成代码能理解的树结构,让执行引擎知道谁是谁的孩子、谁是谁的兄弟,从而正确跳转。
