# 📘 自述文档：基于 .NET 8 的流程可视化框架

## 🧩 项目介绍

本项目是一个基于 **.NET 8** 的流程可视化框架，

- 全部运行依赖、运行环境、编辑器均为 **开源**，
- 支持 **二次开发**，适合用于嵌入式、工控、数据流程自动化等场景。

📺 **Bilibili 视频更新**：不定期更新项目相关内容，可前往我的 [Bilibili 主页](https://space.bilibili.com/33526379) 查看。

---

## 🗓️ 计划任务（更新于 2025 年 7 月 28 日）

- ✅ 正在完善“**流程图转 C# 代码**”功能
- 🔥 构思 **热更新机制**
- 🔧 多客户端 **远程控制逻辑**设计中

---

## 🖌️ 如何绘制流程？

### 🛠️ 准备工作

1. 编译 `Serein.Workbench` 项目，确保工作台可运行。
2. 新建一个类库项目，并添加对 `Serein.Library` 的引用（也可通过 Negut 获取）。

### ✨ 开始绘图

1. 在类库项目中添加一个类，使用 `[DynamicFlow]` 特性标记类。
2. 使用 `[NodeAction]` 特性标记方法。
3. 编译项目并将生成的 DLL 拖入工作台左侧“类库”面板中。
4. 工作台自动解析并加载所有节点方法。

### 🎨 绘制节点流程

1. 在工作台中新建一个画布。
2. 从左侧类库面板中拖拽方法到画布上生成节点。
3. 在节点连接器之间拖拽生成连线，表示数据/控制流。
4. 可重复添加、连接多个节点构建完整流程。

### ▶️ 运行流程

- **从起始节点运行**：
  - 设置某节点为“起始节点” → 点击“运行” → 选择“从当前画布运行”
- **从指定节点运行**：
  - 框选单个节点 → 按 `F5` 运行
- **运行所有画布**：
  - 在“运行”菜单中选择“运行所有画布”

### 💾 保存/加载项目

- **保存项目**：`视图` → `保存项目` → 选择保存路径
- **加载项目**：`视图` → `加载本地项目` → 选择项目文件

### 🪵 查看输出日志

- 在 `视图` → `输出窗口` 中查看异常、日志与调试信息

---

## 🧱 如何让方法成为流程节点？

- 使用 `[NodeAction]` 特性标记方法
- 指定合适的节点类型（如 Action、Flipflop、Script 等）

---

## 📚 关键接口说明

### IFlowContext（流程上下文）

> 节点之间数据传递的核心接口，由运行环境自动注入。

#### 🔑 核心特点

- 全局单例环境接口，支持注册/获取实例（IoC 模式）
- 每个流程实例中上下文隔离，防止脏数据产生

#### 🔍 常用属性

| 属性                | 类型 | 描述                     |
| ----------------- | -- | ---------------------- |
| `RunState`        | 枚举 | 表示流程运行状态（初始化、运行中、运行完成） |
| `NextOrientation` | 枚举 | 指定下一个分支（成功、失败、异常）      |
| `Exit()`          | 方法 | 提前终止当前流程               |

---

## 🧾 DynamicNodeType 枚举说明

### 1️⃣ 控件不生成节点（生命周期方法）

| 枚举值       | 描述                            |
| --------- | ----------------------------- |
| `Init`    | 程序启动时最先执行，适合进行类初始化等操作         |
| `Loading` | 所有 DLL 的 Init 执行完毕后调用，适合业务初始化 |
| `Exit`    | 程序关闭时调用，适合释放线程/关闭服务           |

> 参数均为 `IFlowContext`，返回值支持异步，但框架不处理其结果。

### 2️⃣ 基础节点类型

#### 📎 FlowCall（流程接口节点）

- 实现跨画布复用逻辑
- 支持参数共享模式（全局同步）或参数独立模式（本地隔离）

#### 💡 Script（脚本节点）

- 使用 AST 抽象语法树动态编译运行
- 内置 `global()`、`now()` 等函数，参考 `Serein.Library.ScriptBaseFunc` 类中成员方法。
- 在代码中调用 `SereinScript.AddStaticFunction("your function name", MethodInfo);` 注册更多的内置方法。

示例：

```csharp
// 入参是一个对象，入参名称是 "info" ，包含Var、Data属性。
// 自定义类
class Info{
  string PlcName;
  string Content;
  string LogType;
  DateTime LogTime;
}
plc = global("JC-PLC"); // 获取全局数据节点中的数据
log = new Info() {
  Content = plc + " " + info.Var + " - 状态 Value  : " + info.Data,
  PlcName = plc.Name,
  LogType = "info",
  LogTime = now(),
};
return log;
```

#### 🌐 GlobalData（全局数据节点）

- 获取/共享运行时跨节点数据
- 表达式使用：`global("DataKey")`

### 3️⃣ 从 DLL 生成控件的节点

#### ⚙️ Action

- 最基础的节点类型，入参自动匹配，返回值支持异步

#### 🔁 Flipflop（触发器）

- 可响应外部事件或超时触发
- 支持状态返回（成功、失败、取消）与触发源类型（外部、超时）

#### 🧠 ExpOp（表达式节点）

- 提取对象的子属性、字段、数组、字典值
- 示例：`@Get .Array[22]` 或 `@Get .Dict["key"]`

#### 🔍 ExpCondition（条件表达式节点）

- 条件判断节点，支持布尔/数值/文本逻辑判断

示例：

```text
条件表达式：.Age > 18 && data.Age < 35
注意，右侧出现的“data”是表达式数据的默认名称，假如你需要在表达式里其他地方使用入参数据时，就可以使用“data”。

或 .Text.StartsWith("1100")  // 判断文本是否以1100开头
```

#### 🖼️ UI（嵌入控件）

- 显示自定义 `UserControl` 到工作台
- 返回实现 `IEmbeddedContent` 接口的对象

示例代码片段：

```csharp
await context.Env.UIContextOperation.InvokeAsync(() => {
    var userControl = new UserControl();
    adapter = new WpfUserControlAdapter(userControl, userControl);
});
return adapter;
public class WpfUserControlAdapter : IEmbeddedContent
{
   private readonly UserControl userControl;
   private readonly IFlowControl flowControl;
   public WpfUserControlAdapter(UserControl userControl, IFlowControl flowControl)
   {
    this.userControl = userControl;
    this.flowControl= flowControl;
   }
   public IFlowControl GetFlowControl()
   {
    return flowControl;
   }
   public object GetUserControl()
   {
    return userControl;
   }
}
```

---

## ✅ 总结

- 该框架提供灵活的流程编辑、运行与二次扩展能力
- 支持脚本、全局数据、子流程等高级功能
- 适用于 AGV 调度、PLC 自动化、图形化逻辑控制等场景

---

> 本文档适用于开发者快速上手和参考具体接口用法。如需进一步了解，请关注项目演示视频或参与社区讨论。

