MES/ERP 系统开发面向产线工站流程层级的 WinForm 中间件功能界面组件,核心适配 “多指令端 / 应用调度模式”,解决 MES/ERP 在工站级生产执行、数据同步、多端指令协同的核心问题,实现 ERP 计划层与 MES 执行层在工站维度的闭环管控。以下是完整的设计与实现方案,包含核心架构、业务逻辑、WinForm 界面适配及可直接落地的代码示例。
一、 组件核心定位与架构设计
1. 核心定位(MES/ERP 工站层级适配)
| 多指令端 | ERP 计划端(工单下发 / 计划调整)、MES 工站端(任务执行 / 数据采集)、MES 质控端(质检录入 / 判定)、MES 物料端(物料配送 / 消耗)、ERP 库存端(库存扣减 / 同步) | 跨系统指令隔离、实时数据同步、批量指令处理 |
| 应用调度端 | MES/ERP 工站调度中台(工单拆解、工站排程、多端指令路由、数据稽核、状态同步) | 计划 – 执行闭环、工站数据采集、跨系统异常处理 |
| 核心目标 | 打通 ERP 计划层与 MES 执行层在工站维度的数据流、指令流,实现工站级生产全流程管控 | 工站任务精准下发、执行数据实时回传、跨系统状态一致 |
2. 整体架构(WinForm 中间件适配)
| 界面层 | WinForm 功能组件(工单拆解面板、工站执行监控台、多端指令中心、数据稽核面板) | .NET Framework 4.8 + DevExpress(推荐) |
| 业务逻辑层 | MES/ERP 指令路由服务、工站任务调度服务、跨系统数据同步服务、工站数据采集服务 | 设计模式(适配器、观察者、中介者) |
| 集成层 | ERP 接口适配器、MES 接口适配器、工站设备接口适配器 | WebAPI + WCF + 串口通信 |
| 数据访问层 | ERP 工单 DAO、MES 工站任务 DAO、多端指令日志 DAO、工站采集数据 DAO | SQL Server + Dapper + 读写分离 |
| 基础支撑层 | 跨线程工具、日志组件、缓存组件、权限控制、异常重试 | Log4net + MemoryCache + Polly |
3. 核心数据模型(MES/ERP 工站层级扩展)
csharp
运行
// ERP工单模型(计划层)
public class ERPWorkOrder
{
/// <summary>
/// ERP工单编号
/// </summary>
public string WorkOrderNo { get; set; }
/// <summary>
/// 产品编码
/// </summary>
public string ProductCode { get; set; }
/// <summary>
/// 工单数量
/// </summary>
public decimal OrderQty { get; set; }
/// <summary>
/// 计划开始时间
/// </summary>
public DateTime PlanStartTime { get; set; }
/// <summary>
/// 计划结束时间
/// </summary>
public DateTime PlanEndTime { get; set; }
/// <summary>
/// 工单状态:待下发/已下发/执行中/已完成/已取消
/// </summary>
public string OrderStatus { get; set; }
/// <summary>
/// 所属车间/产线
/// </summary>
public string WorkshopCode { get; set; }
/// <summary>
/// ERP系统编码
/// </summary>
public string ERPSystemCode { get; set; }
}
// MES工站任务模型(执行层)
public class MESStationTask
{
/// <summary>
/// MES任务ID
/// </summary>
public int TaskId { get; set; }
/// <summary>
/// 关联ERP工单编号
/// </summary>
public string WorkOrderNo { get; set; }
/// <summary>
/// 工站编码
/// </summary>
public string StationCode { get; set; }
/// <summary>
/// 产线编码
/// </summary>
public string LineCode { get; set; }
/// <summary>
/// 产品编码
/// </summary>
public string ProductCode { get; set; }
/// <summary>
/// 任务数量
/// </summary>
public decimal TaskQty { get; set; }
/// <summary>
/// 计划开始时间
/// </summary>
public DateTime PlanStartTime { get; set; }
/// <summary>
/// 计划结束时间
/// </summary>
public DateTime PlanEndTime { get; set; }
/// <summary>
/// 实际开始时间
/// </summary>
public DateTime? ActualStartTime { get; set; }
/// <summary>
/// 实际结束时间
/// </summary>
public DateTime? ActualEndTime { get; set; }
/// <summary>
/// 完成数量
/// </summary>
public decimal FinishQty { get; set; }
/// <summary>
/// 合格数量
/// </summary>
public decimal QualifiedQty { get; set; }
/// <summary>
/// 不合格数量
/// </summary>
public decimal UnqualifiedQty { get; set; }
/// <summary>
/// 任务状态:待执行/执行中/已完成/暂停/异常
/// </summary>
public string TaskStatus { get; set; }
/// <summary>
/// 工艺工序序号
/// </summary>
public int ProcessSeq { get; set; }
/// <summary>
/// MES系统编码
/// </summary>
public string MESSystemCode { get; set; }
}
// 多端指令模型(MES/ERP跨系统)
public class MESERPCommand
{
/// <summary>
/// 指令ID
/// </summary>
public string CommandId { get; set; }
/// <summary>
/// 指令来源端编码
/// </summary>
public string SourceTerminalCode { get; set; }
/// <summary>
/// 指令目标端编码
/// </summary>
public string TargetTerminalCode { get; set; }
/// <summary>
/// 指令类型:工单下发/任务调整/执行反馈/质检录入/库存同步/异常上报
/// </summary>
public string CommandType { get; set; }
/// <summary>
/// 关联单号(工单/任务ID)
/// </summary>
public string RelateNo { get; set; }
/// <summary>
/// 指令内容(JSON)
/// </summary>
public string CommandContent { get; set; }
/// <summary>
/// 指令状态:待处理/已接收/已执行/执行失败/已回滚
/// </summary>
public string CommandStatus { get; set; }
/// <summary>
/// 指令创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 指令执行时间
/// </summary>
public DateTime? ExecuteTime { get; set; }
/// <summary>
/// 执行失败原因
/// </summary>
public string FailReason { get; set; }
}
// 工站数据采集模型(MES核心)
public class StationDataCollect
{
/// <summary>
/// 采集ID
/// </summary>
public int CollectId { get; set; }
/// <summary>
/// 关联MES任务ID
/// </summary>
public int TaskId { get; set; }
/// <summary>
/// 工站编码
/// </summary>
public string StationCode { get; set; }
/// <summary>
/// 采集类型:产量/设备状态/工艺参数/物料消耗
/// </summary>
public string CollectType { get; set; }
/// <summary>
/// 采集值
/// </summary>
public string CollectValue { get; set; }
/// <summary>
/// 采集时间
/// </summary>
public DateTime CollectTime { get; set; }
/// <summary>
/// 采集人/设备编号
/// </summary>
public string Collector { get; set; }
}
二、 核心业务组件实现(MES/ERP 跨系统适配)
1. MES/ERP 指令路由服务(核心)
csharp
运行
/// <summary>
/// MES/ERP指令路由服务(中介者模式,处理跨系统指令转发与执行)
/// </summary>
public class MESERPCommandRouterService
{
private readonly IDbConnection _dbConn;
private readonly LogHelper _logHelper;
private readonly Policy _retryPolicy; // 异常重试策略
// 指令处理器注册表(策略模式)
private readonly Dictionary<string, ICommandHandler> _commandHandlers = new Dictionary<string, ICommandHandler>();
public MESERPCommandRouterService(string connStr)
{
_dbConn = new SqlConnection(connStr);
_logHelper = new LogHelper();
// 配置重试策略:3次重试,每次间隔1秒
_retryPolicy = Policy.Handle<Exception>()
.WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(1),
(ex, timeSpan, retryCount, context) =>
{
_logHelper.Warn($"指令执行失败,第{retryCount}次重试:{ex.Message}");
});
// 注册指令处理器
RegisterCommandHandlers();
}
/// <summary>
/// 注册指令处理器
/// </summary>
private void RegisterCommandHandlers()
{
_commandHandlers.Add("工单下发", new WorkOrderDispatchHandler(_dbConn));
_commandHandlers.Add("任务调整", new StationTaskAdjustHandler(_dbConn));
_commandHandlers.Add("执行反馈", new StationExecuteFeedbackHandler(_dbConn));
_commandHandlers.Add("质检录入", new QualityCheckHandler(_dbConn));
_commandHandlers.Add("库存同步", new InventorySyncHandler(_dbConn));
_commandHandlers.Add("异常上报", new ExceptionReportHandler(_dbConn));
}
/// <summary>
/// 发送跨系统指令
/// </summary>
/// <param name="command">指令信息</param>
/// <returns></returns>
public bool SendCommand(MESERPCommand command)
{
try
{
// 1. 初始化指令信息
command.CommandId = Guid.NewGuid().ToString("N");
command.CreateTime = DateTime.Now;
command.CommandStatus = "待处理";
// 2. 保存指令到日志
_dbConn.Execute(
"INSERT INTO MESERPCommandLog (CommandId, SourceTerminalCode, TargetTerminalCode, CommandType, RelateNo, CommandContent, CommandStatus, CreateTime) " +
"VALUES (@CommandId, @SourceTerminalCode, @TargetTerminalCode, @CommandType, @RelateNo, @CommandContent, @CommandStatus, @CreateTime)",
command);
// 3. 执行指令(带重试策略)
_retryPolicy.Execute(() =>
{
if (_commandHandlers.ContainsKey(command.CommandType))
{
_commandHandlers[command.CommandType].Handle(command);
}
else
{
throw new NotSupportedException($"不支持的指令类型:{command.CommandType}");
}
});
// 4. 更新指令状态
command.CommandStatus = "已执行";
command.ExecuteTime = DateTime.Now;
UpdateCommandStatus(command);
_logHelper.Info($"指令{command.CommandId}执行成功,类型:{command.CommandType},目标端:{command.TargetTerminalCode}");
return true;
}
catch (Exception ex)
{
// 5. 更新指令失败状态
command.CommandStatus = "执行失败";
command.ExecuteTime = DateTime.Now;
command.FailReason = ex.Message;
UpdateCommandStatus(command);
_logHelper.Error($"指令{command.CommandId}执行失败:{ex.Message}");
return false;
}
}
/// <summary>
/// 更新指令状态
/// </summary>
private void UpdateCommandStatus(MESERPCommand command)
{
_dbConn.Execute(
"UPDATE MESERPCommandLog SET CommandStatus=@CommandStatus, ExecuteTime=@ExecuteTime, FailReason=@FailReason WHERE CommandId=@CommandId",
new
{
command.CommandStatus,
command.ExecuteTime,
command.FailReason,
command.CommandId
});
}
/// <summary>
/// 获取跨系统指令汇总(适配WinForm展示)
/// </summary>
public DataTable GetCommandSummary(DateTime startTime, string sourceTerminal = "", string targetTerminal = "")
{
var dt = new DataTable();
dt.Columns.Add("指令ID", typeof(string));
dt.Columns.Add("来源端", typeof(string));
dt.Columns.Add("目标端", typeof(string));
dt.Columns.Add("指令类型", typeof(string));
dt.Columns.Add("关联单号", typeof(string));
dt.Columns.Add("指令状态", typeof(string));
dt.Columns.Add("创建时间", typeof(DateTime));
dt.Columns.Add("执行时间", typeof(DateTime));
dt.Columns.Add("失败原因", typeof(string));
using (var conn = new SqlConnection(_dbConn.ConnectionString))
{
string sql = "SELECT CommandId, SourceTerminalCode, TargetTerminalCode, CommandType, RelateNo, CommandStatus, CreateTime, ExecuteTime, FailReason " +
"FROM MESERPCommandLog WHERE CreateTime >= @StartTime ";
var param = new DynamicParameters();
param.Add("StartTime", startTime);
if (!string.IsNullOrEmpty(sourceTerminal))
{
sql += "AND SourceTerminalCode = @SourceTerminal ";
param.Add("SourceTerminal", sourceTerminal);
}
if (!string.IsNullOrEmpty(targetTerminal))
{
sql += "AND TargetTerminalCode = @TargetTerminal ";
param.Add("TargetTerminal", targetTerminal);
}
sql += "ORDER BY CreateTime DESC";
var logs = conn.Query(sql, param);
foreach (var log in logs)
{
dt.Rows.Add(
log.CommandId,
log.SourceTerminalCode,
log.TargetTerminalCode,
log.CommandType,
log.RelateNo,
log.CommandStatus,
log.CreateTime,
log.ExecuteTime ?? DBNull.Value,
log.FailReason ?? ""
);
}
}
return dt;
}
}
// 指令处理器接口
public interface ICommandHandler
{
void Handle(MESERPCommand command);
}
// 工单下发处理器
public class WorkOrderDispatchHandler : ICommandHandler
{
private readonly IDbConnection _dbConn;
public WorkOrderDispatchHandler(IDbConnection dbConn) => _dbConn = dbConn;
public void Handle(MESERPCommand command)
{
// 1. 解析指令内容
var workOrderCmd = JsonConvert.DeserializeObject<WorkOrderDispatchCommand>(command.CommandContent);
// 2. 获取ERP工单信息
var workOrder = _dbConn.QuerySingle<ERPWorkOrder>(
"SELECT * FROM ERPWorkOrder WHERE WorkOrderNo = @WorkOrderNo",
new { WorkOrderNo = workOrderCmd.WorkOrderNo });
if (workOrder == null)
{
throw new Exception($"ERP工单{workOrderCmd.WorkOrderNo}不存在");
}
// 3. 获取产品工艺路径(关联工站)
var processRoutes = _dbConn.Query<ProcessRouteStation>(
"SELECT ProcessSeq, StationCode FROM ProductProcessRoute WHERE ProductCode = @ProductCode ORDER BY ProcessSeq",
new { ProductCode = workOrder.ProductCode });
if (processRoutes.Count() == 0)
{
throw new Exception($"产品{workOrder.ProductCode}无工艺路径,无法下发");
}
// 4. 拆解为MES工站任务
var currentTime = workOrder.PlanStartTime;
foreach (var route in processRoutes)
{
// 获取工站信息
var station = _dbConn.QuerySingle<ProductionStation>(
"SELECT * FROM ProductionStation WHERE StationCode = @StationCode",
new { StationCode = route.StationCode });
// 计算任务时长
var taskHours = Math.Ceiling((double)workOrder.OrderQty / (double)station.HourlyCapacity);
var taskEndTime = currentTime.AddHours(taskHours);
// 创建MES工站任务
_dbConn.Execute(
"INSERT INTO MESStationTask (WorkOrderNo, StationCode, LineCode, ProductCode, TaskQty, PlanStartTime, PlanEndTime, FinishQty, QualifiedQty, UnqualifiedQty, TaskStatus, ProcessSeq, MESSystemCode) " +
"VALUES (@WorkOrderNo, @StationCode, @LineCode, @ProductCode, @TaskQty, @PlanStartTime, @PlanEndTime, 0, 0, 0, '待执行', @ProcessSeq, @MESSystemCode)",
new
{
workOrder.WorkOrderNo,
route.StationCode,
station.LineCode,
workOrder.ProductCode,
workOrder.OrderQty,
PlanStartTime = currentTime,
PlanEndTime = taskEndTime,
route.ProcessSeq,
MESSystemCode = "MES_V1.0"
});
currentTime = taskEndTime;
}
// 5. 更新ERP工单状态为已下发
_dbConn.Execute(
"UPDATE ERPWorkOrder SET OrderStatus = '已下发', PlanEndTime = @PlanEndTime WHERE WorkOrderNo = @WorkOrderNo",
new { PlanEndTime = currentTime, workOrder.WorkOrderNo });
}
}
// 执行反馈处理器(MES→ERP)
public class StationExecuteFeedbackHandler : ICommandHandler
{
private readonly IDbConnection _dbConn;
public StationExecuteFeedbackHandler(IDbConnection dbConn) => _dbConn = dbConn;
public void Handle(MESERPCommand command)
{
// 1. 解析指令内容
var feedbackCmd = JsonConvert.DeserializeObject<StationExecuteFeedbackCommand>(command.CommandContent);
// 2. 更新MES工站任务状态
_dbConn.Execute(
"UPDATE MESStationTask SET TaskStatus=@TaskStatus, FinishQty=@FinishQty, QualifiedQty=@QualifiedQty, UnqualifiedQty=@UnqualifiedQty, ActualEndTime=@ActualEndTime WHERE TaskId=@TaskId",
new
{
TaskStatus = feedbackCmd.TaskStatus,
feedbackCmd.FinishQty,
feedbackCmd.QualifiedQty,
feedbackCmd.UnqualifiedQty,
ActualEndTime = feedbackCmd.TaskStatus == "已完成" ? DateTime.Now : (DateTime?)null,
feedbackCmd.TaskId
});
// 3. 获取关联的ERP工单
var task = _dbConn.QuerySingle<MESStationTask>(
"SELECT WorkOrderNo FROM MESStationTask WHERE TaskId = @TaskId",
new { TaskId = feedbackCmd.TaskId });
// 4. 统计该工单所有工站任务状态
var taskStats = _dbConn.Query<dynamic>(
"SELECT " +
"SUM(CASE WHEN TaskStatus = '已完成' THEN 1 ELSE 0 END) AS CompleteCount, " +
"COUNT(*) AS TotalCount " +
"FROM MESStationTask WHERE WorkOrderNo = @WorkOrderNo",
new { WorkOrderNo = task.WorkOrderNo });
// 5. 更新ERP工单状态
string orderStatus;
if (taskStats.First().CompleteCount == taskStats.First().TotalCount)
{
orderStatus = "已完成";
}
else if (taskStats.First().CompleteCount > 0)
{
orderStatus = "执行中";
}
else
{
orderStatus = "已下发";
}
_dbConn.Execute(
"UPDATE ERPWorkOrder SET OrderStatus = @OrderStatus WHERE WorkOrderNo = @WorkOrderNo",
new { OrderStatus = orderStatus, WorkOrderNo = task.WorkOrderNo });
}
}
// 其他处理器(任务调整/质检录入/库存同步/异常上报)简化实现
public class StationTaskAdjustHandler : ICommandHandler
{
private readonly IDbConnection _dbConn;
public StationTaskAdjustHandler(IDbConnection dbConn) => _dbConn = dbConn;
public void Handle(MESERPCommand command)
{
var adjustCmd = JsonConvert.DeserializeObject<StationTaskAdjustCommand>(command.CommandContent);
_dbConn.Execute(
"UPDATE MESStationTask SET PlanStartTime=@NewStartTime, PlanEndTime=@NewEndTime, TaskQty=@NewTaskQty WHERE TaskId=@TaskId",
new { adjustCmd.NewStartTime, adjustCmd.NewEndTime, adjustCmd.NewTaskQty, adjustCmd.TaskId });
}
}
public class QualityCheckHandler : ICommandHandler
{
private readonly IDbConnection _dbConn;
public QualityCheckHandler(IDbConnection dbConn) => _dbConn = dbConn;
public void Handle(MESERPCommand command)
{
var qualityCmd = JsonConvert.DeserializeObject<QualityCheckCommand>(command.CommandContent);
_dbConn.Execute(
"UPDATE MESStationTask SET QualifiedQty=@QualifiedQty, UnqualifiedQty=@UnqualifiedQty WHERE TaskId=@TaskId",
new { qualityCmd.QualifiedQty, qualityCmd.UnqualifiedQty, qualityCmd.TaskId });
}
}
public class InventorySyncHandler : ICommandHandler
{
private readonly IDbConnection _dbConn;
public InventorySyncHandler(IDbConnection dbConn) => _dbConn = dbConn;
public void Handle(MESERPCommand command)
{
// 同步MES物料消耗到ERP库存
var inventoryCmd = JsonConvert.DeserializeObject<InventorySyncCommand>(command.CommandContent);
_dbConn.Execute(
"UPDATE ERPInventory SET StockQty = StockQty – @ConsumeQty WHERE MaterialCode = @MaterialCode",
new { inventoryCmd.ConsumeQty, inventoryCmd.MaterialCode });
}
}
public class ExceptionReportHandler : ICommandHandler
{
private readonly IDbConnection _dbConn;
public ExceptionReportHandler(IDbConnection dbConn) => _dbConn = dbConn;
public void Handle(MESERPCommand command)
{
var exceptionCmd = JsonConvert.DeserializeObject<ExceptionReportCommand>(command.CommandContent);
_dbConn.Execute(
"INSERT INTO MESExceptionLog (TaskId, StationCode, ExceptionType, ExceptionDesc, ReportTime) " +
"VALUES (@TaskId, @StationCode, @ExceptionType, @ExceptionDesc, @ReportTime)",
new { exceptionCmd.TaskId, exceptionCmd.StationCode, exceptionCmd.ExceptionType, exceptionCmd.ExceptionDesc, ReportTime = DateTime.Now });
// 更新任务状态为异常
_dbConn.Execute("UPDATE MESStationTask SET TaskStatus='异常' WHERE TaskId=@TaskId", new { exceptionCmd.TaskId });
}
}
// 指令内容DTO
public class WorkOrderDispatchCommand { public string WorkOrderNo { get; set; } }
public class StationTaskAdjustCommand { public int TaskId { get; set; } public DateTime NewStartTime { get; set; } public DateTime NewEndTime { get; set; } public decimal NewTaskQty { get; set; } }
public class StationExecuteFeedbackCommand { public int TaskId { get; set; } public string TaskStatus { get; set; } public decimal FinishQty { get; set; } public decimal QualifiedQty { get; set; } public decimal UnqualifiedQty { get; set; } }
public class QualityCheckCommand { public int TaskId { get; set; } public decimal QualifiedQty { get; set; } public decimal UnqualifiedQty { get; set; } }
public class InventorySyncCommand { public string MaterialCode { get; set; } public decimal ConsumeQty { get; set; } }
public class ExceptionReportCommand { public int TaskId { get; set; } public string StationCode { get; set; } public string ExceptionType { get; set; } public string ExceptionDesc { get; set; } }
2. MES 工站数据采集服务(执行层核心)
csharp
运行
/// <summary>
/// MES工站数据采集服务(适配设备/人工采集)
/// </summary>
public class MESStationDataCollectService
{
private readonly IDbConnection _dbConn;
private readonly LogHelper _logHelper;
private readonly MESERPCommandRouterService _commandRouter;
public MESStationDataCollectService(string connStr)
{
_dbConn = new SqlConnection(connStr);
_logHelper = new LogHelper();
_commandRouter = new MESERPCommandRouterService(connStr);
}
/// <summary>
/// 采集工站数据(产量/设备状态/工艺参数/物料消耗)
/// </summary>
/// <param name="collectData">采集数据</param>
/// <returns></returns>
public bool CollectStationData(StationDataCollect collectData)
{
try
{
// 1. 保存采集数据
collectData.CollectId = _dbConn.QuerySingle<int>(
"INSERT INTO StationDataCollect (TaskId, StationCode, CollectType, CollectValue, CollectTime, Collector) " +
"VALUES (@TaskId, @StationCode, @CollectType, @CollectValue, @CollectTime, @Collector); " +
"SELECT SCOPE_IDENTITY();",
new
{
collectData.TaskId,
collectData.StationCode,
collectData.CollectType,
collectData.CollectValue,
collectData.CollectTime,
collectData.Collector
});
// 2. 根据采集类型处理业务逻辑
switch (collectData.CollectType)
{
case "产量":
HandleProductionCollect(collectData);
break;
case "设备状态":
HandleEquipmentStatusCollect(collectData);
break;
case "工艺参数":
HandleProcessParamCollect(collectData);
break;
case "物料消耗":
HandleMaterialConsumeCollect(collectData);
break;
}
_logHelper.Info($"工站{collectData.StationCode}数据采集成功,类型:{collectData.CollectType},值:{collectData.CollectValue}");
return true;
}
catch (Exception ex)
{
_logHelper.Error($"工站数据采集失败:{ex.Message}");
return false;
}
}
/// <summary>
/// 处理产量采集
/// </summary>
private void HandleProductionCollect(StationDataCollect collectData)
{
var finishQty = decimal.Parse(collectData.CollectValue);
// 1. 更新MES任务完成数量
_dbConn.Execute(
"UPDATE MESStationTask SET FinishQty = FinishQty + @FinishQty WHERE TaskId = @TaskId",
new { FinishQty = finishQty, collectData.TaskId });
// 2. 获取任务最新状态
var task = _dbConn.QuerySingle<MESStationTask>(
"SELECT * FROM MESStationTask WHERE TaskId = @TaskId",
new { collectData.TaskId });
// 3. 判断任务是否完成
string taskStatus = task.FinishQty >= task.TaskQty ? "已完成" : "执行中";
if (taskStatus != task.TaskStatus)
{
_dbConn.Execute(
"UPDATE MESStationTask SET TaskStatus = @TaskStatus, ActualEndTime = @ActualEndTime WHERE TaskId = @TaskId",
new { TaskStatus = taskStatus, ActualEndTime = taskStatus == "已完成" ? DateTime.Now : (DateTime?)null, collectData.TaskId });
// 4. 发送执行反馈指令到ERP
_commandRouter.SendCommand(new MESERPCommand
{
SourceTerminalCode = "MES_STATION",
TargetTerminalCode = "ERP_PLAN",
CommandType = "执行反馈",
RelateNo = task.WorkOrderNo,
CommandContent = JsonConvert.SerializeObject(new StationExecuteFeedbackCommand
{
TaskId = task.TaskId,
TaskStatus = taskStatus,
FinishQty = task.FinishQty + finishQty,
QualifiedQty = task.QualifiedQty,
UnqualifiedQty = task.UnqualifiedQty
})
});
}
}
/// <summary>
/// 处理设备状态采集
/// </summary>
private void HandleEquipmentStatusCollect(StationDataCollect collectData)
{
// 更新工站状态
_dbConn.Execute(
"UPDATE ProductionStation SET StationStatus = @StationStatus WHERE StationCode = @StationCode",
new { StationStatus = collectData.CollectValue, collectData.StationCode });
// 若设备异常,发送异常上报指令
if (collectData.CollectValue == "故障" || collectData.CollectValue == "异常")
{
_commandRouter.SendCommand(new MESERPCommand
{
SourceTerminalCode = "MES_STATION",
TargetTerminalCode = "MES_CENTER",
CommandType = "异常上报",
RelateNo = collectData.TaskId.ToString(),
CommandContent = JsonConvert.SerializeObject(new ExceptionReportCommand
{
TaskId = collectData.TaskId,
StationCode = collectData.StationCode,
ExceptionType = "设备异常",
ExceptionDesc = $"工站{collectData.StationCode}设备状态为{collectData.CollectValue}"
})
});
}
}
/// <summary>
/// 处理工艺参数采集
/// </summary>
private void HandleProcessParamCollect(StationDataCollect collectData)
{
// 保存工艺参数到历史表(用于质量追溯)
_dbConn.Execute(
"INSERT INTO StationProcessParam (TaskId, StationCode, ParamValue, CollectTime) " +
"VALUES (@TaskId, @StationCode, @ParamValue, @CollectTime)",
new { collectData.TaskId, collectData.StationCode, ParamValue = collectData.CollectValue, collectData.CollectTime });
}
/// <summary>
/// 处理物料消耗采集
/// </summary>
private void HandleMaterialConsumeCollect(StationDataCollect collectData)
{
var consumeData = JsonConvert.DeserializeObject<MaterialConsumeDto>(collectData.CollectValue);
// 1. 保存物料消耗记录
_dbConn.Execute(
"INSERT INTO StationMaterialConsume (TaskId, StationCode, MaterialCode, ConsumeQty, ConsumeTime) " +
"VALUES (@TaskId, @StationCode, @MaterialCode, @ConsumeQty, @ConsumeTime)",
new { collectData.TaskId, collectData.StationCode, consumeData.MaterialCode, consumeData.ConsumeQty, ConsumeTime = collectData.CollectTime });
// 2. 发送库存同步指令到ERP
_commandRouter.SendCommand(new MESERPCommand
{
SourceTerminalCode = "MES_STATION",
TargetTerminalCode = "ERP_INVENTORY",
CommandType = "库存同步",
RelateNo = consumeData.MaterialCode,
CommandContent = JsonConvert.SerializeObject(new InventorySyncCommand
{
MaterialCode = consumeData.MaterialCode,
ConsumeQty = consumeData.ConsumeQty
})
});
}
/// <summary>
/// 获取工站采集数据(适配WinForm展示)
/// </summary>
public DataTable GetStationCollectData(string stationCode, DateTime startTime, string collectType = "")
{
var dt = new DataTable();
dt.Columns.Add("采集ID", typeof(int));
dt.Columns.Add("任务ID", typeof(int));
dt.Columns.Add("工站编码", typeof(string));
dt.Columns.Add("采集类型", typeof(string));
dt.Columns.Add("采集值", typeof(string));
dt.Columns.Add("采集时间", typeof(DateTime));
dt.Columns.Add("采集人/设备", typeof(string));
string sql = "SELECT CollectId, TaskId, StationCode, CollectType, CollectValue, CollectTime, Collector " +
"FROM StationDataCollect " +
"WHERE StationCode = @StationCode AND CollectTime >= @StartTime ";
var param = new DynamicParameters();
param.Add("StationCode", stationCode);
param.Add("StartTime", startTime);
if (!string.IsNullOrEmpty(collectType))
{
sql += "AND CollectType = @CollectType ";
param.Add("CollectType", collectType);
}
sql += "ORDER BY CollectTime DESC";
var data = _dbConn.Query(sql, param);
foreach (var item in data)
{
dt.Rows.Add(item.CollectId, item.TaskId, item.StationCode, item.CollectType, item.CollectValue, item.CollectTime, item.Collector);
}
return dt;
}
}
// 物料消耗DTO
public class MaterialConsumeDto
{
public string MaterialCode { get; set; }
public decimal ConsumeQty { get; set; }
}
3. 多端指令调度中台(WinForm 核心)
csharp
运行
/// <summary>
/// MES/ERP多端指令调度中台(观察者模式)
/// </summary>
public class MultiTerminalMESERPDispatcher
{
private readonly MESERPCommandRouterService _commandRouter;
private readonly MESStationDataCollectService _dataCollectService;
private readonly Thread _commandMonitorThread;
// 指令事件(WinForm界面订阅)
public event EventHandler<MESERPCommandEventArgs> CommandProcessed;
// 数据采集事件
public event EventHandler<StationDataCollectEventArgs> DataCollected;
public MultiTerminalMESERPDispatcher(string connStr)
{
_commandRouter = new MESERPCommandRouterService(connStr);
_dataCollectService = new MESStationDataCollectService(connStr);
// 启动指令监控线程
_commandMonitorThread = new Thread(MonitorCommandStatus) { IsBackground = true };
_commandMonitorThread.Start();
}
/// <summary>
/// 发送跨系统指令
/// </summary>
public bool SendMESERPCommand(MESERPCommand command)
{
var result = _commandRouter.SendCommand(command);
if (result)
{
OnCommandProcessed(new MESERPCommandEventArgs(command));
}
return result;
}
/// <summary>
/// 采集工站数据
/// </summary>
public bool CollectStationData(StationDataCollect collectData)
{
var result = _dataCollectService.CollectStationData(collectData);
if (result)
{
OnDataCollected(new StationDataCollectEventArgs(collectData));
}
return result;
}
/// <summary>
/// 监控指令状态(定时刷新)
/// </summary>
private void MonitorCommandStatus()
{
while (true)
{
try
{
// 每30秒检查一次未执行的指令
var unprocessedCommands = _commandRouter.GetCommandSummary(DateTime.Now.AddDays(-1));
var failedCommands = unprocessedCommands.AsEnumerable()
.Where(row => row["指令状态"].ToString() == "执行失败")
.ToList();
if (failedCommands.Count > 0)
{
// 触发事件通知WinForm界面
OnCommandProcessed(new MESERPCommandEventArgs(null, true, $"发现{failedCommands.Count}条执行失败的指令"));
}
}
catch (Exception ex)
{
LogHelper.Error($"监控指令状态失败:{ex.Message}");
}
finally
{
Thread.Sleep(30000); // 30秒休眠
}
}
}
/// <summary>
/// 触发指令处理事件
/// </summary>
protected virtual void OnCommandProcessed(MESERPCommandEventArgs e)
{
CommandProcessed?.Invoke(this, e);
}
/// <summary>
/// 触发数据采集事件
/// </summary>
protected virtual void OnDataCollected(StationDataCollectEventArgs e)
{
DataCollected?.Invoke(this, e);
}
/// <summary>
/// 获取指令汇总
/// </summary>
public DataTable GetCommandSummary(DateTime startTime, string sourceTerminal = "", string targetTerminal = "")
{
return _commandRouter.GetCommandSummary(startTime, sourceTerminal, targetTerminal);
}
/// <summary>
/// 获取工站采集数据
/// </summary>
public DataTable GetStationCollectData(string stationCode, DateTime startTime, string collectType = "")
{
return _dataCollectService.GetStationCollectData(stationCode, startTime, collectType);
}
}
// 指令事件参数
public class MESERPCommandEventArgs : EventArgs
{
public MESERPCommand Command { get; set; }
public bool IsWarning { get; set; }
public string WarningMessage { get; set; }
public MESERPCommandEventArgs(MESERPCommand command, bool isWarning = false, string warningMessage = "")
{
Command = command;
IsWarning = isWarning;
WarningMessage = warningMessage;
}
}
// 数据采集事件参数
public class StationDataCollectEventArgs : EventArgs
{
public StationDataCollect CollectData { get; set; }
public StationDataCollectEventArgs(StationDataCollect collectData)
{
CollectData = collectData;
}
}
// 通用日志辅助类
public static class LogHelper
{
private static readonly ILog _log = LogManager.GetLogger(typeof(LogHelper));
public static void Info(string message) => _log.Info(message);
public static void Warn(string message) => _log.Warn(message);
public static void Error(string message) => _log.Error(message);
public static void Error(string message, Exception ex) => _log.Error(message, ex);
}
三、 WinForm 界面组件实现(核心界面)
1. ERP 工单下发界面(计划层→执行层)
csharp
运行
public partial class ERPWorkOrderDispatchForm : Form
{
private readonly MultiTerminalMESERPDispatcher _dispatcher;
private readonly string _connStr;
public ERPWorkOrderDispatchForm()
{
InitializeComponent();
_connStr = ConfigHelper.GetConnStr();
_dispatcher = new MultiTerminalMESERPDispatcher(_connStr);
_dispatcher.CommandProcessed += OnCommandProcessed;
// 初始化ERP工单列表
LoadERPWorkOrderList();
// 初始化指令汇总
dgvCommandSummary.DataSource = _dispatcher.GetCommandSummary(DateTime.Today, "ERP_PLAN", "MES_CENTER");
}
/// <summary>
/// 加载ERP工单列表
/// </summary>
private void LoadERPWorkOrderList()
{
using (var conn = new SqlConnection(_connStr))
{
var workOrders = conn.Query<ERPWorkOrder>(
"SELECT * FROM ERPWorkOrder WHERE OrderStatus IN ('待下发', '已下发', '执行中') ORDER BY PlanStartTime");
dgvWorkOrders.DataSource = workOrders;
// 状态列颜色
foreach (DataGridViewRow row in dgvWorkOrders.Rows)
{
var status = row.Cells["OrderStatus"].Value.ToString();
switch (status)
{
case "待下发": row.DefaultCellStyle.BackColor = Color.LightYellow; break;
case "已下发": row.DefaultCellStyle.BackColor = Color.LightBlue; break;
case "执行中": row.DefaultCellStyle.BackColor = Color.LightGreen; break;
case "已完成": row.DefaultCellStyle.BackColor = Color.LightGray; break;
}
}
}
}
/// <summary>
/// 工单下发按钮点击
/// </summary>
private void btnDispatchWorkOrder_Click(object sender, EventArgs e)
{
if (dgvWorkOrders.SelectedRows.Count == 0)
{
MessageBox.Show("请选择要下发的ERP工单");
return;
}
var workOrderNo = dgvWorkOrders.SelectedRows[0].Cells["WorkOrderNo"].Value.ToString();
var result = _dispatcher.SendMESERPCommand(new MESERPCommand
{
SourceTerminalCode = "ERP_PLAN",
TargetTerminalCode = "MES_CENTER",
CommandType = "工单下发",
RelateNo = workOrderNo,
CommandContent = JsonConvert.SerializeObject(new WorkOrderDispatchCommand { WorkOrderNo = workOrderNo })
});
if (result)
{
MessageBox.Show($"ERP工单{workOrderNo}下发到MES成功");
LoadERPWorkOrderList();
dgvCommandSummary.DataSource = _dispatcher.GetCommandSummary(DateTime.Today, "ERP_PLAN", "MES_CENTER");
}
else
{
MessageBox.Show($"ERP工单{workOrderNo}下发失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 指令处理事件(更新界面)
/// </summary>
private void OnCommandProcessed(object sender, MESERPCommandEventArgs e)
{
Invoke(new Action(() =>
{
if (e.IsWarning)
{
lblWarning.Text = e.WarningMessage;
lblWarning.ForeColor = Color.Red;
}
else
{
lblWarning.Text = "";
}
LoadERPWorkOrderList();
dgvCommandSummary.DataSource = _dispatcher.GetCommandSummary(DateTime.Today, "ERP_PLAN", "MES_CENTER");
}));
}
/// <summary>
/// 刷新按钮点击
/// </summary>
private void btnRefresh_Click(object sender, EventArgs e)
{
LoadERPWorkOrderList();
dgvCommandSummary.DataSource = _dispatcher.GetCommandSummary(DateTime.Today, "ERP_PLAN", "MES_CENTER");
}
}
2. MES 工站执行监控界面(执行层核心)
csharp
运行
public partial class MESStationExecuteMonitorForm : Form
{
private readonly MultiTerminalMESERPDispatcher _dispatcher;
private readonly string _connStr;
public MESStationExecuteMonitorForm()
{
InitializeComponent();
_connStr = ConfigHelper.GetConnStr();
_dispatcher = new MultiTerminalMESERPDispatcher(_connStr);
_dispatcher.CommandProcessed += OnCommandProcessed;
_dispatcher.DataCollected += OnDataCollected;
// 初始化工站列表
LoadStationList();
// 初始化指令汇总
dgvCommandSummary.DataSource = _dispatcher.GetCommandSummary(DateTime.Today, "MES_STATION");
}
/// <summary>
/// 初始化工站列表
/// </summary>
private void LoadStationList()
{
using (var conn = new SqlConnection(_connStr))
{
var stations = conn.Query<ProductionStation>("SELECT * FROM ProductionStation");
cboStationCode.DataSource = stations;
cboStationCode.DisplayMember = "StationName";
cboStationCode.ValueMember = "StationCode";
}
}
/// <summary>
/// 加载MES工站任务
/// </summary>
private void LoadMESStationTasks(string stationCode)
{
using (var conn = new SqlConnection(_connStr))
{
var tasks = conn.Query<MESStationTaskDto>(
@"SELECT t.*, s.StationName, s.StationType,
CASE WHEN t.TaskQty = 0 THEN 0 ELSE ROUND((t.FinishQty/t.TaskQty)*100,2) END AS CompletionRate
FROM MESStationTask t
LEFT JOIN ProductionStation s ON t.StationCode = s.StationCode
WHERE t.StationCode = @StationCode
ORDER BY t.PlanStartTime",
new { StationCode = stationCode });
dgvStationTasks.DataSource = tasks;
// 状态列颜色
foreach (DataGridViewRow row in dgvStationTasks.Rows)
{
var status = row.Cells["TaskStatus"].Value.ToString();
switch (status)
{
case "执行中": row.DefaultCellStyle.BackColor = Color.LightGreen; break;
case "异常": row.DefaultCellStyle.BackColor = Color.LightPink; break;
case "已完成": row.DefaultCellStyle.BackColor = Color.LightGray; break;
case "暂停": row.DefaultCellStyle.BackColor = Color.LightYellow; break;
}
}
}
}
/// <summary>
/// 工站选择变更
/// </summary>
private void cboStationCode_SelectedIndexChanged(object sender, EventArgs e)
{
if (cboStationCode.SelectedValue != null)
{
var stationCode = cboStationCode.SelectedValue.ToString();
LoadMESStationTasks(stationCode);
LoadStationCollectData(stationCode);
}
}
/// <summary>
/// 加载工站采集数据
/// </summary>
private void LoadStationCollectData(string stationCode)
{
dgvCollectData.DataSource = _dispatcher.GetStationCollectData(stationCode, DateTime.Today, cboCollectType.Text);
}
/// <summary>
/// 上报产量按钮点击
/// </summary>
private void btnReportProduction_Click(object sender, EventArgs e)
{
if (dgvStationTasks.SelectedRows.Count == 0)
{
MessageBox.Show("请选择要上报产量的工站任务");
return;
}
var taskId = int.Parse(dgvStationTasks.SelectedRows[0].Cells["TaskId"].Value.ToString());
var stationCode = cboStationCode.SelectedValue.ToString();
var finishQty = decimal.Parse(txtFinishQty.Text);
// 采集产量数据
var result = _dispatcher.CollectStationData(new StationDataCollect
{
TaskId = taskId,
StationCode = stationCode,
CollectType = "产量",
CollectValue = finishQty.ToString(),
CollectTime = DateTime.Now,
Collector = Environment.UserName
});
if (result)
{
MessageBox.Show("产量上报成功");
LoadMESStationTasks(stationCode);
LoadStationCollectData(stationCode);
}
else
{
MessageBox.Show("产量上报失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 上报设备状态按钮点击
/// </summary>
private void btnReportEquipmentStatus_Click(object sender, EventArgs e)
{
var stationCode = cboStationCode.SelectedValue.ToString();
var equipmentStatus = cboEquipmentStatus.Text;
// 采集设备状态数据
var result = _dispatcher.CollectStationData(new StationDataCollect
{
TaskId = 0, // 设备状态不关联具体任务
StationCode = stationCode,
CollectType = "设备状态",
CollectValue = equipmentStatus,
CollectTime = DateTime.Now,
Collector = Environment.UserName
});
if (result)
{
MessageBox.Show($"工站{stationCode}设备状态更新为{equipmentStatus}成功");
LoadStationCollectData(stationCode);
}
else
{
MessageBox.Show("设备状态上报失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 上报物料消耗按钮点击
/// </summary>
private void btnReportMaterialConsume_Click(object sender, EventArgs e)
{
if (dgvStationTasks.SelectedRows.Count == 0)
{
MessageBox.Show("请选择关联的工站任务");
return;
}
var taskId = int.Parse(dgvStationTasks.SelectedRows[0].Cells["TaskId"].Value.ToString());
var stationCode = cboStationCode.SelectedValue.ToString();
var materialCode = txtMaterialCode.Text.Trim();
var consumeQty = decimal.Parse(txtConsumeQty.Text);
// 采集物料消耗数据
var result = _dispatcher.CollectStationData(new StationDataCollect
{
TaskId = taskId,
StationCode = stationCode,
CollectType = "物料消耗",
CollectValue = JsonConvert.SerializeObject(new MaterialConsumeDto { MaterialCode = materialCode, ConsumeQty = consumeQty }),
CollectTime = DateTime.Now,
Collector = Environment.UserName
});
if (result)
{
MessageBox.Show("物料消耗上报成功,已同步到ERP库存");
LoadStationCollectData(stationCode);
}
else
{
MessageBox.Show("物料消耗上报失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// 指令处理事件
/// </summary>
private void OnCommandProcessed(object sender, MESERPCommandEventArgs e)
{
Invoke(new Action(() =>
{
dgvCommandSummary.DataSource = _dispatcher.GetCommandSummary(DateTime.Today, "MES_STATION");
if (cboStationCode.SelectedValue != null)
{
LoadMESStationTasks(cboStationCode.SelectedValue.ToString());
}
}));
}
/// <summary>
/// 数据采集事件
/// </summary>
private void OnDataCollected(object sender, StationDataCollectEventArgs e)
{
Invoke(new Action(() =>
{
if (cboStationCode.SelectedValue != null && cboStationCode.SelectedValue.ToString() == e.CollectData.StationCode)
{
LoadStationCollectData(e.CollectData.StationCode);
}
}));
}
}
// MES工站任务DTO(展示用)
public class MESStationTaskDto : MESStationTask
{
public string StationName { get; set; }
public string StationType { get; set; }
public double CompletionRate { get; set; } // 完成率(%)
}
3. MES/ERP 多端指令调度工作台(汇总监控)
csharp
运行
public partial class MESERPMultiTerminalWorkbenchForm : Form
{
private readonly MultiTerminalMESERPDispatcher _dispatcher;
private readonly string _connStr;
public MESERPMultiTerminalWorkbenchForm()
{
InitializeComponent();
_connStr = ConfigHelper.GetConnStr();
_dispatcher = new MultiTerminalMESERPDispatcher(_connStr);
_dispatcher.CommandProcessed += OnCommandProcessed;
// 初始化指令端筛选
cboSourceTerminal.Items.AddRange(new string[] { "全部", "ERP_PLAN", "ERP_INVENTORY", "MES_CENTER", "MES_STATION", "MES_QUALITY" });
cboSourceTerminal.SelectedIndex = 0;
cboTargetTerminal.Items.AddRange(new string[] { "全部", "ERP_PLAN", "ERP_INVENTORY", "MES_CENTER", "MES_STATION", "MES_QUALITY" });
cboTargetTerminal.SelectedIndex = 0;
// 初始化指令汇总
RefreshCommandSummary();
// 初始化数据看板
LoadDataDashboard();
}
/// <summary>
/// 刷新指令汇总
/// </summary>
private void RefreshCommandSummary()
{
var sourceTerminal = cboSourceTerminal.Text == "全部" ? "" : cboSourceTerminal.Text;
var targetTerminal = cboTargetTerminal.Text == "全部" ? "" : cboTargetTerminal.Text;
dgvCommandSummary.DataSource = _dispatcher.GetCommandSummary(DateTime.Today, sourceTerminal, targetTerminal);
// 失败指令标红
foreach (DataGridViewRow row in dgvCommandSummary.Rows)
{
if (row.Cells["指令状态"].Value.ToString() == "执行失败")
{
row.DefaultCellStyle.BackColor = Color.LightPink;
row.DefaultCellStyle.ForeColor = Color.Red;
}
}
}
/// <summary>
/// 加载数据看板
/// </summary>
private void LoadDataDashboard()
{
using (var conn = new SqlConnection(_connStr))
{
// 1. 指令执行状态统计
var commandStat = conn.Query(
"SELECT CommandStatus, COUNT(*) AS Count FROM MESERPCommandLog WHERE CreateTime >= @StartTime GROUP BY CommandStatus",
new { StartTime = DateTime.Today });
chartCommandStatus.Series.Clear();
var commandSeries = new Series("指令执行状态", SeriesChartType.Pie);
foreach (var data in commandStat)
{
commandSeries.Points.Add(new DataPoint(data.CommandStatus, data.Count));
}
chartCommandStatus.Series.Add(commandSeries);
// 2. ERP工单状态统计
var workOrderStat = conn.Query(
"SELECT OrderStatus, COUNT(*) AS Count FROM ERPWorkOrder GROUP BY OrderStatus");
chartWorkOrderStatus.Series.Clear();
var workOrderSeries = new Series("ERP工单状态", SeriesChartType.Column);
foreach (var data in workOrderStat)
{
workOrderSeries.Points.Add(new DataPoint(data.OrderStatus, data.Count));
}
chartWorkOrderStatus.Series.Add(workOrderSeries);
// 3. MES工站任务状态统计
var taskStat = conn.Query(
"SELECT TaskStatus, COUNT(*) AS Count FROM MESStationTask GROUP BY TaskStatus");
chartTaskStatus.Series.Clear();
var taskSeries = new Series("MES工站任务状态", SeriesChartType.Column);
foreach (var data in taskStat)
{
taskSeries.Points.Add(new DataPoint(data.TaskStatus, data.Count));
}
chartTaskStatus.Series.Add(taskSeries);
// 4. 工站产能利用率
var capacityStat = conn.Query(
@"SELECT s.StationName,
ISNULL(ROUND((SUM(t.TaskQty)/ (s.HourlyCapacity * 24 * 7)) * 100,2),0) AS UtilizationRate
FROM ProductionStation s
LEFT JOIN MESStationTask t ON s.StationCode = t.StationCode
AND t.PlanEndTime > GETDATE() AND t.TaskStatus != '已完成'
GROUP BY s.StationName, s.HourlyCapacity");
chartCapacity.Series.Clear();
var capacitySeries = new Series("工站产能利用率(%)", SeriesChartType.Bar);
foreach (var data in capacityStat)
{
capacitySeries.Points.Add(new DataPoint(data.StationName, data.UtilizationRate));
}
chartCapacity.Series.Add(capacitySeries);
}
}
/// <summary>
/// 指令端筛选变更
/// </summary>
private void cboSourceTerminal_SelectedIndexChanged(object sender, EventArgs e)
{
RefreshCommandSummary();
}
private void cboTargetTerminal_SelectedIndexChanged(object sender, EventArgs e)
{
RefreshCommandSummary();
}
/// <summary>
/// 刷新按钮点击
/// </summary>
private void btnRefresh_Click(object sender, EventArgs e)
{
RefreshCommandSummary();
LoadDataDashboard();
}
/// <summary>
/// 指令处理事件
/// </summary>
private void OnCommandProcessed(object sender, MESERPCommandEventArgs e)
{
Invoke(new Action(() =>
{
if (e.IsWarning)
{
lblWarning.Text = e.WarningMessage;
lblWarning.ForeColor = Color.Red;
}
else
{
lblWarning.Text = "";
}
RefreshCommandSummary();
LoadDataDashboard();
}));
}
}
四、 MES/ERP 适配要点与扩展
1. 核心适配要点
- 跨系统指令路由:基于中介者 + 策略模式实现 ERP→MES、MES→ERP 的指令路由,解决跨系统指令协同问题;
- 数据一致性:指令执行带重试策略,异常指令监控,确保 MES/ERP 数据一致;
- 工站数据采集:支持产量、设备状态、工艺参数、物料消耗多类型采集,适配 MES 执行层核心需求;
- 多端协同:计划端 / 工站端 / 质控端 / 库存端指令隔离与汇总,支撑 MES/ERP 多角色协同;
- WinForm 交互:状态列颜色区分、数据看板可视化、异常指令预警,提升操作效率。
2. 扩展方向
- 标准接口适配:支持 SAP / 用友 / 金蝶等主流 ERP 的标准接口,实现开箱即用的 ERP 对接;
- 设备集成:适配 OPC UA/Modbus 等工业协议,实现工站设备数据自动采集,无需人工录入;
- 高级排程:集成 APS 高级排程算法,实现工站任务的智能优化排程;
- 移动端适配:添加 WebAPI 层,支持工站端移动端扫码接收任务、反馈执行状态;
- 数据追溯:基于采集数据实现生产全流程追溯,支持质量问题反向追踪到工站 / 设备 / 人员;
- 预警中心:工单延迟、设备异常、物料短缺预警,WinForm 端弹出提示 / 声音提醒 / 邮件推送。
总结
核心关键点
该组件可直接作为 MES/ERP 集成的核心 WinForm 中间件,适配多指令端 / 应用调度模式,支撑产线工站流程层级的全流程管控。
