四、 节点业务层(FlowNode)- 工业流程引擎架构

工业流程引擎架构

节点业务层(FlowNode)

核心作用:封装具体的工业工序逻辑,实现业务与调度层的解耦,支持灵活拓展。
核心组件:各类工业节点(如:设备启停节点、IO读写节点、延时等待节点、条件判断节点、循环工序节点、报警提示节点、数据保存节点等)。
实现机制:每个节点继承统一接口,绑定对应的硬件抽象层(HAL)接口或业务服务,节点执行时调用底层硬件或业务逻辑。


  1. 解构用户请求

    • 主题:工业流程引擎架构——第四部分:节点业务层(FlowNode)。

    • 核心功能:封装具体的工业工序逻辑,实现业务与调度层的解耦,支持灵活扩展。

    • 核心组件:各类工业节点(设备启停、IO读写、延时等待、条件判断、循环工序、报警提示、数据保存等)。

    • 实现机制:每个节点继承统一接口,绑定硬件抽象层(HAL)或业务服务,执行时调用底层硬件或业务逻辑。

    • 要求

      • 完整的设计方案(系统框图、软件流程图)。

      • 用通俗易懂的实例进行详细、深入、准确的解释。

      • UML建模(类图、时序图等)。

      • 完整的项目文件结构。

  2. 领域与背景分析

    • 这是关于工业流程引擎的“插件层”或“策略层”。

    • 之前的部分:流程模型(数据)、画布渲染(UI)、流程运行器(编排/状态机)。

    • 当前部分:实际的工作单元(做具体工作的事物)。该层必须高度可扩展,以便工程师可以添加新的设备类型或业务逻辑,而无需更改核心调度引擎。

    • 关键概念:策略模式、工厂模式、依赖注入(HAL/服务)、命令模式、模板方法模式。

  3. 起草内容结构

    • 标题与定位:核心哲学,将其比作“工具箱”或“乐高积木”。

    • 总体架构(系统框图):展示FlowNode如何适配FlowRunner、HAL和业务服务。

    • 核心组件(节点类型):根据列表(设备、IO、定时、条件、循环、警报、数据)进行详细阐述。

    • 实现机制(设计模式):统一接口(INodeExecutor)、模板方法、工厂模式、HAL绑定。

    • 软件流程图:节点执行的生命周期(初始化 -> 输入验证 -> 硬件调用 -> 输出处理 -> 完成)。

    • UML建模:类图展示NodeExecutor接口、抽象基类、具体节点(DeviceStartNodeDelayNode等)和HAL接口。展示“执行节点”的时序图。

    • 通俗实例:芯片封装产线中的“加热节点”或“IO读取节点”。

    • 项目文件结构executor/hal/factory/

    • 开发机制与步骤:如何添加新节点(扩展机制)。

  4. 起草过程中的自我修正/细化

    • 注意:提到了“硬件抽象层(HAL)”。确保HAL被清晰地建模为接口,而FlowNode实现依赖于这些接口(依赖倒置)。

    • 节点类型:按类别(设备控制、流程控制、数据处理)对它们进行分组,使其结构更清晰。

    • 执行逻辑:调度层(FlowRunner)调用NodeExecutor.execute(context)。节点决定如何运行(同步/异步),但FlowRunner处理超时/重试。实际上,节点内部逻辑可以是同步的,而FlowRunner处理包装器。澄清这一点:节点包含业务逻辑,它可能调用异步HAL调用,但它必须将控制权返回给FlowRunner。

  5. 编写Mermaid图表

    • 系统框图:层级结构 FlowRunner -> NodeFactory -> NodeExecutors -> HAL/Service层。

    • 软件流程图execute方法内部的详细流程:解析输入 -> 调用HAL -> 处理结果 -> 设置输出变量。

    • UML类图:接口INodeExecutor,抽象类BaseNodeExecutor(含模板方法doExecute),具体类HeaterNodeDelayNodeAlarmNode,HAL接口IDeviceService

    • UML时序图FlowRunner -> HeaterNode -> PLCConnector -> 返回。

  6. 打磨细节(通俗实例)

    • 使用“加热节点”示例。输入:targetTempdeviceId。输出:actualTemp。逻辑:调用halService.writeRegister(deviceId, targetTemp),然后循环读取直到达到温度。或异步启动。

    • 解释“循环工序节点”如何在内部使用while循环,但在引擎的DAG中仅将其视为单个节点。

  7. 对照要求检查

    • FlowNode的完整设计方案?是的。

    • 系统框图 + 软件流程图?是的。

    • 通俗易懂的实例?是的(芯片封装/加热)。

    • UML(类图、时序图)?是的。

    • Markdown + Mermaid?是的。

    • 项目文件结构?是的。

    • 深入机制/方法/步骤?是的。

  8. 生成最终输出(将详细内容组织成连贯的叙述)。确保代码块具有正确的mermaid语法。结构保持清晰干净。


