云计算百科
云计算领域专业知识百科平台

MES/ERP 场景多层级委托封装组件

MES/ERP 场景打造工序流程业务执行事件的多层级委托拼接封装WinForm 信息中间件

MES/ERP 工序流程业务执行事件封装 WinForm 中间件解决方案

本次为 MES/ERP 场景打造工序流程业务执行事件的多层级委托拼接封装WinForm 信息中间件,核心围绕工序流程的生命周期事件(初始化、启动、执行、完成、异常、跳转等),设计多结构拼接的委托函数体系(基础委托、组合委托、流程委托),实现业务逻辑与 UI 层、数据层完全解耦,同时提供 WinForm 可视化界面 + 后台服务化能力,适配 MES/ERP 的工序流程标准化、可配置、高扩展需求。

一、核心设计理念与架构

1. 核心痛点与解决方案

MES/ERP 工序流程痛点技术解决方案
工序流程事件零散,耦合性高 按工序生命周期封装标准化基础事件委托,覆盖全流程节点
不同产品 / 产线工序组合不同 设计多结构拼接委托(基础委托组合成工序委托,工序委托拼接成流程委托)
业务逻辑修改需改动大量代码 委托函数动态注册 / 注销 / 组合,支持配置化拼接工序流程
需同时支持可视化操作和后台运行 WinForm 界面 +后台服务线程池,界面与后台解耦,支持最小化到托盘后台运行
与 MES/ERP 数据交互复杂 内置标准化数据交互接口,适配 ERP 工单下发、MES 工序执行反馈

2. 整体架构(三层委托 + 三端解耦)

3. 委托函数体系(多结构拼接设计)

