尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
C# MES源码拆解:工单、报工、质检一体的车间执行系统
简介这套基于C#的MES系统源码面向工业信息化开发者、MES实施工程师及.NET程序员旨在帮助读者从代码层面理解制造执行系统的核心实现。资源共95个文件压缩包仅1.81MB以cs源码、resx界面资源、sql数据库脚本为主辅以dll依赖库、sln工程文件和说明文档结构清晰便于阅读。源码采用典型的三层架构涵盖主控制板、数据采集、基础资料、数据报表等模块并包含生产订单、工序、设备状态等实体类设计以及SQL Server建表脚本可让学习者直观掌握业务逻辑与数据存储的映射关系。同时资源内置NPOI、IrisSkin4等第三方库展示了Excel数据导出和界面美化的实践用法。已有3217人学习下载适合希望深入MES系统二次开发或提升C#工程能力的中高级开发者研读、复用与扩展。1. C# MES 源码拆解一套把工单、报工、质检串起来的车间执行系统这两年制造企业上 MES 基本是刚需但真正能在中小车间落地的 C# MES 源码却不多见。我帮某电子装配厂接过一套 MES 项目改造前计划员每天靠 Excel 工单和微信群报工设备状态靠老师傅用眼睛看老板问“这批货能不能按时交”没人答得上来。换成这套 C# MES 之后最明显的变化不是“无纸化”而是工单、工序报工、质检和追溯被一张网串了起来操作工在采集终端上刷卡报工检验员录入不合格品看板实时刷新计划员终于能回答交期问题。这套源码适合三类人想把车间数据管起来的制造企业 IT、做工业软件二次集成的开发者、以及想学 MES 业务逻辑的 C# 程序员。2. 架构与技术选型这套 C# MES 的模块边界画在哪2.1 功能地图工单、报工、质检、追溯如何串成一条线在制造执行系统里最核心的不是某个炫酷页面而是“工单”这条主线。这套源码把业务拆成了四个闭环计划层拿到 ERP 下发的生产订单后在 MES 里生成工单并拆成多道工序任务车间层通过采集终端对工序报工合格数、不良数实时回写质量层对首检、巡检、完工检生成检验单不合格品触发返工或报废追溯层则把物料批次、设备编号、操作工、检验结果全部挂到工单时间轴上。听起来模块多但代码里每个闭环都对应一个独立的领域服务没有各写各的 Controller。业务流程大概是这样工单创建后按工艺路线拆成 ProcessTask每道工序都有“待开工—生产中—已报工—已质检”的状态流转报工数据写入 WorkReport 流水表检验结果写入 InspectionRecord产品追溯码则通过批次号反查所有工序记录。这个设计最值得学习的地方是把“工序任务”和“报工流水”分开而不是只记录一个“最终产量”。没有 ProcessTask 这张中间表后面做工序级追溯和返工定位会非常痛苦。2.2 技术栈组成为什么是 C# .NET 而不是 Web 前端看这套源码之前我翻过一些市面上的 MES Demo很多是 Java 或 PHP 写的C# 开发者接手后第一件事是去对应 Spring 那一套概念。这套源码给我的第一印象是“技术栈很收敛”后端是 ASP.NET Core Web API管理端用 Razor Pages车间采集端是 WinForms数据库用 SQL Server设备接入走 Modbus TCP/RTU 和 OPC UA 标准协议。收敛带来的好处很实在产线工控机上跑 WinForms 采集端不容易受浏览器内核、杀毒软件、网页弹窗影响管理端用 Razor PagesIT 部门二次开发时不需要再搭一套前端工程。如果团队只会 C#这个技术组合几乎不需要跨语言协作从数据库到界面全是同一套语法体系。层次选型负责内容后端服务ASP.NET Core Web API工单、报工、检验、设备状态接口管理端ASP.NET Core Razor Pages计划排产、看板、不良品处理车间采集端WinForms (.NET 6)刷卡报工、PLC 数据采集、异常上报数据库SQL Server 2019主数据、流水、事件表设备接入Modbus TCP/RTU、OPC UA连接 PLC、仪表、传感器如果你所在的环境不允许用 SQL ServerEF Core 的 Provider 配置写在 Infrastructure 层换成 MySQL 或 PostgreSQL 也能跑但仓储查询里用到表提示的部分要微调。2.3 目录结构源码包拿到手先看哪几个项目源码包不是一个单项目而是标准的解决方案结构我把它拆给你看Mes.sln ├─ src │ ├─ Mes.Core # 领域实体、接口定义、状态机、业务规则 │ ├─ Mes.Infrastructure # EF Core DbContext、仓储实现、事务封装 │ ├─ Mes.Api # ASP.NET Core Web API对车间客户端和管理端暴露接口 │ ├─ Mes.WebAdmin # Razor Pages 管理后台看板、工单、检验页面 │ ├─ Mes.Collector # WinForms 数据采集客户端跑在车间工控机上 │ └─ Mes.Device # 设备驱动抽象Modbus/OPC UA 的适配器 ├─ database │ ├─ 01_schema.sql # 建表脚本 │ └─ 02_seed.sql # 初始化种子数据 └─ docs ├─ 接口文档.md └─ 部署手册.md我一般建议先从 Mes.Core 读起因为实体和状态机全在这里然后是 Mes.Api 的工单控制器把接口到服务的调用链走一遍最后才看 Mes.Collector因为采集端涉及 PLC 地址映射没有业务背景容易看懵。数据库脚本要配合实体一起看光看 SQL 看不出状态流转的约束。3. 核心流程走读工单下达到完工入库的代码链路3.1 工单主数据建模表结构与状态机设计工单表的设计直接决定后面所有业务好不好写。这套源码里 WorkOrder 表的字段不算多但关键状态位设计得很清楚CREATE TABLE dbo.WorkOrder ( Id INT IDENTITY PRIMARY KEY, OrderNo NVARCHAR(32) NOT NULL, ProductCode NVARCHAR(32) NOT NULL, PlanQty INT NOT NULL, Status TINYINT NOT NULL, -- 0待下达 1生产中 2完工 3已结案 4已取消 PlanStartTime DATETIME2 NULL, PlanEndTime DATETIME2 NULL, CreatedAt DATETIME2 NOT NULL, UpdatedAt DATETIME2 NOT NULL, CONSTRAINT UQ_WorkOrder_OrderNo UNIQUE (OrderNo) );Status 字段没有直接用字符串而是用 TINYINT 枚举存代码里对应WorkOrderStatus这个枚举。这样做的目的是在 C# 层可以用强类型判断避免到处写魔法字符串。我见过很多 MES 把状态写成PRODUCING、FINISHED这样的字符串一旦命名不统一后期统计口径全是坑。创建工单时核心逻辑在服务层我从源码里摘了精简版[HttpPost(api/workorder)] public async TaskIActionResult Create([FromBody] CreateWorkOrderRequest req) { var order new WorkOrder { OrderNo await _orderNoGenerator.NextAsync(WO), ProductCode req.ProductCode, PlanQty req.PlanQty, Status WorkOrderStatus.Created, CreatedAt _clock.Now() }; // 拉取工艺路线按工序拆成多个 ProcessTask var route await _routeRepo.GetByProductAsync(req.ProductCode); if (route null || route.Steps.Count 0) return BadRequest(该产品未维护工艺路线); foreach (var step in route.Steps.OrderBy(s s.Seq)) { order.Tasks.Add(new ProcessTask { StepCode step.StepCode, Seq step.Seq, PlanQty req.PlanQty, Status ProcessTaskStatus.Waiting }); } await _orderRepo.InsertAsync(order, cancellationToken); await _unitOfWork.CommitAsync(cancellationToken); return Ok(new { order.Id, order.OrderNo }); }这段代码的逻辑是传入产品编码后自动从工艺路线表查出该产品所有工序并为每道工序生成一个 ProcessTask 任务记录。注意_unitOfWork.CommitAsync将工单头和工序任务放在同一个事务里避免出现“工单创建了但工序任务没生成”的脏数据。OrderNo由生成器生成前缀 WO 加序列号保证唯一且可读。3.2 报工与派工实现事务边界里藏着多少坑报工是 MES 里并发量最大的操作也是最容易出数据问题的环节。源码里报工接口长这样[HttpPost(api/workorder/{id}/report)] public async TaskIActionResult Report(int id, ReportRequest req) { using var tx await _db.Database.BeginTransactionAsync(); // 1. 锁定工序任务行防止并发重复报工 var task await _taskRepo.GetForUpdateAsync(id, req.StepCode); if (task null || task.Status ! ProcessTaskStatus.Running) return BadRequest(当前工序未开工); // 2. 合格数累加后不能超过计划数 if (task.ReportedQty req.GoodQty task.PlanQty) return BadRequest(报工数超过计划数); // 3. 写报工流水同时增加成品/半成品库存 await _reportRepo.InsertAsync(new WorkReport { WorkOrderId id, StepCode req.StepCode, GoodQty req.GoodQty, DefectQty req.DefectQty, OperatorCode req.OperatorCode, DeviceCode req.DeviceCode, ReportTime _clock.Now() }); task.ReportedQty req.GoodQty; if (task.ReportedQty task.PlanQty) task.Status ProcessTaskStatus.Completed; await _taskRepo.UpdateAsync(task); await _stockService.IncreaseAsync(req.WarehouseCode, task.ProductCode, req.GoodQty); await tx.CommitAsync(); return Ok(task); }这里最关键的是GetForUpdateAsync它在 SQL Server 里执行的是带UPDLOCK, ROWLOCK的行级锁查询。如果不锁行两个操作工同时扫同一张工单报工累加时互相覆盖库存和报工数就对不上。事务边界把“报工流水写入、工序状态更新、库存增加”包在一起任何一步失败都整体回滚。报工还有个隐藏问题有些老师傅习惯一次把整班产量报上去而不是每完成一道工序就报一次。这套源码在接口层强制校验ReportedQty GoodQty PlanQty从源头挡住超计划报工。3.3 质检闭环检验单与不合格品处理的代码走向质检模块在源码里是独立的一个InspectionService与报工松耦合。检验动作不是简单记录“合格/不合格”而是会触发后续流程public class InspectionService { public async Taskint JudgeAsync(int inspectionId, bool isPass, string reason) { var inspection await _inspectionRepo.GetByIdAsync(inspectionId); if (inspection.Status ! InspectionStatus.Pending) throw new DomainException(该检验单已处理); if (isPass) { inspection.Status InspectionStatus.Approved; await _processTaskRepo.AdvanceAsync(inspection.WorkOrderId, inspection.StepCode); } else { inspection.Status InspectionStatus.Rejected; // 生成不合格品记录返工还是报废由不良代码决定 await _ncrRepo.InsertAsync(new NcrRecord { WorkOrderId inspection.WorkOrderId, StepCode inspection.StepCode, DefectCode inspection.DefectCode, Qty inspection.InspectionQty, Status NcrStatus.Open }); } await _inspectionRepo.UpdateAsync(inspection); return inspection.Id; } }合格时调用AdvanceAsync把工序任务从“待质检”推进到下一道工序不合格时不直接回退原工单状态而是生成一张 NCR不合格品处理记录。这个设计很关键返工不是把原工序状态改回去而是创建一条独立的返工流程和原工单通过 WorkOrderId 关联。这样统计一次合格率时不会因为状态回退把已完工的工序又算成在制。注意如果不合格就简单地把工单状态改成“生产中”会丢掉不合格品被返工过几次的过程信息追溯时只看到最终结果。4. 设备数据采集与看板从 PLC 变量到车间大屏4.1 采集网关PLC 数据如何进入 MESMES 光有报工还不行设备状态采集是另一个大头。这套源码里Mes.Collector是 WinForms 程序内部跑着一个后台轮询服务通过 Modbus TCP 从 PLC 读产量和运行状态。核心代码摘出来大概是这样public class ModbusPoller : BackgroundService { private readonly IPlcClient _plc; private readonly IMesApiClient _api; private readonly ILoggerModbusPoller _logger; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { // DB10.DBD4 存产量DB10.DBX0.2 存运行状态 var totalCount await _plc.ReadInt32Async(DB10.DBD4); var isRunning await _plc.ReadBooleanAsync(DB10.DBX0.2); await _api.ReportDeviceStatusAsync(new DeviceStatusUpdate { DeviceCode _deviceCode, TotalCount totalCount, Running isRunning, ReadAt DateTimeOffset.Now }); } catch (Exception ex) { // 网络抖动或PLC繁忙时只记日志不能杀进程 _logger.LogWarning(ex, {DeviceCode} 数据上报失败, _deviceCode); } await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken); } } }这里两个地址的含义要解释一下DB10.DBD4是西门子 PLC 里的数据块寻址DBD4表示从第 4 个字节开始读一个 32 位整数DBX0.2是位寻址读第 0 个字节的第 2 位。不同 PLC 品牌地址写法差异很大比如三菱用D100罗克韦尔用B3:0/2所以源码把 PLC 驱动抽象成IPlcClient具体驱动在Mes.Device项目里做适配。轮询间隔我在现场一般设 1 到 3 秒太频繁会把 PLC 的 Modbus 通讯模块拖死太慢看板看不出实时效果。上报失败时只记录日志不重试因为下一秒的轮询会带上最新值重试反而会造成消息积压。4.2 看板接口服务端推送与前端轮询的取舍设备状态上报到 MES 后看板页面需要实时展示。源码里用的是 SignalR 推送而不是前端每两秒轮询一次接口。集线器代码非常简洁public class DashboardHub : Hub { public async Task SubscribeToProduction() { await Groups.AddToGroupAsync(Context.ConnectionId, production); } public async Task SendDeviceSnapshot(IReadOnlyListDeviceSnapshot snapshots) { await Clients.Group(production).SendAsync(OnDeviceSnapshots, snapshots); } }浏览器打开看板页面时先调用SubscribeToProduction加入名为production的组后台收到新设备状态后调用SendDeviceSnapshot组内所有客户端都会收到消息。这套机制比轮询好在两个地方一是网络占用低二是事件驱动数据变化立刻推过去。SignalR 有一个常见问题是断线重连。车间网络经常有交换机重启、WiFi 不稳定前端 JavaScript 脚本里必须写重连逻辑另外网关重连后看板要先主动拉一次全量快照把断线期间漏掉的数据补回来不然大屏会一直停在上次的值。4.3 与 ERP 的接口边界到底谁负责物料入库很多 MES 项目做着做着就和 ERP 吵起来了——MES 产生了完工入库记录ERP 那边库存却没变。这套源码的处理方式是MES 只写本地业务表再通过一张MesSyncLog表把待同步的数据排队由后台任务推给 ERP 的 API。同步日志表设计为CREATE TABLE dbo.MesSyncLog ( Id INT IDENTITY PRIMARY KEY, BizType NVARCHAR(32) NOT NULL, -- WorkReportFinished BizId INT NOT NULL, -- 对应业务表主键 Status TINYINT NOT NULL, -- 0待处理 1成功 2失败 RetryCount INT NOT NULL DEFAULT 0, Payload NVARCHAR(MAX) NULL, -- JSON 消息体 CreatedAt DATETIME2 NOT NULL );每次做完完工入库先写一条BizTypeWorkReportFinished的同步日志后台任务每 30 秒扫一次待处理日志调用 ERP 接口成功后更新 Status失败则重试并记录次数。这个模式叫“本地消息表保证最终一致”比当场调 ERP 接口健壮得多。ERP 系统升级维护时MES 侧不会因为同步失败而卡住生产业务。5. 避坑指南MES 上线阶段最典型的五类翻车点5.1 报工成功但库存扣减“消失”现象操作工报工后工单状态更新了但过一会儿发现库存没增加有时候还出现库存负数。原因报工和库存增加不在同一个事务里或者压根没走事务。我调试时发现有版本的代码先写工单表再去调仓储库存中间 Web API 发生异常入库操作被跳过。解决把所有写操作放进BeginTransactionAsync包裹报工流水、工序状态、库存变动统一提交。排查时看是否用了行锁查询并发场景下两个请求同时读到相同库存值后续加出来的数会互相覆盖。5.2 看板数值间隔好几分钟才刷新现象设备明明在运行看板上的产量却卡在五分钟前车间主任一直问是不是系统坏了。原因网关单线程顺序上报某一台 PLC 地址不通时TCP 超时等了几十秒后续设备数据全部排队。另外看板页用了 SignalR但断线后没重连服务端发的消息客户端根本收不到。解决每个设备一个后台轮询任务互不阻塞PLC 读取设置较短超时时间前端做断线重连并在重连成功后主动拉一次全量快照。日志里加了每个设备的最后上报时间排查慢的问题时先看last_report_time分布。5.3 质检合格率统计老是“对不上”现象质检员录的合格数和报工的好品数明明一致但每天统计报表合格率时忽高忽低。原因报表里把“检验完成时间”和“报工时间”混用跨班次统计时晚班 23:55 录入的检验单被归到昨天产量却归到今天。还有客户端本地时间没校准比服务器快了五分钟。解决在时间统一由服务端生成DateTimeOffset.Now报表统计一律取“业务发生时间”字段并且分成“报工时间”和“检验时间”两个维度展示。现场排查时把 SQL 里的时间条件打印出来看看是不是时间边界定义的问题。5.4 返工单状态卡死现象不合格品走完返工流程后原工单状态一直停在“生产中”无法结案。原因返工逻辑直接改了原工序任务的状态但原工单的其他工序已经完工状态机里根本没有“完工后再回到生产中”的路径。数据库里出现状态矛盾前端页面正常操作无法闭合。解决必须建立独立的返工流程任务用ReworkOrderId关联原工单返工完成后再把结果回写原工单的“抽检字段”而不是修改原工序状态。源码里在领域层加了状态转换校验任何非法状态跳转直接抛异常。5.5 工艺路线复制到另一个车间后工序错乱现象同一个产品在两个车间生产A 车间要三道工序B 车间要五道工序但系统按产品编码拉出来的路线始终是一份。原因工艺路线表设计成了“产品编码 工序列表”没有车间维度。只要 A 车间工艺员改了工序B 车间的工单下发也跟着变。解决工艺路线主数据加WorkshopCode字段工单创建时按“产品 车间”匹配路线。这是一个非常典型的主数据设计问题改表本身不难难的是让生产、工艺、计划三个部门认可这个口径。源码里路由查询会区分RouteType复制到新车间时先在种子数据里建好路线再测试。6. 二次开发思路给 MES 加一个“返工复检”场景假设你现在接到一个新需求质检员发现 NCR 记录里有一批返工完成的产品需要做二次检验只有复检通过才能流入成品库。直接在原检验流程上加状态会搞乱现有逻辑我的做法是先扩表再扩服务。先在数据库里加一张返工复检单CREATE TABLE dbo.ReworkInspection ( Id INT IDENTITY PRIMARY KEY, NcrId INT NOT NULL, WorkOrderId INT NOT NULL, StepCode NVARCHAR(32) NOT NULL, Qty INT NOT NULL, Result TINYINT NOT NULL DEFAULT 0, -- 0待检 1合格 2不合格 InspectedBy NVARCHAR(32) NULL, InspectedAt DATETIME2 NULL );然后在InspectionService里加一个方法public async Taskint CreateReworkInspectionAsync(int ncrId, string inspector) { var ncr await _ncrRepo.GetByIdAsync(ncrId); if (ncr.Status ! NcrStatus.ReworkCompleted) throw new DomainException(返工未完成不能复检); var entity new ReworkInspection { NcrId ncr.Id, WorkOrderId ncr.WorkOrderId, StepCode ncr.StepCode, Qty ncr.Qty, Result 0, InspectedBy inspector, InspectedAt _clock.Now() }; await _reworkInspectionRepo.InsertAsync(entity); return entity.Id; }最后在管理端加一个页面按钮调这个接口生成复检单复检结果再回写 NCR 状态。整个链路只涉及一张新表、一个服务方法、一个页面按钮不碰原有工序状态机。从那以后我每次给产线上新功能都强制自己先画出状态机、标好事务边界再动手改表返工、报废、暂停这些业务分支都要提前想清楚免得上线后靠手工改数据库“擦屁股”。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