工业流程引擎架构 —— 节点业务层(FlowNode)深度设计方案

一、定位与核心哲学

在整体分层架构中,节点业务层(FlowNode) 是工业流程引擎的 “可更换的机械臂/工具箱”。它封装了所有的“动作”和“判断”,是调度层(大脑)与硬件层(肌肉)之间的“神经末梢”。

通俗类比:如果把整个引擎比作一家自动化餐厅

  • 画布层 = 厨师面前的平板电脑(显示菜谱流程)。
  • 调度层 = 餐厅的中央调度系统(决定先做哪道菜)。
  • 节点业务层(FlowNode) = 厨师手中的不同厨具(炒锅、蒸笼、烤箱、计时器)
  • 调度系统说:“开始炒菜”,炒锅(节点)负责具体翻动。
  • 调度系统说:“蒸米饭”,蒸笼(节点)负责控制蒸汽。
  • 每一种厨具(节点)都遵循统一的接口(有把手、能加热/操作),但内部实现完全不同。当餐厅需要做新菜时(扩展新工艺),只需添置一个新厨具(新节点类),完全不用改造调度系统。

核心设计目标

  1. 开闭原则(OCP):对扩展开放(新增节点类型),对修改关闭(不碰调度层和画布层)。
  2. 单一职责:每个节点类只负责一种工业工序(如只负责“Modbus写寄存器”)。
  3. 依赖倒置:节点不直接依赖硬件驱动,而是依赖 硬件抽象层接口(HAL),便于测试和硬件替换。
  4. 可组合性:节点执行后可产生标准化的输出(变量),供后续节点(如条件网关)消费。

二、总体架构设计(系统框图)

FlowNode层在整体架构中处于 “执行落地” 的位置,上承调度器,下接硬件/中间件。

┌─────────────────────────────────────────────────────────────────────────────┐
│                        流程调度层(FlowRunner)                            │
│                  (调用 NodeExecutorProxy 执行节点)                        │
└───────────────────────────────┬─────────────────────────────────────────────┘
                                │ 传递 ExecutionContext + NodeConfig
                                ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                      节点业务层门面(NodeExecutorFactory)                  │
│                根据节点类型(如 SERVICE_TASK)动态创建执行器               │
└───────────────────────────────┬─────────────────────────────────────────────┘
                                │
┌───────────────────────────────┼─────────────────────────────────────────────┐
│              统一节点执行器接口(NodeExecutor)                            │
│  ┌────────────────────────────┴──────────────────────────────────────────┐ │
│  │          抽象基类 AbstractNodeExecutor(模板方法模式)                │ │
│  │  ├─ validateInputs()  → 校验输入参数                                  │ │
│  │  ├─ doExecute()       → 具体业务逻辑(子类实现)                     │ │
│  │  └─ writeOutputs()    → 写入输出变量到上下文                         │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                    │                                       │
│          ┌─────────────┬────────────┼────────────┬───────────────┐        │
│          ▼             ▼            ▼            ▼               ▼        │
│   ┌─────────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ │
│   │设备控制类    │ │ 流程控制  │ │ 数据IO   │ │ 逻辑判断   │ │ 通知类   │ │
│   │·设备启停    │ │ ·延时等待 │ │ ·读寄存器│ │ ·条件判断  │ │ ·报警提示│ │
│   │·电机调速    │ │ ·循环工序 │ │ ·写数据库│ │ ·表达式    │ │ ·邮件短信│ │
│   │·加热控制    │ │ ·子流程   │ │ ·文件操作│ │            │ │          │ │
│   └──────┬──────┘ └─────┬────┘ └────┬─────┘ └──────┬─────┘ └────┬─────┘ │
└──────────┼───────────────┼───────────┼───────────────┼────────────┼───────┘
           │               │           │               │            │
           ▼               ▼           ▼               ▼            ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                   硬件抽象层(HAL)& 基础服务层                             │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐  ┌─────────────────────┐ │