采用三级委托拼接,从原子操作到完整流程,支持无限组合,适配任意工序结构:

  • 基础事件委托:工序单节点的原子操作(如 PLC 指令发送、产量上报、日志记录),是最细粒度委托;
  • 工序组合委托:将多个基础委托按执行顺序 / 条件拼接,封装成单个工序的完整执行逻辑;
  • 流程拼接委托:将多个工序组合委托按工艺路线(串行 / 并行 / 跳转)拼接,封装成整条产线的工序流程逻辑。
  • 二、前置准备

    1. 环境与 NuGet 依赖

    • 开发环境:.NET Framework 4.8(WinForm 兼容性最优)、Visual Studio 2022+
    • 核心 NuGet 包:

      bash

      运行

      Install-Package Dapper # 轻量ORM,本地/ERP数据交互
      Install-Package System.Data.SQLite.Core # 本地工序/委托配置存储
      Install-Package Newtonsoft.Json # JSON序列化,工艺路线/委托参数解析
      Install-Package System.Configuration # 配置文件管理,ERP/MES接口配置

    2. 核心枚举(MES/ERP 工序流程标准化)

    封装 MES/ERP 通用的工序、流程、事件枚举,作为委托事件和业务执行的基础标识:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using Dapper;
    using System.Data.SQLite;
    using System.IO;
    using Newtonsoft.Json;

    #region MES/ERP核心枚举
    /// <summary>
    /// 工序执行状态(MES/ERP通用标准)
    /// </summary>
    public enum ProcessStatus
    {
    UnInitialized = 0, // 未初始化
    Ready = 1, // 就绪(可执行)
    Running = 2, // 执行中
    Completed = 3, // 执行完成
    Exception = 4, // 执行异常
    Paused = 5, // 暂停
    Skipped = 6 // 工序跳过
    }

    /// <summary>
    /// 工序流程事件类型(生命周期全节点,委托绑定核心)
    /// </summary>
    public enum ProcessFlowEventType
    {
    ProcessInit, // 工序初始化
    ProcessStart, // 工序启动
    ProcessExec, // 工序执行中(实时)
    ProcessComplete, // 工序完成
    ProcessException, // 工序异常
    ProcessRecover, // 异常恢复
    ProcessPause, // 工序暂停
    ProcessResume, // 工序恢复
    FlowStart, // 流程整体启动
    FlowComplete, // 流程整体完成
    FlowException // 流程整体异常
    }

    /// <summary>
    /// 委托拼接类型(控制多委托执行逻辑)
    /// </summary>
    public enum DelegateCombineType
    {
    Serial, // 串行执行(按注册顺序)
    Parallel, // 并行执行(同时触发)
    Conditional // 条件执行(满足前置条件才执行)
    }

    /// <summary>
    /// 工序流程执行模式
    /// </summary>
    public enum FlowExecuteMode
    {
    WinFormUI, // UI线程执行(带进度反馈)
    BackendService // 后台服务执行(无UI阻塞,推荐)
    }
    #endregion

    三、核心模型(MES/ERP 工序流程 + 委托封装)

    设计与 ERP/MES 无缝对接的业务模型,同时封装委托拼接的核心模型,支持委托的注册、组合、存储、执行:

    #region MES/ERP业务核心模型
    /// <summary>
    /// ERP工单信息(上游ERP下发,MES执行依据)
    /// </summary>
    public class ErpWorkOrder
    {
    public string OrderNo { get; set; } // 工单号(ERP唯一标识)
    public string ProductCode { get; set; } // 产品编码
    public string ProductName { get; set; } // 产品名称
    public int PlanQty { get; set; } // 计划产量
    public int CompleteQty { get; set; } // 完成产量
    public DateTime CreateTime { get; set; } = DateTime.Now; // 工单创建时间
    public string LineCode { get; set; } // 绑定产线编码
    }

    /// <summary>
    /// 工艺路线(ERP下发,定义工序执行顺序和参数)
    /// </summary>
    public class ProcessRoute
    {
    public string RouteId { get; set; } = Guid.NewGuid().ToString("N"); // 工艺路线ID
    public string OrderNo { get; set; } // 关联工单号
    public List<ProcessNode> ProcessNodes { get; set; } = new List<ProcessNode>(); // 工序节点列表
    public string LineCode { get; set; } // 产线编码
    public int CurrentProcessIndex { get; set; } = 0; // 当前执行工序索引
    }

    /// <summary>
    /// 工序节点(工艺路线最小单位,绑定委托组合)
    /// </summary>
    public class ProcessNode
    {
    public string ProcessId { get; set; } = Guid.NewGuid().ToString("N"); // 工序ID
    public string ProcessCode { get; set; } // 工序编码(MES/ERP标准化)
    public string ProcessName { get; set; } // 工序名称
    public int SortNo { get; set; } // 工序执行顺序
    public ProcessStatus Status { get; set; } = ProcessStatus.UnInitialized; // 工序状态
    public string DelegateCombineId { get; set; } // 绑定的委托组合ID
    public Dictionary<string, string> Params { get; set; } = new Dictionary<string, string>(); // 工序参数(如PLC地址、目标产量)
    public int CompleteQty { get; set; } = 0; // 本工序完成产量
    }
    #endregion

    #region 委托拼接核心模型
    /// <summary>
    /// 基础委托信息(原子操作,可独立执行/组合执行)
    /// </summary>
    public class BaseDelegateInfo
    {
    public string DelegateId { get; set; } = Guid.NewGuid().ToString("N"); // 委托ID
    public string DelegateName { get; set; } // 委托名称(如"PLC发送启动指令")
    public ProcessFlowEventType BindEvent { get; set; } // 绑定的工序流程事件
    public string MethodName { get; set; } // 委托绑定的方法名(反射/直接绑定)
    public Delegate DelegateFunc { get; set; } // 实际委托函数
    public Dictionary<string, string> Params { get; set; } = new Dictionary<string, string>(); // 委托执行参数
    }

    /// <summary>
    /// 委托组合信息(多个基础委托拼接成工序级逻辑)
    /// </summary>
    public class DelegateCombineInfo
    {
    public string CombineId { get; set; } = Guid.NewGuid().ToString("N"); // 组合ID(工序节点绑定)
    public string CombineName { get; set; } // 组合名称(如"预装工序完整执行逻辑")
    public DelegateCombineType CombineType { get; set; } // 拼接类型(串行/并行/条件)
    public List<BaseDelegateInfo> BaseDelegates { get; set; } = new List<BaseDelegateInfo>(); // 包含的基础委托
    public string ConditionalExpr { get; set; } = "true"; // 条件执行表达式(如"CompleteQty>0")
    }

    /// <summary>
    /// 流程委托拼接(多个工序组合委托拼接成产线流程逻辑)
    /// </summary>
    public class FlowDelegateCombineInfo
    {
    public string FlowCombineId { get; set; } = Guid.NewGuid().ToString("N"); // 流程组合ID
    public string FlowName { get; set; } // 流程名称(如"B产品总装流程")
    public string RouteId { get; set; } // 关联工艺路线ID
    public List<DelegateCombineInfo> ProcessCombines { get; set; } = new List<DelegateCombineInfo>(); // 工序组合委托列表
    public FlowExecuteMode ExecuteMode { get; set; } = FlowExecuteMode.BackendService; // 执行模式
    }

    /// <summary>
    /// 委托执行上下文(传递给所有委托,包含全量业务信息)
    /// </summary>
    public class DelegateExecuteContext
    {
    public ErpWorkOrder WorkOrder { get; set; } // 关联工单
    public ProcessRoute ProcessRoute { get; set; } // 关联工艺路线
    public ProcessNode CurrentProcess { get; set; } // 当前执行工序
    public ProcessFlowEventType CurrentEvent { get; set; } // 当前触发事件
    public CancellationToken Cts { get; set; } // 取消令牌(用于终止执行)
    public string ErrorMsg { get; set; } // 执行错误信息
    public bool IsSuccess { get; set; } = true; // 执行是否成功
    }
    #endregion

    四、核心委托定义(MES/ERP 工序流程专用)

    封装与工序流程事件强绑定的通用委托类型,覆盖无返回值 / 有返回值 / 异步执行三种场景,适配 MES/ERP 的实时性和复杂性要求:

    csharp

    运行

    #region MES/ERP工序流程专用委托定义
    /// <summary>
    /// 基础无返回值委托(最常用,如日志、PLC指令、状态更新)
    /// 入参:委托执行上下文
    /// </summary>
    /// <param name="context">委托执行上下文</param>
    public delegate void ProcessFlowBaseDelegate(DelegateExecuteContext context);

    /// <summary>
    /// 有返回值委托(如数据查询、参数校验、条件判断)
    /// 入参:委托执行上下文;返回值:执行结果(bool)
    /// </summary>
    /// <param name="context">委托执行上下文</param>
    /// <returns>执行结果</returns>
    public delegate bool ProcessFlowFuncDelegate(DelegateExecuteContext context);

    /// <summary>
    /// 异步委托(如耗时操作:PLC通信、ERP数据交互、设备校准)
    /// 入参:委托执行上下文+取消令牌;返回值:异步任务
    /// </summary>
    /// <param name="context">委托执行上下文</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>异步任务</returns>
    public delegate Task ProcessFlowAsyncDelegate(DelegateExecuteContext context, CancellationToken cancellationToken);
    #endregion

    五、核心服务封装(委托管理 + 流程引擎 + 后台服务)

    1. 本地数据库助手(SQLite)

    封装工序流程 / 委托配置 / 工单信息的本地存储,实现配置持久化,程序重启后委托拼接关系、工序状态不丢失:

    csharp

    运行

    /// <summary>
    /// SQLite本地数据库助手(单例)- MES/ERP工序委托专用
    /// 存储:工单、工艺路线、工序节点、委托配置、委托组合
    /// </summary>
    public sealed class MesErpDbHelper
    {
    // 单例实例
    private static readonly Lazy<MesErpDbHelper> _instance = new Lazy<MesErpDbHelper>(() => new MesErpDbHelper());
    public static MesErpDbHelper Instance => _instance.Value;

    // 数据库路径(程序目录下MesErpProcessDelegate.db)
    private readonly string _dbPath;
    private readonly string _connStr;
    private readonly object _dbLock = new object();

    private MesErpDbHelper()
    {
    _dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MesErpProcessDelegate.db");
    // 开启WAL模式,提升后台服务多线程读写性能
    _connStr = $"Data Source={_dbPath};Version=3;Journal Mode=WAL;Pooling=true;Max Pool Size=20;";
    // 初始化数据库表结构
    InitDbTables();
    }

    /// <summary>
    /// 初始化数据库表(不存在则创建)
    /// </summary>
    private void InitDbTables()
    {
    lock (_dbLock)
    {
    if (!File.Exists(_dbPath)) SQLiteConnection.CreateFile(_dbPath);

    using (var conn = new SQLiteConnection(_connStr))
    {
    conn.Open();
    // 1. ERP工单表
    conn.Execute($@"CREATE TABLE IF NOT EXISTS ErpWorkOrder (
    OrderNo TEXT PRIMARY KEY, ProductCode TEXT, ProductName TEXT, PlanQty INT,
    CompleteQty INT, CreateTime DATETIME, LineCode TEXT)");

    // 2. 工艺路线表
    conn.Execute($@"CREATE TABLE IF NOT EXISTS ProcessRoute (
    RouteId TEXT PRIMARY KEY, OrderNo TEXT, LineCode TEXT, CurrentProcessIndex INT,
    ProcessNodes TEXT, FOREIGN KEY(OrderNo) REFERENCES ErpWorkOrder(OrderNo))");

    // 3. 委托组合表(工序级)
    conn.Execute($@"CREATE TABLE IF NOT EXISTS DelegateCombine (
    CombineId TEXT PRIMARY KEY, CombineName TEXT, CombineType INT,
    ConditionalExpr TEXT, BaseDelegates TEXT)");

    // 4. 流程委托组合表(产线级)
    conn.Execute($@"CREATE TABLE IF NOT EXISTS FlowDelegateCombine (
    FlowCombineId TEXT PRIMARY KEY, FlowName TEXT, RouteId TEXT,
    ExecuteMode INT, ProcessCombines TEXT,
    FOREIGN KEY(RouteId) REFERENCES ProcessRoute(RouteId))");

    LogHelper.Info("MES/ERP本地数据库初始化完成,路径:" + _dbPath);
    }
    }
    }

    #region 通用CRUD方法
    public T QueryFirst<T>(string sql, object param = null)
    {
    lock (_dbLock)
    {
    using (var conn = new SQLiteConnection(_connStr))
    {
    conn.Open();
    return conn.QueryFirstOrDefault<T>(sql, param);
    }
    }
    }

    public List<T> QueryList<T>(string sql, object param = null)
    {
    lock (_dbLock)
    {
    using (var conn = new SQLiteConnection(_connStr))
    {
    conn.Open();
    return conn.Query<T>(sql, param).ToList();
    }
    }
    }

    public int Execute(string sql, object param = null)
    {
    lock (_dbLock)
    {
    using (var conn = new SQLiteConnection(_connStr))
    {
    conn.Open();
    using (var tran = conn.BeginTransaction())
    {
    try
    {
    var rows = conn.Execute(sql, param, tran);
    tran.Commit();
    return rows;
    }
    catch (Exception ex)
    {
    tran.Rollback();
    LogHelper.Error("SQL执行失败:" + sql, ex);
    throw;
    }
    }
    }
    }
    }

    /// <summary>
    /// 保存对象到数据库(JSON序列化复杂字段)
    /// </summary>
    public void SaveObject<T>(string tableName, string keyCol, T model)
    {
    var jsonProps = model.GetType().GetProperties()
    .Where(p => p.PropertyType.IsGenericType || p.PropertyType == typeof(Dictionary<string, string>))
    .Select(p => p.Name).ToList();

    var sqlCols = string.Join(",", model.GetType().GetProperties()
    .Where(p => !jsonProps.Contains(p.Name))
    .Select(p => p.Name));
    var sqlVals = string.Join(",", model.GetType().GetProperties()
    .Where(p => !jsonProps.Contains(p.Name))
    .Select(p => $"@{p.Name}"));

    // 先删除原有数据
    Execute($"DELETE FROM {tableName} WHERE {keyCol} = @{keyCol}", model);
    // 插入新数据(复杂字段JSON序列化)
    var insertSql = $"INSERT INTO {tableName} ({sqlCols},{string.Join(",", jsonProps)}) VALUES ({sqlVals},{string.Join(",", jsonProps.Select(p => $"@{p}"))})";
    var param = new DynamicParameters(model);
    foreach (var prop in jsonProps)
    {
    var val = model.GetType().GetProperty(prop).GetValue(model);
    param.Add(prop, JsonConvert.SerializeObject(val));
    }
    Execute(insertSql, param);
    }

    /// <summary>
    /// 读取对象从数据库(JSON反序列化复杂字段)
    /// </summary>
    public T GetObject<T>(string tableName, string keyCol, string keyVal)
    {
    var sql = $"SELECT * FROM {tableName} WHERE {keyCol} = @KeyVal";
    var dynamicModel = QueryFirst<dynamic>(sql, new { KeyVal = keyVal });
    if (dynamicModel == null) return default;

    var model = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(dynamicModel));
    var jsonProps = typeof(T).GetProperties()
    .Where(p => p.PropertyType.IsGenericType || p.PropertyType == typeof(Dictionary<string, string>))
    .Select(p => p.Name).ToList();

    foreach (var prop in jsonProps)
    {
    var jsonVal = dynamicModel.GetType().GetProperty(prop)?.GetValue(dynamicModel)?.ToString();
    if (!string.IsNullOrEmpty(jsonVal))
    {
    var val = JsonConvert.DeserializeObject(jsonVal, typeof(T).GetProperty(prop).PropertyType);
    typeof(T).GetProperty(prop).SetValue(model, val);
    }
    }
    return model;
    }
    #endregion
    }

    2. 委托管理中心(核心,多结构拼接 + 动态注册)

    实现基础委托的注册 / 注销、委托的组合拼接、委托的持久化 / 加载,是整个中间件的委托大脑,支持可视化配置和动态修改:

    /// <summary>
    /// 工序流程委托管理中心(单例)- MES/ERP专用
    /// 核心:基础委托注册、多结构拼接、委托加载/保存、委托执行
    /// </summary>
    public sealed class ProcessDelegateManager
    {
    // 单例实例
    private static readonly Lazy<ProcessDelegateManager> _instance = new Lazy<ProcessDelegateManager>(() => new ProcessDelegateManager());
    public static ProcessDelegateManager Instance => _instance.Value;

    // 本地数据库助手
    private readonly MesErpDbHelper _dbHelper = MesErpDbHelper.Instance;
    // 基础委托注册表(内存缓存,提高执行效率)
    private readonly Dictionary<string, BaseDelegateInfo> _baseDelegateDict = new Dictionary<string, BaseDelegateInfo>();
    // 委托组合注册表
    private readonly Dictionary<string, DelegateCombineInfo> _combineDelegateDict = new Dictionary<string, DelegateCombineInfo>();
    // 流程委托组合注册表
    private readonly Dictionary<string, FlowDelegateCombineInfo> _flowCombineDelegateDict = new Dictionary<string, FlowDelegateCombineInfo>();
    // 线程安全锁
    private readonly object _delegateLock = new object();

    private ProcessDelegateManager()
    {
    // 程序启动时加载本地存储的委托配置
    LoadAllDelegatesFromDb();
    // 注册MES/ERP通用基础委托(原子操作,可直接使用)
    RegisterDefaultBaseDelegates();
    }

    #region 基础委托操作(注册/注销/加载/保存)
    /// <summary>
    /// 注册基础委托(支持任意类型委托:Base/Func/Async)
    /// </summary>
    public void RegisterBaseDelegate(BaseDelegateInfo delegateInfo)
    {
    lock (_delegateLock)
    {
    if (_baseDelegateDict.ContainsKey(delegateInfo.DelegateId))
    _baseDelegateDict[delegateInfo.DelegateId] = delegateInfo;
    else
    _baseDelegateDict.Add(delegateInfo.DelegateId, delegateInfo);
    LogHelper.Info($"基础委托注册成功:{delegateInfo.DelegateName}(ID:{delegateInfo.DelegateId})");
    }
    }

    /// <summary>
    /// 注销基础委托
    /// </summary>
    public void UnRegisterBaseDelegate(string delegateId)
    {
    lock (_delegateLock)
    {
    if (_baseDelegateDict.ContainsKey(delegateId))
    {
    _baseDelegateDict.Remove(delegateId);
    LogHelper.Info($"基础委托注销成功:ID={delegateId}");
    }
    }
    }

    /// <summary>
    /// 注册MES/ERP通用默认基础委托(开箱即用)
    /// </summary>
    private void RegisterDefaultBaseDelegates()
    {
    // 1. 工序状态更新委托(绑定ProcessStart事件)
    RegisterBaseDelegate(new BaseDelegateInfo
    {
    DelegateName = "工序状态更新为执行中",
    BindEvent = ProcessFlowEventType.ProcessStart,
    DelegateFunc = new ProcessFlowBaseDelegate(DefaultDelegates.UpdateProcessStatusToRunning)
    });

    // 2. 产量上报委托(绑定ProcessComplete事件)
    RegisterBaseDelegate(new BaseDelegateInfo
    {
    DelegateName = "工序产量上报到本地库",
    BindEvent = ProcessFlowEventType.ProcessComplete,
    DelegateFunc = new ProcessFlowBaseDelegate(DefaultDelegates.ReportProcessYieldToDb)
    });

    // 3. ERP工单进度更新委托(异步,绑定FlowComplete事件)
    RegisterBaseDelegate(new BaseDelegateInfo
    {
    DelegateName = "异步更新ERP工单进度",
    BindEvent = ProcessFlowEventType.FlowComplete,
    DelegateFunc = new ProcessFlowAsyncDelegate(DefaultDelegates.AsyncUpdateErpOrderProgress)
    });

    // 4. 工序异常日志委托(绑定ProcessException事件)
    RegisterBaseDelegate(new BaseDelegateInfo
    {
    DelegateName = "工序异常日志记录",
    BindEvent = ProcessFlowEventType.ProcessException,
    DelegateFunc = new ProcessFlowBaseDelegate(DefaultDelegates.RecordProcessExceptionLog)
    });
    }
    #endregion

    #region 委托组合操作(工序级/流程级拼接)
    /// <summary>
    /// 拼接工序级委托组合(多个基础委托按规则组合)
    /// </summary>
    public DelegateCombineInfo CombineProcessDelegates(string combineName, DelegateCombineType combineType, List<BaseDelegateInfo> baseDelegates, string conditionalExpr = "true")
    {
    lock (_delegateLock)
    {
    var combine = new DelegateCombineInfo
    {
    CombineName = combineName,
    CombineType = combineType,
    BaseDelegates = baseDelegates,
    ConditionalExpr = conditionalExpr
    };
    _combineDelegateDict.Add(combine.CombineId, combine);
    // 持久化到本地库
    _dbHelper.SaveObject("DelegateCombine", "CombineId", combine);
    LogHelper.Info($"工序委托组合拼接成功:{combineName}(包含{baseDelegates.Count}个基础委托)");
    return combine;
    }
    }

    /// <summary>
    /// 拼接产线级流程委托(多个工序组合委托拼接成完整流程)
    /// </summary>
    public FlowDelegateCombineInfo CombineFlowDelegates(string flowName, string routeId, List<DelegateCombineInfo> processCombines, FlowExecuteMode executeMode)
    {
    lock (_delegateLock)
    {
    var flowCombine = new FlowDelegateCombineInfo
    {
    FlowName = flowName,
    RouteId = routeId,
    ProcessCombines = processCombines,
    ExecuteMode = executeMode
    };
    _flowCombineDelegateDict.Add(flowCombine.FlowCombineId, flowCombine);
    // 持久化到本地库
    _dbHelper.SaveObject("FlowDelegateCombine", "FlowCombineId", flowCombine);
    LogHelper.Info($"流程委托拼接成功:{flowName}(包含{processCombines.Count}个工序组合)");
    return flowCombine;
    }
    }
    #endregion

    #region 委托加载/保存
    /// <summary>
    /// 从本地库加载所有委托配置
    /// </summary>
    private void LoadAllDelegatesFromDb()
    {
    // 加载工序委托组合
    var processCombines = _dbHelper.QueryList<dynamic>("SELECT CombineId FROM DelegateCombine");
    foreach (var item in processCombines)
    {
    var combine = _dbHelper.GetObject<DelegateCombineInfo>("DelegateCombine", "CombineId", item.CombineId);
    _combineDelegateDict.Add(combine.CombineId, combine);
    }

    // 加载流程委托组合
    var flowCombines = _dbHelper.QueryList<dynamic>("SELECT FlowCombineId FROM FlowDelegateCombine");
    foreach (var item in flowCombines)
    {
    var flowCombine = _dbHelper.GetObject<FlowDelegateCombineInfo>("FlowDelegateCombine", "FlowCombineId", item.FlowCombineId);
    _flowCombineDelegateDict.Add(flowCombine.FlowCombineId, flowCombine);
    }

    LogHelper.Info($"从本地库加载委托配置完成:工序组合{_combineDelegateDict.Count}个,流程组合{_flowCombineDelegateDict.Count}个");
    }

    /// <summary>
    /// 根据工序ID获取绑定的委托组合
    /// </summary>
    public DelegateCombineInfo GetProcessCombineByProcessId(string processId, ProcessRoute route)
    {
    var processNode = route.ProcessNodes.FirstOrDefault(p => p.ProcessId == processId);
    if (processNode == null || string.IsNullOrEmpty(processNode.DelegateCombineId))
    throw new KeyNotFoundException($"工序{processId}未绑定委托组合");

    return _combineDelegateDict.TryGetValue(processNode.DelegateCombineId, out var combine) ? combine : null;
    }
    #endregion

    #region 委托执行(核心,支持多类型/多拼接规则)
    /// <summary>
    /// 执行委托组合(自动识别串行/并行/条件,支持异步)
    /// </summary>
    public async Task ExecuteCombineDelegate(DelegateCombineInfo combine, DelegateExecuteContext context)
    {
    if (combine == null || !combine.BaseDelegates.Any()) return;
    // 条件执行:先判断条件是否满足
    if (!EvaluateConditionalExpr(combine.ConditionalExpr, context))
    {
    LogHelper.Info($"委托组合{combine.CombineName}条件不满足,跳过执行");
    return;
    }

    LogHelper.Info($"开始执行委托组合:{combine.CombineName}(类型:{combine.CombineType})");
    switch (combine.CombineType)
    {
    case DelegateCombineType.Serial:
    // 串行执行:按顺序执行每个基础委托
    foreach (var baseDel in combine.BaseDelegates)
    {
    if (context.Cts.IsCancellationRequested) break;
    await ExecuteBaseDelegate(baseDel, context);
    }
    break;
    case DelegateCombineType.Parallel:
    // 并行执行:所有委托同时执行,等待全部完成
    var parallelTasks = new List<Task>();
    foreach (var baseDel in combine.BaseDelegates)
    {
    if (context.Cts.IsCancellationRequested) break;
    parallelTasks.Add(ExecuteBaseDelegate(baseDel, context));
    }
    await Task.WhenAll(parallelTasks);
    break;
    case DelegateCombineType.Conditional:
    // 条件执行:每个委托独立判断条件(简化版,可扩展)
    foreach (var baseDel in combine.BaseDelegates)
    {
    if (context.Cts.IsCancellationRequested) break;
    var expr = baseDel.Params.ContainsKey("ConditionalExpr") ? baseDel.Params["ConditionalExpr"] : "true";
    if (EvaluateConditionalExpr(expr, context))
    await ExecuteBaseDelegate(baseDel, context);
    }
    break;
    }
    }

    /// <summary>
    /// 执行基础委托(自动识别委托类型:Base/Func/Async)
    /// </summary>
    private async Task ExecuteBaseDelegate(BaseDelegateInfo baseDel, DelegateExecuteContext context)
    {
    if (baseDel.DelegateFunc == null)
    {
    LogHelper.Warn($"基础委托{baseDel.DelegateName}未绑定函数,跳过");
    return;
    }

    try
    {
    LogHelper.Info($"执行基础委托:{baseDel.DelegateName}");
    // 按委托类型分发执行
    if (baseDel.DelegateFunc is ProcessFlowBaseDelegate baseFunc)
    {
    baseFunc(context); // 同步无返回值
    }
    else if (baseDel.DelegateFunc is ProcessFlowFuncDelegate func)
    {
    context.IsSuccess = func(context); // 同步有返回值
    }
    else if (baseDel.DelegateFunc is ProcessFlowAsyncDelegate asyncFunc)
    {
    await asyncFunc(context, context.Cts); // 异步执行
    }
    else
    {
    throw new NotSupportedException($"不支持的委托类型:{baseDel.DelegateFunc.GetType().Name}");
    }
    }
    catch (Exception ex)
    {
    context.IsSuccess = false;
    context.ErrorMsg = ex.Message;
    LogHelper.Error($"基础委托{baseDel.DelegateName}执行失败:{ex.Message}", ex);
    throw;
    }
    }

    /// <summary>
    /// 解析条件执行表达式(简化版,可扩展为表达式引擎)
    /// </summary>
    private bool EvaluateConditionalExpr(string expr, DelegateExecuteContext context)
    {
    // 基础实现:支持简单表达式如"true"、"CompleteQty>0"、"Status==2"
    if (expr.Trim().ToLower() == "true") return true;
    if (expr.Trim().ToLower() == "false") return false;

    // 扩展点:集成表达式引擎(如NCalc)支持复杂表达式
    try
    {
    // 示例:解析CompleteQty>0
    if (expr.Contains("CompleteQty>"))
    {
    var qty = int.Parse(expr.Split('>')[1].Trim());
    return context.CurrentProcess.CompleteQty > qty;
    }
    return true;
    }
    catch
    {
    return false;
    }
    }
    #endregion
    }

    /// <summary>
    /// MES/ERP通用默认委托实现(原子操作,开箱即用)
    /// </summary>
    public static class DefaultDelegates
    {
    private static readonly MesErpDbHelper _dbHelper = MesErpDbHelper.Instance;

    /// <summary>
    /// 基础委托:更新工序状态为执行中
    /// </summary>
    public static void UpdateProcessStatusToRunning(DelegateExecuteContext context)
    {
    context.CurrentProcess.Status = ProcessStatus.Running;
    // 持久化工序状态
    _dbHelper.SaveObject("ProcessRoute", "RouteId", context.ProcessRoute);
    LogHelper.Info($"工序{context.CurrentProcess.ProcessCode}状态更新为:执行中");
    }

    /// <summary>
    /// 基础委托:工序产量上报到本地库
    /// </summary>
    public static void ReportProcessYieldToDb(DelegateExecuteContext context)
    {
    context.WorkOrder.CompleteQty = context.ProcessRoute.ProcessNodes.Sum(p => p.CompleteQty);
    // 更新工单和工艺路线
    _dbHelper.SaveObject("ErpWorkOrder", "OrderNo", context.WorkOrder);
    _dbHelper.SaveObject("ProcessRoute", "RouteId", context.ProcessRoute);
    LogHelper.Info($"工序{context.CurrentProcess.ProcessCode}产量上报:{context.CurrentProcess.CompleteQty}件,工单累计:{context.WorkOrder.CompleteQty}件");
    }

    /// <summary>
    /// 异步委托:更新ERP工单进度(模拟ERP接口调用,实际替换为真实ERP交互代码)
    /// </summary>
    public static async Task AsyncUpdateErpOrderProgress(DelegateExecuteContext context, CancellationToken ct)
    {
    // 模拟ERP接口网络请求(耗时2秒)
    await Task.Delay(2000, ct);
    // 实际场景:调用ERP的WebAPI/WS更新工单进度
    var progress = (double)context.WorkOrder.CompleteQty / context.WorkOrder.PlanQty * 100;
    LogHelper.Info($"异步更新ERP工单{context.WorkOrder.OrderNo}进度:{progress:F2}%");
    }

    /// <summary>
    /// 基础委托:记录工序异常日志
    /// </summary>
    public static void RecordProcessExceptionLog(DelegateExecuteContext context)
    {
    LogHelper.Error($"【工序异常】{context.CurrentProcess.ProcessCode}:{context.ErrorMsg},工单:{context.WorkOrder.OrderNo}");
    context.CurrentProcess.Status = ProcessStatus.Exception;
    _dbHelper.SaveObject("ProcessRoute", "RouteId", context.ProcessRoute);
    }
    }

    3. 工序流程引擎(委托执行 + 流程控制)

    实现工序流程的生命周期管理,触发对应事件并分发到委托管理中心执行拼接后的委托,支持串行 / 并行工序、异常处理、流程暂停 / 恢复 / 终止:

    csharp

    运行

    /// <summary>
    /// MES/ERP工序流程引擎(单例)
    /// 核心:流程生命周期管理、事件触发、委托分发、流程控制(暂停/恢复/终止)
    /// </summary>
    public sealed class ProcessFlowEngine
    {
    // 单例实例
    private static readonly Lazy<ProcessFlowEngine> _instance = new Lazy<ProcessFlowEngine>(() => new ProcessFlowEngine());
    public static ProcessFlowEngine Instance => _instance.Value;

    // 依赖服务
    private readonly ProcessDelegateManager _delegateManager = ProcessDelegateManager.Instance;
    private readonly MesErpDbHelper _dbHelper = MesErpDbHelper.Instance;
    // 正在执行的流程(流程ID->取消令牌源)
    private readonly Dictionary<string, CancellationTokenSource> _runningFlows = new Dictionary<string, CancellationTokenSource>();
    private readonly object _flowLock = new object();

    #region 流程生命周期操作(启动/暂停/恢复/终止)
    /// <summary>
    /// 启动工序流程(核心入口)
    /// </summary>
    /// <param name="flowCombine">流程委托组合</param>
    /// <param name="workOrder">ERP工单</param>
    /// <param name="route">工艺路线</param>
    /// <returns>执行任务</returns>
    public async Task StartFlowAsync(FlowDelegateCombineInfo flowCombine, ErpWorkOrder workOrder, ProcessRoute route)
    {
    lock (_flowLock)
    {
    if (_runningFlows.ContainsKey(flowCombine.FlowCombineId))
    throw new InvalidOperationException($"流程{flowCombine.FlowName}已在执行中");

    // 创建取消令牌源(用于暂停/终止流程)
    var cts = new CancellationTokenSource();
    _runningFlows.Add(flowCombine.FlowCombineId, cts);
    // 后台执行流程,不阻塞UI
    Task.Run(async () => await ExecuteFlowAsync(flowCombine, workOrder, route, cts.Token), cts.Token);
    }

    LogHelper.Info($"流程{flowCombine.FlowName}启动成功(工单:{workOrder.OrderNo}),执行模式:{flowCombine.ExecuteMode}");
    }

    /// <summary>
    /// 暂停工序流程
    /// </summary>
    public void PauseFlow(string flowCombineId)
    {
    lock (_flowLock)
    {
    if (_runningFlows.TryGetValue(flowCombineId, out var cts))
    {
    cts.Cancel();
    LogHelper.Info($"流程{flowCombineId}已暂停");
    }
    }
    }

    /// <summary>
    /// 终止工序流程
    /// </summary>
    public void StopFlow(string flowCombineId)
    {
    lock (_flowLock)
    {
    if (_runningFlows.TryGetValue(flowCombineId, out var cts))
    {
    cts.Cancel();
    _runningFlows.Remove(flowCombineId);
    cts.Dispose();
    LogHelper.Info($"流程{flowCombineId}已终止");
    }
    }
    }
    #endregion

    #region 流程执行核心逻辑
    /// <summary>
    /// 执行完整工序流程(触发事件+执行委托+流程控制)
    /// </summary>
    private async Task ExecuteFlowAsync(FlowDelegateCombineInfo flowCombine, ErpWorkOrder workOrder, ProcessRoute route, CancellationToken ct)
    {
    var flowContext = new DelegateExecuteContext
    {
    WorkOrder = workOrder,
    ProcessRoute = route,
    CurrentEvent = ProcessFlowEventType.FlowStart,
    Cts = ct
    };

    try
    {
    // 触发流程启动事件
    await TriggerFlowEvent(ProcessFlowEventType.FlowStart, flowContext);

    // 按工艺路线顺序执行每个工序
    while (route.CurrentProcessIndex < route.ProcessNodes.Count && !ct.IsCancellationRequested)
    {
    var currentProcess = route.ProcessNodes[route.CurrentProcessIndex];
    flowContext.CurrentProcess = currentProcess;
    flowContext.CurrentEvent = ProcessFlowEventType.ProcessInit;

    // 执行单个工序的完整生命周期
    await ExecuteSingleProcessAsync(currentProcess, flowCombine, flowContext, ct);

    // 工序执行成功,切换到下一个工序
    if (currentProcess.Status == ProcessStatus.Completed || currentProcess.Status == ProcessStatus.Skipped)
    {
    route.CurrentProcessIndex++;
    _dbHelper.SaveObject("ProcessRoute", "RouteId", route);
    }
    // 工序异常,终止流程
    else if (currentProcess.Status == ProcessStatus.Exception)
    {
    throw new Exception($"工序{currentProcess.ProcessCode}执行异常,流程终止");
    }
    }

    // 所有工序执行完成,触发流程完成事件
    if (!ct.IsCancellationRequested)
    {
    flowContext.CurrentEvent = ProcessFlowEventType.FlowComplete;
    await TriggerFlowEvent(ProcessFlowEventType.FlowComplete, flowContext);
    LogHelper.Info($"流程{flowCombine.FlowName}执行完成(工单:{workOrder.OrderNo}),完成产量:{workOrder.CompleteQty}件");
    }
    }
    catch (OperationCanceledException)
    {
    LogHelper.Info($"流程{flowCombine.FlowName}已暂停/终止(工单:{workOrder.OrderNo})");
    }
    catch (Exception ex)
    {
    flowContext.CurrentEvent = ProcessFlowEventType.FlowException;
    flowContext.IsSuccess = false;
    flowContext.ErrorMsg = ex.Message;
    await TriggerFlowEvent(ProcessFlowEventType.FlowException, flowContext);
    LogHelper.Error($"流程{flowCombine.FlowName}执行失败(工单:{workOrder.OrderNo}):{ex.Message}", ex);
    }
    finally
    {
    lock (_flowLock)
    {
    if (_runningFlows.ContainsKey(flowCombine.FlowCombineId))
    {
    _runningFlows[flowCombine.FlowCombineId].Dispose();
    _runningFlows.Remove(flowCombine.FlowCombineId);
    }
    }
    }
    }

    /// <summary>
    /// 执行单个工序的完整生命周期(初始化->启动->执行->完成)
    /// </summary>
    private async Task ExecuteSingleProcessAsync(ProcessNode process, FlowDelegateCombineInfo flowCombine, DelegateExecuteContext context, CancellationToken ct)
    {
    // 1. 工序初始化事件
    context.CurrentEvent = ProcessFlowEventType.ProcessInit;
    process.Status = ProcessStatus.Ready;
    await TriggerFlowEvent(ProcessFlowEventType.ProcessInit, context);
    await Task.Delay(500, ct); // 模拟工序初始化耗时

    // 2. 工序启动事件
    context.CurrentEvent = ProcessFlowEventType.ProcessStart;
    await TriggerFlowEvent(ProcessFlowEventType.ProcessStart, context);

    // 3. 执行工序绑定的委托组合(核心)
    var processCombine = _delegateManager.GetProcessCombineByProcessId(process.ProcessId, context.ProcessRoute);
    await _delegateManager.ExecuteCombineDelegate(processCombine, context);

    // 4. 工序完成事件
    if (context.IsSuccess && !ct.IsCancellationRequested)
    {
    context.CurrentEvent = ProcessFlowEventType.ProcessComplete;
    process.Status = ProcessStatus.Completed;
    await TriggerFlowEvent(ProcessFlowEventType.ProcessComplete, context);
    }
    }

    /// <summary>
    /// 触发工序流程事件(分发到委托管理中心执行绑定的委托)
    /// </summary>
    private async Task TriggerFlowEvent(ProcessFlowEventType eventType, DelegateExecuteContext context)
    {
    context.CurrentEvent = eventType;
    LogHelper.Info($"触发工序流程事件:{eventType}(工序:{context.CurrentProcess?.ProcessCode ?? "流程整体"})");

    // 执行所有绑定该事件的基础委托(简化版,可扩展为按流程/工序过滤)
    var allBaseDelegates = _delegateManager.GetType().GetField("_baseDelegateDict", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
    .GetValue(_delegateManager) as Dictionary<string, BaseDelegateInfo>;

    var bindDelegates = allBaseDelegates?.Values.Where(d => d.BindEvent == eventType).ToList() ?? new List<BaseDelegateInfo>();
    foreach (var del in bindDelegates)
    {
    if (context.Cts.IsCancellationRequested) break;
    await _delegateManager.ExecuteBaseDelegate(del, context);
    }
    }
    #endregion
    }

    4. 后台服务管理器(WinForm 无 UI 阻塞执行)

    实现WinForm 界面与后台服务解耦,支持流程后台运行、最小化到系统托盘、后台日志记录、状态实时同步到 UI:

    csharp

    运行

    /// <summary>
    /// MES/ERP工序流程后台服务管理器
    /// 核心:无UI阻塞执行、系统托盘、后台线程池、UI状态同步
    /// </summary>
    public class ProcessFlowBackendService
    {
    // 后台服务线程池
    private readonly TaskFactory _backendTaskFactory;
    // UI状态更新委托(跨线程安全)
    public Action<string, string> UiStatusUpdateAction;
    // 后台日志委托
    public Action<string> BackendLogAction;

    public ProcessFlowBackendService()
    {
    // 配置后台线程池(无同步上下文,避免UI阻塞)
    var scheduler = TaskScheduler.Default;
    _backendTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, scheduler);
    }

    /// <summary>
    /// 后台执行工序流程(无UI阻塞)
    /// </summary>
    public void RunFlowInBackend(FlowDelegateCombineInfo flowCombine, ErpWorkOrder workOrder, ProcessRoute route)
    {
    _backendTaskFactory.StartNew(async () =>
    {
    try
    {
    UiStatusUpdateAction?.Invoke(flowCombine.FlowName, "后台执行中");
    BackendLogAction?.Invoke($"后台服务启动流程:{flowCombine.FlowName}(工单:{workOrder.OrderNo})");
    // 调用流程引擎启动流程
    await ProcessFlowEngine.Instance.StartFlowAsync(flowCombine, workOrder, route);
    UiStatusUpdateAction?.Invoke(flowCombine.FlowName, "执行完成");
    }
    catch (Exception ex)
    {
    UiStatusUpdateAction?.Invoke(flowCombine.FlowName, "执行失败");
    BackendLogAction?.Invoke($"后台流程执行失败:{ex.Message}");
    LogHelper.Error("后台服务执行异常", ex);
    }
    }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
    }

    /// <summary>
    /// 注册系统托盘(WinForm最小化到后台运行)
    /// </summary>
    public void RegisterSystemTray(Form mainForm, NotifyIcon notifyIcon)
    {
    // 初始化托盘图标
    notifyIcon.Icon = SystemIcons.Application;
    notifyIcon.Text = "MES/ERP工序流程中间件";
    notifyIcon.Visible = true;

    // 托盘右键菜单
    var contextMenu = new ContextMenuStrip();
    contextMenu.Items.Add("显示界面", null, (s, e) => { mainForm.Show(); mainForm.WindowState = FormWindowState.Normal; });
    contextMenu.Items.Add("退出", null, (s, e) => { Application.Exit(); });
    notifyIcon.ContextMenuStrip = contextMenu;

    // 窗体最小化到托盘
    mainForm.Resize += (s, e) =>
    {
    if (mainForm.WindowState == FormWindowState.Minimized)
    {
    mainForm.Hide();
    notifyIcon.ShowBalloonTip(2000, "提示", "MES/ERP工序流程中间件已最小化到后台运行", ToolTipIcon.Info);
    }
    };

    // 托盘左键双击显示界面
    notifyIcon.MouseDoubleClick += (s, e) =>
    {
    if (e.Button == MouseButtons.Left)
    {
    mainForm.Show();
    mainForm.WindowState = FormWindowState.Normal;
    }
    };

    BackendLogAction?.Invoke("系统托盘注册成功,支持后台运行");
    }
    }

    5. 通用日志助手

    封装文件日志 + 控制台日志,支持分级日志(Info/Warn/Error),方便 MES/ERP 工序流程的问题排查:

    csharp

    运行

    /// <summary>
    /// MES/ERP通用日志助手
    /// </summary>
    public static class LogHelper
    {
    private static readonly string _logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MesErpLogs");

    static LogHelper()
    {
    if (!Directory.Exists(_logDir)) Directory.CreateDirectory(_logDir);
    }

    /// <summary>
    /// 信息日志
    /// </summary>
    public static void Info(string msg)
    {
    WriteLog("INFO", msg);
    }

    /// <summary>
    /// 警告日志
    /// </summary>
    public static void Warn(string msg)
    {
    WriteLog("WARN", msg);
    }

    /// <summary>
    /// 错误日志
    /// </summary>
    public static void Error(string msg, Exception ex = null)
    {
    var fullMsg = ex == null ? msg : $"{msg}\\r\\n异常信息:{ex.Message}\\r\\n堆栈跟踪:{ex.StackTrace}";
    WriteLog("ERROR", fullMsg);
    }

    /// <summary>
    /// 写入日志文件
    /// </summary>
    private static void WriteLog(string level, string msg)
    {
    var logContent = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{level}] {msg}\\r\\n";
    // 控制台输出
    Console.WriteLine(logContent);
    // 文件输出(按日期分文件)
    var logFile = Path.Combine(_logDir, $"MesErp_ProcessDelegate_{DateTime.Now:yyyyMMdd}.log");
    try
    {
    File.AppendAllText(logFile, logContent, System.Text.Encoding.UTF8);
    }
    catch
    {
    // 忽略日志写入异常,避免影响主流程
    }
    }
    }

    六、WinForm 界面实现(可视化中间件)

    设计简洁易用的 WinForm 界面,实现ERP 工单导入 / 工艺路线配置 / 委托拼接 / 流程启动 / 后台运行 / 状态监控 / 日志查看全功能,支持跨线程 UI 状态同步:

    /// <summary>
    /// MES/ERP工序流程委托中间件主界面
    /// 核心功能:工单管理、工艺路线配置、委托拼接、流程执行、后台运行、状态监控、日志查看
    /// </summary>
    public partial class MainForm : Form
    {
    #region 服务实例
    private readonly MesErpDbHelper _dbHelper = MesErpDbHelper.Instance;
    private readonly ProcessDelegateManager _delegateManager = ProcessDelegateManager.Instance;
    private readonly ProcessFlowEngine _flowEngine = ProcessFlowEngine.Instance;
    private readonly ProcessFlowBackendService _backendService = new ProcessFlowBackendService();
    private readonly NotifyIcon _notifyIcon = new NotifyIcon();
    #endregion

    #region 界面控件
    private ComboBox cboWorkOrder;
    private Button btnAddOrder;
    private Button btnAddProcess;
    private Button btnCombineDelegate;
    private Button btnStartFlow;
    private Button btnPauseFlow;
    private Button btnStopFlow;
    private DataGridView dgvProcessRoute;
    private DataGridView dgvRunningFlow;
    private TextBox txtLog;
    private Label lblBackendStatus;
    private ComboBox cboExecuteMode;
    #endregion

    public MainForm()
    {
    InitializeComponent();
    // 初始化UI
    InitMainUI();
    // 注册后台服务委托(UI状态同步+日志)
    RegisterBackendServiceDelegates();
    // 注册系统托盘(后台运行)
    _backendService.RegisterSystemTray(this, _notifyIcon);
    // 加载初始数据
    LoadInitData();
    }

    #region UI初始化
    private void InitMainUI()
    {
    // 窗体基础设置
    this.Text = "MES/ERP工序流程委托中间件服务";
    this.Size = new Size(1000, 600);
    this.StartPosition = FormStartPosition.CenterScreen;
    this.FormClosing += (s, e) => { _notifyIcon.Visible = false; };

    // 控件初始化
    cboWorkOrder = new ComboBox { Location = new Point(10, 10), Size = new Size(200, 23), DropDownStyle = ComboBoxStyle.DropDownList };
    btnAddOrder = new Button { Location = new Point(220, 10), Size = new Size(80, 23), Text = "新增工单" };
    btnAddProcess = new Button { Location = new Point(310, 10), Size = new Size(80, 23), Text = "新增工序" };
    btnCombineDelegate = new Button { Location = new Point(400, 10), Size = new Size(100, 23), Text = "委托拼接" };
    cboExecuteMode = new ComboBox { Location = new Point(510, 10), Size = new Size(120, 23), DataSource = Enum.GetValues(typeof(FlowExecuteMode)) };
    btnStartFlow = new Button { Location = new Point(640, 10), Size = new Size(80, 23), Text = "启动流程", BackColor = Color.LightGreen };
    btnPauseFlow = new Button { Location = new Point(730, 10), Size = new Size(80, 23), Text = "暂停流程", BackColor = Color.Orange };
    btnStopFlow = new Button { Location = new Point(820, 10), Size = new Size(80, 23), Text = "终止流程", BackColor = Color.LightPink };
    lblBackendStatus = new Label { Location = new Point(910, 10), Size = new Size(80, 23), Text = "后台状态:空闲" };

    // 工艺路线网格
    dgvProcessRoute = new DataGridView { Location = new Point(10, 40), Size = new Size(980, 200), ReadOnly = true, AllowUserToAddRows = false };
    dgvProcessRoute.Columns.AddRange(new[]
    {
    new DataGridViewTextBoxColumn { Name = "ProcessCode", HeaderText = "工序编码", Width = 100 },
    new DataGridViewTextBoxColumn { Name = "ProcessName", HeaderText = "工序名称", Width = 100 },
    new DataGridViewTextBoxColumn { Name = "SortNo", HeaderText = "执行顺序", Width = 80 },
    new DataGridViewTextBoxColumn { Name = "Status", HeaderText = "工序状态", Width = 100 },
    new DataGridViewTextBoxColumn { Name = "CompleteQty", HeaderText = "完成产量", Width = 100 },
    new DataGridViewTextBoxColumn { Name = "DelegateCombine", HeaderText = "委托组合", Width = 200 },
    new DataGridViewTextBoxColumn { Name = "Params", HeaderText = "工序参数", Width = 300 }
    });

    // 运行流程网格
    dgvRunningFlow = new DataGridView { Location = new Point(10, 250), Size = new Size(980, 100), ReadOnly = true, AllowUserToAddRows = false };
    dgvRunningFlow.Columns.AddRange(new[]
    {
    new DataGridViewTextBoxColumn { Name = "FlowName", HeaderText = "流程名称", Width = 200 },
    new DataGridViewTextBoxColumn { Name = "OrderNo", HeaderText = "关联工单", Width = 150 },
    new DataGridViewTextBoxColumn { Name = "ExecuteMode", HeaderText = "执行模式", Width = 150 },
    new DataGridViewTextBoxColumn { Name = "Status", HeaderText = "流程状态", Width = 150 },
    new DataGridViewTextBoxColumn { Name = "CurrentProcess", HeaderText = "当前工序", Width = 200 },
    new DataGridViewTextBoxColumn { Name = "CompleteQty", HeaderText = "完成产量", Width = 130 }
    });

    // 日志文本框
    txtLog = new TextBox { Location = new Point(10, 360), Size = new Size(980, 220), Multiline = true, ReadOnly = true, ScrollBars = ScrollBars.Vertical };

    // 绑定控件事件
    btnAddOrder.Click += BtnAddOrder_Click;
    btnAddProcess.Click += BtnAddProcess_Click;
    btnCombineDelegate.Click += BtnCombineDelegate_Click;
    btnStartFlow.Click += BtnStartFlow_Click;
    btnPauseFlow.Click += BtnPauseFlow_Click;
    btnStopFlow.Click += BtnStopFlow_Click;
    cboWorkOrder.SelectedIndexChanged += CboWorkOrder_SelectedIndexChanged;

    // 添加控件到窗体
    this.Controls.AddRange(new Control[] { cboWorkOrder, btnAddOrder, btnAddProcess, btnCombineDelegate, cboExecuteMode,
    btnStartFlow, btnPauseFlow, btnStopFlow, lblBackendStatus, dgvProcessRoute, dgvRunningFlow, txtLog });
    }
    #endregion

    #region 委托注册(跨线程UI同步)
    private void RegisterBackendServiceDelegates()
    {
    // 后台服务状态更新
    _backendService.UiStatusUpdateAction = (flowName, status) =>
    {
    if (this.InvokeRequired)
    this.Invoke(new Action<string, string>((f, s) => { lblBackendStatus.Text = $"后台状态:{s}"; }), flowName, status);
    else
    lblBackendStatus.Text = $"后台状态:{status}";
    };

    // 后台服务日志同步到UI
    _backendService.BackendLogAction = (log) =>
    {
    if (txtLog.InvokeRequired)
    txtLog.Invoke(new Action<string>(AddLogToUi), log);
    else
    AddLogToUi(log);
    };

    // 全局日志同步到UI
    typeof(LogHelper).GetField("_logDir", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
    LogHelper.Info("UI委托注册成功,支持跨线程状态同步");
    }

    /// <summary>
    /// 添加日志到UI文本框
    /// </summary>
    private void AddLogToUi(string log)
    {
    txtLog.AppendText($"[{DateTime.Now:HH:mm:ss.fff}] {log}\\r\\n");
    txtLog.ScrollToCaret();
    }
    #endregion

    #region 数据加载
    private void LoadInitData()
    {
    // 加载ERP工单到下拉框
    var orders = _dbHelper.QueryList<ErpWorkOrder>("SELECT * FROM ErpWorkOrder");
    cboWorkOrder.DataSource = orders;
    cboWorkOrder.DisplayMember = "OrderNo";
    cboWorkOrder.ValueMember = "OrderNo";

    AddLogToUi("初始数据加载完成,工单数量:" + orders.Count);
    }

    /// <summary>
    /// 加载选中工单的工艺路线
    /// </summary>
    private void LoadProcessRoute(string orderNo)
    {
    var route = _dbHelper.GetObject<ProcessRoute>("ProcessRoute", "OrderNo", orderNo);
    if (route == null) return;

    dgvProcessRoute.Rows.Clear();
    foreach (var process in route.ProcessNodes.OrderBy(p => p.SortNo))
    {
    var row = dgvProcessRoute.Rows.Add();
    dgvProcessRoute.Rows[row].Cells["ProcessCode"].Value = process.ProcessCode;
    dgvProcessRoute.Rows[row].Cells["ProcessName"].Value = process.ProcessName;
    dgvProcessRoute.Rows[row].Cells["SortNo"].Value = process.SortNo;
    dgvProcessRoute.Rows[row].Cells["Status"].Value = process.Status;
    dgvProcessRoute.Rows[row].Cells["CompleteQty"].Value = process.CompleteQty;
    dgvProcessRoute.Rows[row].Cells["DelegateCombine"].Value = process.DelegateCombineId;
    dgvProcessRoute.Rows[row].Cells["Params"].Value = JsonConvert.SerializeObject(process.Params);
    }
    }
    #endregion

    #region 界面事件处理(核心功能)
    /// <summary>
    /// 新增ERP工单
    /// </summary>
    private void BtnAddOrder_Click(object sender, EventArgs e)
    {
    var order = new ErpWorkOrder
    {
    OrderNo = "WO-" + DateTime.Now.ToString("yyyyMMddHHmmss"),
    ProductCode = "PROD-" + new Random().Next(1000, 9999),
    ProductName = "测试产品-" + new Random().Next(100, 999),
    PlanQty = 1000,
    LineCode = "LINE-001"
    };
    _dbHelper.SaveObject("ErpWorkOrder", "OrderNo", order);
    // 初始化工艺路线
    var route = new ProcessRoute { OrderNo = order.OrderNo, LineCode = order.LineCode };
    _dbHelper.SaveObject("ProcessRoute", "RouteId", route);

    LoadInitData();
    AddLogToUi($"新增ERP工单成功:{order.OrderNo},产品:{order.ProductName},计划产量:{order.PlanQty}件");
    }

    /// <summary>
    /// 新增工序节点
    /// </summary>
    private void BtnAddProcess_Click(object sender, EventArgs e)
    {
    if (cboWorkOrder.SelectedValue == null)
    {
    MessageBox.Show("请先选择工单!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
    }

    var orderNo = cboWorkOrder.SelectedValue.ToString();
    var route = _dbHelper.GetObject<ProcessRoute>("ProcessRoute", "OrderNo", orderNo);
    if (route == null) route = new ProcessRoute { OrderNo = orderNo };

    var process = new ProcessNode
    {
    ProcessCode = "PROC-" + new Random().Next(1000, 9999),
    ProcessName = "测试工序-" + new Random().Next(100, 999),
    SortNo = route.ProcessNodes.Count + 1,
    Status = ProcessStatus.UnInitialized
    };
    route.ProcessNodes.Add(process);
    _dbHelper.SaveObject("ProcessRoute", "RouteId", route);

    LoadProcessRoute(orderNo);
    AddLogToUi($"新增工序成功:{process.ProcessCode},工序名称:{process.ProcessName},执行顺序:{process.SortNo}");
    }

    /// <summary>
    /// 委托拼接(基础委托->工序组合委托)
    /// </summary>
    private void BtnCombineDelegate_Click(object sender, EventArgs e)
    {
    if (cboWorkOrder.SelectedValue == null || dgvProcessRoute.SelectedRows.Count == 0)
    {
    MessageBox.Show("请选择工单和工序!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
    }

    var orderNo = cboWorkOrder.SelectedValue.ToString();
    var processRow = dgvProcessRoute.SelectedRows[0];
    var processCode = processRow.Cells["ProcessCode"].Value.ToString();
    var route = _dbHelper.GetObject<ProcessRoute>("ProcessRoute", "OrderNo", orderNo);
    var process = route.ProcessNodes.First(p => p.ProcessCode == processCode);

    // 获取所有基础委托(简化版,实际可做可视化选择)
    var allBaseDelegates = _delegateManager.GetType().GetField("_baseDelegateDict", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
    .GetValue(_delegateManager) as Dictionary<string, BaseDelegateInfo>;
    var selectBaseDelegates = allBaseDelegates.Values.Take(2).ToList(); // 选择前2个基础委托

    // 拼接工序级委托组合(串行执行)
    var combine = _delegateManager.CombineProcessDelegates($"{processCode}委托组合", DelegateCombineType.Serial, selectBaseDelegates);
    // 绑定到工序节点
    process.DelegateCombineId = combine.CombineId;
    _dbHelper.SaveObject("ProcessRoute", "RouteId", route);

    LoadProcessRoute(orderNo);
    AddLogToUi($"工序{processCode}委托拼接成功,绑定组合ID:{combine.CombineId},包含{selectBaseDelegates.Count}个基础委托");
    }

    /// <summary>
    /// 启动工序流程(UI/后台模式可选)
    /// </summary>
    private void BtnStartFlow_Click(object sender, EventArgs e)
    {
    if (cboWorkOrder.SelectedValue == null || dgvProcessRoute.Rows.Count == 0)
    {
    MessageBox.Show("请先配置工单和工序!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
    }

    var orderNo = cboWorkOrder.SelectedValue.ToString();
    var order = _dbHelper.GetObject<ErpWorkOrder>("ErpWorkOrder", "OrderNo", orderNo);
    var route = _dbHelper.GetObject<ProcessRoute>("ProcessRoute", "OrderNo", orderNo);
    var executeMode = (FlowExecuteMode)cboExecuteMode.SelectedValue;

    // 拼接产线级流程委托
    var processCombines = route.ProcessNodes.Where(p => !string.IsNullOrEmpty(p.DelegateCombineId))
    .Select(p => _delegateManager.GetProcessCombineByProcessId(p.ProcessId, route)).ToList();
    var flowCombine = _delegateManager.CombineFlowDelegates($"{orderNo}流程", route.RouteId, processCombines, executeMode);

    // 执行流程(UI模式/后台模式)
    if (executeMode == FlowExecuteMode.WinFormUI)
    {
    _flowEngine.StartFlowAsync(flowCombine, order, route);
    AddLogToUi($"UI模式启动流程:{flowCombine.FlowName},工单:{orderNo}");
    }
    else
    {
    _backendService.RunFlowInBackend(flowCombine, order, route);
    AddLogToUi($"后台模式启动流程:{flowCombine.FlowName},工单:{orderNo},后台服务已接管");
    }

    // 更新运行流程网格
    UpdateRunningFlowGrid(flowCombine, order, route);
    }

    /// <summary>
    /// 暂停流程
    /// </summary>
    private void BtnPauseFlow_Click(object sender, EventArgs e)
    {
    if (dgvRunningFlow.SelectedRows.Count == 0)
    {
    MessageBox.Show("请选择运行中的流程!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
    }

    var flowName = dgvRunningFlow.SelectedRows[0].Cells["FlowName"].Value.ToString();
    var flowCombine = _delegateManager.GetType().GetField("_flowCombineDelegateDict", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
    .GetValue(_delegateManager) as Dictionary<string, FlowDelegateCombineInfo>;
    var flow = flowCombine.Values.First(f => f.FlowName == flowName);

    _flowEngine.PauseFlow(flow.FlowCombineId);
    AddLogToUi($"暂停流程:{flowName},流程ID:{flow.FlowCombineId}");
    }

    /// <summary>
    /// 终止流程
    /// </summary>
    private void BtnStopFlow_Click(object sender, EventArgs e)
    {
    if (dgvRunningFlow.SelectedRows.Count == 0)
    {
    MessageBox.Show("请选择运行中的流程!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
    }

    var flowName = dgvRunningFlow.SelectedRows[0].Cells["FlowName"].Value.ToString();
    var flowCombine = _delegateManager.GetType().GetField("_flowCombineDelegateDict", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
    .GetValue(_delegateManager) as Dictionary<string, FlowDelegateCombineInfo>;
    var flow = flowCombine.Values.First(f => f.FlowName == flowName);

    _flowEngine.StopFlow(flow.FlowCombineId);
    AddLogToUi($"终止流程:{flowName},流程ID:{flow.FlowCombineId}");
    dgvRunningFlow.Rows.Remove(dgvRunningFlow.SelectedRows[0]);
    }

    /// <summary>
    /// 工单选择变更,加载对应工艺路线
    /// </summary>
    private void CboWorkOrder_SelectedIndexChanged(object sender, EventArgs e)
    {
    if (cboWorkOrder.SelectedValue != null)
    LoadProcessRoute(cboWorkOrder.SelectedValue.ToString());
    }
    #endregion

    #region UI辅助方法
    /// <summary>
    /// 更新运行流程网格
    /// </summary>
    private void UpdateRunningFlowGrid(FlowDelegateCombineInfo flowCombine, ErpWorkOrder order, ProcessRoute route)
    {
    var row = dgvRunningFlow.Rows.Add();
    dgvRunningFlow.Rows[row].Cells["FlowName"].Value = flowCombine.FlowName;
    dgvRunningFlow.Rows[row].Cells["OrderNo"].Value = order.OrderNo;
    dgvRunningFlow.Rows[row].Cells["ExecuteMode"].Value = flowCombine.ExecuteMode;
    dgvRunningFlow.Rows[row].Cells["Status"].Value = "执行中";
    dgvRunningFlow.Rows[row].Cells["CurrentProcess"].Value = route.ProcessNodes[route.CurrentProcessIndex].ProcessCode;
    dgvRunningFlow.Rows[row].Cells["CompleteQty"].Value = order.CompleteQty;
    }
    #endregion

    // 设计器代码(自动生成,无需手动编写)
    private void InitializeComponent()
    {
    this.SuspendLayout();
    this.ClientSize = new System.Drawing.Size(1000, 600);
    this.Name = "MainForm";
    this.Text = "MES/ERP工序流程委托中间件服务";
    this.ResumeLayout(false);
    }
    }

    // 程序入口
    static class Program
    {
    [STAThread]
    static void Main()
    {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
    }
    }

    七、部署与使用说明

    1. 部署步骤

  • 创建项目:在 Visual Studio 2022 中创建「Windows 窗体应用(.NET Framework 4.8)」项目;
  • 安装 NuGet 包:执行前置准备中的 NuGet 安装命令,安装所有依赖;
  • 添加代码:将上述所有代码按类拆分到对应文件中(或直接复制到 Program.cs,删除原有代码);
  • 编译项目:生成 EXE 可执行文件(默认在 bin/Debug/ 下);
  • 直接运行:无需安装数据库 / ERP/MES 环境,运行 EXE 即可,程序会自动创建本地数据库和日志目录。
  • 2. 核心功能操作流程

  • 新增 ERP 工单:点击「新增工单」,程序自动生成标准化 ERP 工单(包含工单号、产品、计划产量);
  • 新增工序节点:选择工单,点击「新增工序」,生成工序节点并添加到工艺路线;
  • 委托拼接:选择工序,点击「委托拼接」,程序自动将通用默认基础委托拼接成工序级组合委托并绑定到工序;
  • 选择执行模式:下拉框选择「WinFormUI」(带 UI 进度反馈)或「BackendService」(后台无 UI 阻塞运行);
  • 启动流程:点击「启动流程」,程序自动拼接产线级流程委托并执行,日志实时同步到 UI;
  • 流程控制:点击「暂停流程 / 终止流程」,可对运行中的流程进行管控;
  • 后台运行:将窗体最小化,程序自动隐藏到系统托盘,后台继续执行流程。
  • 3. 与实际 MES/ERP 对接扩展

  • ERP 工单导入:修改BtnAddOrder_Click方法,添加 ERP 接口调用代码(如 WebAPI/WS/SQL),实现从实际 ERP 系统导入工单;
  • MES 数据交互:扩展DefaultDelegates类,添加 MES 接口委托(如 MES 工序状态上报、MES 产量同步);
  • 设备对接:在基础委托中添加 PLC / 传感器 / 机器人通信代码(如S7.Net、Modbus、OPC UA),实现工序执行的设备控制;
  • 复杂表达式:集成 NCalc/EL 表达式引擎,替换EvaluateConditionalExpr方法,支持复杂的委托条件执行表达式;
  • 可视化委托配置:新增委托配置窗体,实现基础委托的可视化选择、参数配置、拼接规则设置。
  • 八、核心优势与总结

    核心优势

  • 委托多结构拼接:独创「基础委托 + 工序组合委托 + 流程拼接委托」三级体系,支持任意工序流程结构,适配 MES/ERP 的个性化工艺路线;
  • 业务与 UI 完全解耦:委托函数封装所有业务逻辑,UI 层仅负责展示和操作,修改业务逻辑无需改动 UI 代码;
  • 后台服务化能力:支持 WinForm UI 执行和后台无 UI 阻塞执行,最小化到系统托盘,适配产线 7*24 小时运行需求;
  • 配置持久化:基于 SQLite 本地数据库,委托拼接关系、工单、工艺路线、工序状态全持久化,程序重启不丢失;
  • 高扩展性:默认提供 MES/ERP 通用基础委托,支持自定义基础委托注册、委托组合规则修改、设备 / 系统接口扩展;
  • 线程安全:全流程加锁 + 异步执行 + 取消令牌,支持多流程并发执行,无资源竞争和 UI 阻塞问题。
  • 关键点回顾

  • 围绕MES/ERP 工序流程生命周期封装标准化事件委托,覆盖从流程启动到完成的全节点;
  • 采用三级委托拼接设计,实现原子操作到完整流程的无限组合,解决工序流程配置化问题;
  • 设计后台服务管理器,实现 WinForm 界面与后台执行解耦,支持系统托盘后台运行;
  • 基于SQLite 本地数据库实现配置和状态持久化,降低部署依赖,适配产线边缘节点;
  • 提供开箱即用的通用基础委托,同时支持自定义委托注册,快速对接实际 MES/ERP/ 设备系统。
  • 该中间件完全适配 MES/ERP 的工序流程业务场景,可直接作为独立的工序执行中间件使用,也可集成到现有 MES/ERP 系统中,作为工序流程执行的核心引擎。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » MES/ERP 场景多层级委托封装组件
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!