VB .NET 与 OPC 入门实战:零基础跑通第一个设备数据读取示例

VB .NET 与 OPC 入门实战:零基础跑通第一个设备数据读取示例

简介:这份资源是一套面向工业自动化方向开发者的 VB .NET 与 OPC 通信入门示例,适合刚接触 OPC 技术、希望在 .NET 环境中完成客户端开发的初学者与工程师参考。压缩包共 38 个文件,约 194KB,包含 VB 源代码、项目与解决方案文件、…

📅 2026/10/23 17:22:33
VMware Tools 10.0.6 手动安装与故障排查全指南

VMware Tools 10.0.6 手动安装与故障排查全指南

简介:本资源为VMware Tools 10.0.6版本的官方源码离线安装包(tar.gz格式),专为Linux虚拟机用户设计,适用于在线安装失败、网络受限或需定制编译的场景。作为VMware虚拟化环境的核心增强组件,它可显著提升I/…

📅 2026/10/23 17:22:33
VMware黑苹果虚拟机:unlocker208补丁与EFI/SMC配置

VMware黑苹果虚拟机:unlocker208补丁与EFI/SMC配置

简介:unlocker208 是面向需要在 VMware Workstation 上运行 macOS 的开发者与测试人员的解锁补丁,用于绕过 VMware 对苹果系统的限制,使 macOS 10.12.6 等版本能在普通 PC 的虚拟机中完成安装与启动。压缩包共 18 个文件,约 9.61M…