│  │ PLC连接器   │  │ 传感器驱动  │  │ 数据库DAO  │  │ 消息中间件(MQTT)  │ │
│  │ (Modbus)   │  │ (OPC UA)   │  │ (JDBC)    │  │ 邮件/短信网关       │ │
│  └────────────┘  └────────────┘  └────────────┘  └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

三、核心组件与节点类型详解

3.1 统一节点接口设计(模板方法模式)

每个节点执行器都遵循以下标准生命周期:

public interface NodeExecutor {
    // 判断是否支持该节点类型
    boolean supports(NodeType type);
    // 执行入口(由调度层调用)
    void execute(ExecutionContext ctx, NodeEntity node) throws NodeExecutionException;
}

// 抽象基类(模板方法模式)
public abstract class AbstractNodeExecutor implements NodeExecutor {
    @Override
    public final void execute(ExecutionContext ctx, NodeEntity node) {
        // 1. 前置校验(必须通过才能继续)
        validateInputs(ctx, node);
        // 2. 记录开始时间
        long startTime = System.currentTimeMillis();
        try {
            // 3. 核心业务(子类实现)
            Object result = doExecute(ctx, node);
            // 4. 写入输出变量
            writeOutputs(ctx, node, result);
            // 5. 记录成功日志
        } catch (Exception e) {
            // 6. 异常统一处理
            handleException(ctx, node, e);
            throw new NodeExecutionException(e);
        } finally {
            // 7. 记录执行耗时(监控埋点)
            recordMetrics(ctx, node, System.currentTimeMillis() - startTime);
        }
    }
    // 子类必须实现
    protected abstract Object doExecute(ExecutionContext ctx, NodeEntity node);
    // 可选重写
    protected void validateInputs(...) { /* 默认空实现 */ }
    protected void writeOutputs(...) { /* 默认:将result存入变量 resultVar */ }
}

3.2 七大核心工业节点实现(面向实际场景)

(1)设备启停节点(DeviceControlNode)
  • 作用:启动或停止一台物理设备(电机、泵、加热器)。
  • 配置参数deviceIdaction(START/STOP)、protocol(Modbus/OPC UA)。
  • 执行逻辑:通过HAL层向PLC发送写寄存器指令。
  • 输出变量deviceStatus(ON/OFF)、executionTime
(2)IO读写节点(IOReadWriteNode)
  • 作用:读取传感器值或向执行器写入模拟量/数字量。
  • 配置参数registerAddressdataType(BOOL/INT/FLOAT)、value(写入值)。
  • 执行逻辑
    • 读取hal.readRegister(deviceId, address) → 存入变量 readValue
    • 写入hal.writeRegister(deviceId, address, value)
  • 输出变量readValue(读取模式)。
(3)延时等待节点(DelayNode)
  • 作用:使流程暂停指定的毫秒/秒/分钟。常用于“等待物料到位”或“等待化学反应”。
  • 配置参数durationunit(SECONDS/MILLIS)。
  • 执行逻辑Thread.sleep(durationMs)异步定时回调(工业场景强烈建议异步,避免阻塞调度线程)。
  • 输出变量waitTimeelapsedSeconds
(4)条件判断节点(ConditionNode)
  • 作用:在节点内部执行复杂逻辑判断(注意:画布层有网关,此节点用于节点内部判定)。
  • 配置参数expression(如 ${temperature > 100 && pressure < 5})。
  • 执行逻辑:使用 Aviator/SpEL 解析表达式。
  • 输出变量conditionResult(Boolean)。
(5)循环工序节点(LoopNode)
  • 作用:对一组设备动作进行批量循环(如“拧紧5颗螺丝”)。将此逻辑封装在单个节点内,避免画布膨胀。
  • 配置参数loopCountiteratorVar(循环变量名)、subNodeId(被循环的子流程ID)。
  • 执行逻辑for (i=0; i<loopCount; i++) { ctx.setVariable(iteratorVar, i); executeSubNode(i); }
  • 输出变量loopIndexloopCompletedCount
