MES/ERP 场景打造工序流程业务执行事件的多层级委托拼接封装WinForm 信息中间件
MES/ERP 工序流程业务执行事件封装 WinForm 中间件解决方案
本次为 MES/ERP 场景打造工序流程业务执行事件的多层级委托拼接封装WinForm 信息中间件,核心围绕工序流程的生命周期事件(初始化、启动、执行、完成、异常、跳转等),设计多结构拼接的委托函数体系(基础委托、组合委托、流程委托),实现业务逻辑与 UI 层、数据层完全解耦,同时提供 WinForm 可视化界面 + 后台服务化能力,适配 MES/ERP 的工序流程标准化、可配置、高扩展需求。
一、核心设计理念与架构
1. 核心痛点与解决方案
| 工序流程事件零散,耦合性高 | 按工序生命周期封装标准化基础事件委托,覆盖全流程节点 |
| 不同产品 / 产线工序组合不同 | 设计多结构拼接委托(基础委托组合成工序委托,工序委托拼接成流程委托) |
| 业务逻辑修改需改动大量代码 | 委托函数动态注册 / 注销 / 组合,支持配置化拼接工序流程 |
| 需同时支持可视化操作和后台运行 | WinForm 界面 +后台服务线程池,界面与后台解耦,支持最小化到托盘后台运行 |
| 与 MES/ERP 数据交互复杂 | 内置标准化数据交互接口,适配 ERP 工单下发、MES 工序执行反馈 |
2. 整体架构(三层委托 + 三端解耦)

3. 委托函数体系(多结构拼接设计)
采用三级委托拼接,从原子操作到完整流程,支持无限组合,适配任意工序结构:
二、前置准备
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. 部署步骤
2. 核心功能操作流程
3. 与实际 MES/ERP 对接扩展
八、核心优势与总结
核心优势
关键点回顾
该中间件完全适配 MES/ERP 的工序流程业务场景,可直接作为独立的工序执行中间件使用,也可集成到现有 MES/ERP 系统中,作为工序流程执行的核心引擎。
网硕互联帮助中心






评论前必须登录!
注册