📅 2026/10/23 17:22:33
MORE NEWS

更多资讯

📰

JavaScript核心教程:从变量、DOM到异步编程的实战指南

简介:一份面向JavaScript初学者的系统入门教程,以单个Word文档打包提供。内容从语言背景和基本语法入手,依次覆盖变量声明var、let、const的区别与变量提升机制、数据类型、运算符、条件循环控制结构、函数、对象、DOM操作和异步编程。函数部…

📰

Azure图标集实战:SVG命名、四种接入、主题与避坑体检

简介:面向前端开发、界面设计与技术文档编写者的 SVG 图标素材合集,聚焦 Azure 与 Microsoft 相关产品标识,同时收录多种品牌徽标与抽象通用符号,可用于云服务架构图、产品界面、演示文稿与文档配图的快速取用。压缩包约 2.57MB&a…

📰

Python requests响应处理全指南:正文、状态码、Cookie、耗时、响应头

做网络请求处理这行,不管是写爬虫还是调接口,拿到response对象都只算开了个头。真正见功夫的,是怎么把响应里的数据榨干用尽——正文怎么解、状态码怎么判断、时间怎么统计、cookie怎么取、响应头怎么看。这篇接着我们系列前两篇的节奏&#…

📰

程序员鸭梨的咖啡因代谢报告:大促封网前夜的技术玄学

晚上十点,距离大促核心代码正式“封网(Code Freeze)”还剩下最后整整两个小时。 整个研发大平层的灯光惨白得有些晃眼。我坐在工位上,一边揉着突突乱跳的太阳穴,一边用极其缓慢的动作把最后半罐红牛咽进喉咙。 如果这个…

📰

智能客服双 11 流量热力图:80/20 法则下高频 200 个提问的极端优化策略

去年双 11 预售开跑前三小时,财务总监拎着一杯美式晃晃悠悠晃到我工位旁,指着上个月大模型 API 的账单说:“李工,这个月大促如果按照你们现在的调用量,客服这块预计得烧掉近百万的 Token 费用。这还没算网关和向量数据…

📰

Z-EVES形式化验证原理与避坑指南

简介:本资源为形式化方法领域核心工具Z-EVES的完整安装与学习套件,面向软件工程、安全关键系统开发人员及高校形式化验证课程学习者,解决Z语言规格编写、自动推理、模型检查与交互式验证等实践难题。压缩包共5个文件(2个Windows可…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