(6)报警提示节点(AlarmNode)
  • 作用:当工艺异常时,向监控系统推送报警,并触发声光提示。
  • 配置参数alarmLevel(INFO/WARN/CRITICAL)、messagerecipients(接收人)。
  • 执行逻辑:调用 notificationService.sendAlarm(level, msg),同时记录到报警历史库。
(7)数据保存节点(DataSaveNode)
  • 作用:将工艺过程中的关键参数(温度、压力、良率)持久化到数据库或时序库(InfluxDB)。
  • 配置参数tableNamefieldMapping(上下文变量 ↔ 数据库字段)。
  • 执行逻辑jdbcTemplate.insert(tableName, mappedData)influxDB.write(point)
  • 输出变量savedRecordIdsaveTimestamp

四、软件流程图(节点执行生命周期)

4.1 节点通用执行流程(由抽象基类定义)

失败

通过

同步

异步

成功

异常

成功

异常

调度层调用 execute

参数校验
validateInputs

抛出参数异常
状态设为 FAILED

结束

执行前置钩子
(日志/权限检查)

doExecute 核心业务

是否为异步执行?

阻塞等待结果

提交异步任务
注册回调

执行结果

异步回调

执行结果

执行成功

捕获异常

写入输出变量
writeOutputs

执行后置钩子
(指标采集/审计)

节点状态设为 COMPLETED

返回调度层

结束

是否可重试?

抛出 RetryableException
调度层处理重试

抛出 NodeExecutionException
状态设为 FAILED

结束

4.2 具体节点示例:设备加热节点(HeaterNode)内部逻辑

doExecute 开始

读取配置参数
targetTemp, deviceId, tolerance, timeout

读取当前温度
hal.readRegister(tempSensor)

当前温度 >= 目标-容差?

跳过加热, 直接返回当前温度

结束

发送加热指令
hal.writeRegister(heater, ON)

进入循环

等待 5 秒

再次读取当前温度

温度 >= 目标-容差?

关闭加热器
hal.writeRegister(heater, OFF)

返回最终温度

超时?
elapsed > timeoutSec

抛出超时异常
加热失败

五、UML建模(详尽)

5.1 节点业务层核心类图(含HAL依赖)

实现

继承

继承

继承

继承

继承

继承

继承

管理

使用

依赖

依赖

依赖

依赖

依赖

依赖

«interface»

NodeExecutor

+boolean supports(NodeType type)

+void execute(ExecutionContext ctx, NodeEntity node)

«abstract»

AbstractNodeExecutor

-MetricsCollector metrics

-NotificationService notification

+void execute(ExecutionContext ctx, NodeEntity node) : [final]

#void validateInputs(ExecutionContext ctx, NodeEntity node)

#Object doExecute(ExecutionContext ctx, NodeEntity node) : [abstract]

#void writeOutputs(ExecutionContext ctx, NodeEntity node, Object result)

#void handleException(ExecutionContext ctx, NodeEntity node, Exception e)

#void recordMetrics(...)

DeviceControlNode

-IDeviceService deviceService

#Object doExecute(ExecutionContext ctx, NodeEntity node)

-void startDevice(String deviceId)

-void stopDevice(String deviceId)

IOReadWriteNode

-IIOService ioService

#Object doExecute(ExecutionContext ctx, NodeEntity node)

-Object readRegister(String deviceId, int address, DataType type)

-void writeRegister(String deviceId, int address, Object value)

DelayNode

-ITimerService timerService // 异步实现

#Object doExecute(ExecutionContext ctx, NodeEntity node)

ConditionNode

-IExpressionEvaluator evaluator

#Object doExecute(ExecutionContext ctx, NodeEntity node)

LoopNode

-NodeExecutorFactory subNodeFactory

#Object doExecute(ExecutionContext ctx, NodeEntity node)

-void executeSubNode(ExecutionContext ctx, String subNodeId, int loopIndex)

AlarmNode

-IAlarmService alarmService

#Object doExecute(ExecutionContext ctx, NodeEntity node)

DataSaveNode

-IDataPersistenceService persistenceService

#Object doExecute(ExecutionContext ctx, NodeEntity node)

-Map<String,Object> buildRecord(ExecutionContext ctx, NodeEntity node)

NodeExecutorFactory

-Map<NodeType, NodeExecutor> registry

+NodeExecutor getExecutor(NodeType type)

+void register(NodeType type, NodeExecutor executor)

+void registerDefaultExecutors()

«interface»

IDeviceService

+void start(String deviceId)

+void stop(String deviceId)

+DeviceStatus getStatus(String deviceId)

«interface»

IIOService

+Object readRegister(String deviceId, int address, DataType type)

+void writeRegister(String deviceId, int address, Object value)

+List<Object> readMultipleRegisters(...)

«interface»

IAlarmService

+void sendAlarm(AlarmLevel level, String message, String... recipients)

+void clearAlarm(String alarmId)

«interface»

IExpressionEvaluator

+Object evaluate(String expression, Map<String,Object> variables)

+boolean evaluateBoolean(String expression, Map<String,Object> variables)

«interface»

IDataPersistenceService

+long insert(String tableName, Map<String,Object> data)

+void insertBatch(String tableName, List<Map> data)

«interface»

ITimerService

+void schedule(Runnable task, long delayMs)

+void sleep(long ms) : // 模拟阻塞

«enumeration»

NodeType

START_EVENT

END_EVENT

USER_TASK

SERVICE_TASK

DEVICE_CONTROL

IO_READ_WRITE

DELAY

CONDITION

LOOP

ALARM

DATA_SAVE

«enumeration»

DataType

BOOL

INT16

UINT16

INT32

FLOAT32

FLOAT64

STRING

«enumeration»

AlarmLevel

INFO

WARN

CRITICAL

5.2 节点执行时序图(调度层 → 节点 → HAL)

实际PLC设备硬件抽象层(HAL)加热节点(HeaterNode)NodeExecutorFactory调度器(FlowRunner)实际PLC设备硬件抽象层(HAL)加热节点(HeaterNode)NodeExecutorFactory调度器(FlowRunner)alt[参数缺失]alt[温度 >= 148]loop[每5秒检测一次]alt[25 < 148 (目标-容差)]getExecutor(SERVICE_TASK)HeaterNode实例execute(ctx, nodeEntity)validateInputs()抛出校验异常doExecute(ctx, node)获取参数: targetTemp=150, deviceId=heater01readRegister(tempSensor, address=40001)Modbus Read返回当前温度: 2525℃writeRegister(heater01, 40002, 1) // 启动Modbus WriteACK启动成功readRegister(tempSensor)温度逐步上升(100, 120, 148)writeRegister(heater01, 40002, 0) // 停止Modbus WriteACKbreak loop返回最终温度 result=150writeOutputs(ctx, node, result)recordMetrics(耗时=32s)执行完成,返回结果将结果存入上下文变量: temp=150

六、通俗实例:芯片封装产线 —— FlowNode实战

6.1 场景节点拆解

我们以“芯片封装主流程”为例,将画布上的每个图形节点对应到具体的FlowNode实现类:

画布节点名称节点类型对应的FlowNode类配置参数示例执行动作
开始START_EVENT(内置)初始化上下文
加热平台预热SERVICE_TASKHeaterNodedeviceId=heater_01, targetTemp=150, tolerance=2, timeout=60调用HAL启动加热器,闭环控制到150℃
精密定位SERVICE_TASKIOReadWriteNodedeviceId=aligner_01, register=40010, value=${visionOffset}写入偏移量,驱动定位平台
芯片处理SERVICE_TASKDeviceControlNodedeviceId=handler_01, action=START启动处理单元执行封装
质量检测CONDITION(配合网关)ConditionNodeexpression=${qualityScore > 85}计算得分,输出布尔值给网关路由
数据保存SERVICE_TASKDataSaveNodetableName=production_batch, fields={batchNo, temp, score}将关键参数存入生产数据库
异常报警SERVICE_TASKAlarmNodelevel=CRITICAL, message=封装失败推送短信+大屏声光报警
延时等待SERVICE_TASKDelayNodeduration=10, unit=SECONDS等待胶水固化(非阻塞)

6.2 关键节点代码实现(精简版)

(1) 加热节点(HeaterNode)—— 闭环PID控制模拟
@Component
public class HeaterNode extends AbstractNodeExecutor {
    @Autowired private IIOService ioService;  // HAL注入

    @Override
    protected Object doExecute(ExecutionContext ctx, NodeEntity node) {
        // 1. 读取配置
        String deviceId = node.getConfig().getParam("deviceId").toString();
        double targetTemp = (double) node.getConfig().getParam("targetTemp");
        double tolerance = (double) node.getConfig().getParam("tolerance", 2.0);
        int timeoutSec = (int) node.getConfig().getParam("timeoutSec", 60);
        int tempRegister = 40001;
        int heaterRegister = 40002;

        // 2. 读取当前温度
        double currentTemp = (double) ioService.readRegister(deviceId, tempRegister, DataType.FLOAT32);
        if (currentTemp >= targetTemp - tolerance) {
            return currentTemp; // 已经够热,跳过
        }

        // 3. 启动加热
        ioService.writeRegister(deviceId, heaterRegister, 1);
        long startTime = System.currentTimeMillis();

        try {
            // 4. 闭环等待(轮询检测)
            while (true) {
                Thread.sleep(5000); // 5秒检测一次
                currentTemp = (double) ioService.readRegister(deviceId, tempRegister, DataType.FLOAT32);
                if (currentTemp >= targetTemp - tolerance) {
                    break;
                }
                long elapsed = (System.currentTimeMillis() - startTime) / 1000;
                if (elapsed > timeoutSec) {
                    throw new TimeoutException("加热超时,当前温度: " + currentTemp);
                }
            }
        } finally {
            // 5. 关闭加热器(不管成功还是超时,都要关)
            ioService.writeRegister(deviceId, heaterRegister, 0);
        }
        return currentTemp;
    }

    @Override
    protected void writeOutputs(ExecutionContext ctx, NodeEntity node, Object result) {
        ctx.setVariable("actualTemp", result);
        ctx.setVariable("heatingDuration", System.currentTimeMillis() - startTime);
    }
}
(2) 循环节点(LoopNode)—— 批量拧螺丝
@Component
public class LoopNode extends AbstractNodeExecutor {
    @Autowired private NodeExecutorFactory factory;

    @Override
    protected Object doExecute(ExecutionContext ctx, NodeEntity node) {
        int loopCount = (int) node.getConfig().getParam("loopCount");
        String subNodeId = node.getConfig().getParam("subNodeId").toString();
        String iteratorVar = node.getConfig().getParam("iteratorVar", "index");

        List<Integer> results = new ArrayList<>();
        for (int i = 1; i <= loopCount; i++) {
            ctx.setVariable(iteratorVar, i);  // 动态注入循环变量
            NodeEntity subNode = ctx.loadSubNode(subNodeId);
            NodeExecutor executor = factory.getExecutor(subNode.getNodeType());
            executor.execute(ctx, subNode);   // 递归执行子节点
            results.add(i);
        }
        return results;
    }
}

6.3 节点扩展机制(如何新增一种节点)

假设产线新购了一台 “AOI光学检测仪”,需要新增一个节点。

  1. 定义节点类型:在 NodeType 枚举中增加 AOI_INSPECTION
  2. 编写节点类
    public class AOIInspectionNode extends AbstractNodeExecutor {
        @Autowired private IAOIService aoiService;
        @Override protected Object doExecute(...) {
            String imagePath = ctx.getVariable("imagePath").toString();
            InspectionResult result = aoiService.inspect(imagePath);
            return result;
        }
    }
    
  3. 注册到工厂:在启动类中调用 factory.register(NodeType.AOI_INSPECTION, new AOIInspectionNode())
  4. 画布支持:在前端画布组件库中增加“AOI检测”的拖拽图标(图标关联 NodeType.AOI_INSPECTION)。
  5. 完成:工程师可在画布上拖拽使用,调度层无需任何改动。

七、项目文件结构(节点业务层专属)

engine-core/src/main/java/com/engine/node/
│
├── api/                                                # 对外接口
│   ├── NodeExecutor.java                               # 执行器接口
│   └── NodeType.java                                   # 节点类型枚举
│
├── base/                                               # 抽象基类与工具
│   ├── AbstractNodeExecutor.java                       # 模板方法抽象类
│   ├── NodeExecutionException.java                     # 自定义异常
│   ├── NodeExecutionContextWrapper.java                # 上下文包装器
│   └── validation/
│       ├── InputValidator.java                         # 校验器接口
│       └── DefaultInputValidator.java
│
├── factory/                                            # 工厂与注册中心
│   ├── NodeExecutorFactory.java
│   ├── NodeExecutorRegistry.java
│   └── DefaultNodeExecutorFactory.java
│
├── hal/                                                # 硬件抽象层接口(供节点依赖)
│   ├── IDeviceService.java
│   ├── IIOService.java
│   ├── IAlarmService.java
│   ├── IExpressionEvaluator.java
│   ├── IDataPersistenceService.java
│   └── ITimerService.java
│
├── impl/                                               # 具体节点实现(按功能分类)
│   ├── device/                                         # 设备控制类
│   │   ├── DeviceControlNode.java
│   │   ├── MotorSpeedNode.java
│   │   └── HeaterNode.java
│   │
│   ├── io/                                             # IO读写类
│   │   ├── IOReadWriteNode.java
│   │   └── AnalogOutputNode.java
│   │
│   ├── flow/                                           # 流程控制类
│   │   ├── DelayNode.java
│   │   ├── LoopNode.java
│   │   ├── SubProcessNode.java
│   │   └── WaitForEventNode.java
│   │
│   ├── logic/                                          # 逻辑判断类
│   │   ├── ConditionNode.java
│   │   ├── ScriptNode.java  (支持Groovy脚本)
│   │   └── SwitchCaseNode.java
│   │
│   ├── notify/                                         # 通知报警类
│   │   ├── AlarmNode.java
│   │   ├── EmailNotificationNode.java
│   │   └── MessageQueueNode.java
│   │
│   └── data/                                           # 数据持久化类
│       ├── DataSaveNode.java
│       ├── DataQueryNode.java
│       └── FileExportNode.java
│
├── hook/                                               # 节点钩子(AOP扩展)
│   ├── NodeExecutionHook.java
│   ├── LoggingHook.java                                # 自动打印日志
│   └── MetricsHook.java                                # 自动采集指标(耗时/次数)
│
└── support/                                            # 辅助工具
    ├── ExpressionEvaluatorFactory.java                 # 表达式引擎工厂
    ├── DataTypeMapper.java                             # 数据类型转换
    └── NodeConfigHelper.java                           # 配置解析辅助

八、开发机理与实施步骤

8.1 节点开发原则(SOLID最佳实践)

原则在FlowNode层中的应用
单一职责(SRP)一个节点类只做一件事(如加热节点只管加热,不负责数据保存)。
开闭原则(OCP)新增工艺节点无需修改调度层,只需新增类并注册。
里氏替换(LSP)所有子类都能替换父类(AbstractNodeExecutor),调度层只认接口。
依赖倒置(DIP)节点依赖HAL接口(如 IIOService),而非具体的Modbus驱动实现。
接口隔离(ISP)HAL接口拆分为 IDeviceServiceIIOServiceIAlarmService,而非大而全的万能接口。

8.2 节点测试策略(高可靠性保障)

工业节点必须经过严格的单元测试和模拟测试:

测试类型工具/方法验证内容
单元测试JUnit5 + MockitoMock HAL接口,验证节点逻辑分支(正常/异常/超时)。
集成测试Testcontainers + 模拟PLC连接仿真PLC(如 ModbusSimulator),验证真实协议交互。
混沌测试随机注入超时/网络断连验证节点的重试机制和异常捕获是否健壮。
@ExtendWith(MockitoExtension.class)
class HeaterNodeTest {
    @Mock IIOService ioService;
    @InjectMocks HeaterNode node;
    
    @Test
    void testHeaterReachesTarget() {
        // 模拟温度逐步上升
        when(ioService.readRegister(any(), eq(40001), any()))
            .thenReturn(25.0, 100.0, 148.0);  // mock 三次返回值
        Object result = node.doExecute(ctx, nodeEntity);
        verify(ioService).writeRegister(any(), eq(40002), eq(0)); // 最终关闭
        assertEquals(148.0, result);
    }
}

8.3 开发实施路线图(Sprint划分)

阶段周期核心任务产出物
Sprint 1:基础框架搭建2天定义 NodeExecutor 接口,实现 AbstractNodeExecutor 基类基类代码 + 模板方法实现
Sprint 2:核心节点开发(设备+IO+延时)5天实现 DeviceControlNodeIOReadWriteNodeDelayNode3个核心节点 + 单元测试
Sprint 3:逻辑与流程节点(条件+循环)4天集成Aviator表达式引擎,实现 ConditionNodeLoopNode表达式评估 + 循环逻辑
Sprint 4:数据与通知节点3天实现 DataSaveNode(JDBC)、AlarmNode(邮件/短信)数据库存储 + 通知集成
Sprint 5:工厂与动态注册3天实现 NodeExecutorFactory,支持Spring自动扫描注册工厂类 + 注解 @NodeType
Sprint 6:硬件抽象层(HAL)实现5天实现 Modbus/OPC UA 适配器,注入到节点HAL实现类 + 集成测试
Sprint 7:性能与监控2天增加节点执行耗时统计、熔断降级监控埋点 + Metrics上报

8.4 注解驱动的自动化注册(极简扩展方案)

为方便开发者新增节点,采用 Spring + 自定义注解 自动注册:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NodeType {
    com.engine.node.api.NodeType value();
}

// 使用方式
@Component
@NodeType(com.engine.node.api.NodeType.AOI_INSPECTION)
public class AOIInspectionNode extends AbstractNodeExecutor {
    // ...
}

// 自动注册逻辑
@Component
public class NodeExecutorAutoRegistrar implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired private NodeExecutorFactory factory;
    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        Map<String, Object> beans = event.getApplicationContext()
            .getBeansWithAnnotation(NodeType.class);
        beans.forEach((name, bean) -> {
            NodeType ann = bean.getClass().getAnnotation(NodeType.class);
            factory.register(ann.value(), (NodeExecutor) bean);
        });
    }
}

九、总结

节点业务层(FlowNode)是工业流程引擎的 “最后一公里”,它将抽象的流程调度转化为具体的设备动作、数据操作和逻辑判断。

设计精髓

  1. 模板方法统一形态:通过 AbstractNodeExecutor 固化执行生命周期(校验 → 执行 → 输出),确保所有节点行为一致。
  2. 依赖倒置解耦硬件:节点依赖HAL接口(IIOService 等),更换硬件供应商(从Modbus换成Profibus)只需替换HAL实现,节点代码零改动。
  3. 策略模式无限扩展:通过 NodeExecutorFactory 动态路由,新增一种工序只需写一个类加一个注解,完美契合工业现场“工艺千变万化”的需求。
  4. 分层测试保安全:通过Mock测试和仿真PLC测试,确保每个节点在接入真实设备前逻辑完备。

FlowNode层交付给工厂的价值

  • 工艺工程师只需拖拽节点,无需关心底层硬件驱动。
  • 设备厂商只需按照HAL接口规范开发适配器,即可接入系统。
  • 核心调度层(FlowRunner)永不变,保障了整个工业软件平台的稳定性长期可维护性

当FlowNode层与之前设计的数据模型层(静态定义)、画布渲染层(可视化编辑)、流程调度层(动态执行)组合在一起时,便构建了一个完整的、工业级的、支持热插拔业务逻辑的低代码自动化平台

于2024年4月-2025年9月期间,研究团队在贵州习水国家级自然保护区制定39条样线,涵盖灌木林、常绿阔叶林、针叶林、常绿落叶阔叶混交林、针阔混交林等不同植被类型,每条样线分春夏秋冬4个季节采集样品,用真菌采集软件记录经纬度、海拔、采集地点、时间、生境等信息,使用佳能相机(R6 mark Ⅱ)对大型真菌进行拍照,并采集标本,标本存放于贵州省生物研究所大型真菌标本馆(HGAMF)。 通过形态学初步鉴定,结合分子生物学最终鉴定,参考已]报道的中国毒蘑菇名录开展毒蘑菇的认定。 调查到保护区内有毒真菌7目25科64种,导致中毒的主要类型有急性肾衰竭型、神经精神型和胃肠炎型。最终形成贵州习水国家级自然保护区大型有毒真菌图片数据集,它由以下2个部分组成。 (1)附件1包含78张原始照片(.JPG),照片名字包括了大型有毒真菌的拉丁名和中文名,若无中文名的直接用拉丁名。 (2)附件2是一个压缩文件,包含了2张工作表,其中一张表是大型有毒真菌39条样线的信息,另一张表是大型有毒真菌的中毒类型。 照片采用佳能相机R6 mark Ⅱ拍摄,物种鉴定通过多种文献核实,并经两位以上专家鉴定确认。该数据集可为研究地及周边的普通人识别有毒大型真菌提供参考,通过及时的图片对比,能有效避免误采误食大型有毒真菌,同时为因误食大型真菌可能引发的身体损伤进行了总结,能为患者及时治疗提供参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千江明月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值