
简介这是一套面向制造业信息化开发者与MES学习者的加工装配模拟系统源码资料基于Visual Studio 2010与SQLServer2008R2、.NET 4.0开发适合希望理解MES核心业务逻辑与实现方式的中级开发者参考。系统分为服务端与客户端两大部分服务端涵盖产品、物料、工序、工位、工艺路线等基础档案加工与装配计划管理加工及装配实时看板数据初始化、标签初始化工具与实时监听服务客户端则实现加工与装配的过程控制、搬运过程控制以及质量异常处理核心逻辑通过数据库存储过程实现。资源包共445个文件约10MB以cs源码、dll程序集、resx与resources资源文件、txt说明、config配置、exe可执行程序及png、jpg界面素材为主另含mdf与ldf数据库文件、sln解决方案和docx设计说明书DB文件夹附加即可运行Doc文件夹提供《SimpleMES加工装配模拟系统》设计说明书。目前已有557人学习便于读者快速搭建模拟环境、研读源码结构与存储过程设计掌握MES加工装配流程的落地思路。1. 从一张工单跑不完说起SimpleMES 加工装配系统到底解决什么问题很多中小制造企业的车间里工单从 ERP 下发之后就开始“失联”纸质流转卡被油污糊住、装配缺料靠班组长吼、返工返修记录写在白板上一擦就没。SimpleMES 这类轻量级 MES 加工装配系统瞄准的正是这个断层——它不追求覆盖排产、供应链、财务全链路而是把“工单下发→工序报工→装配追溯→返修闭环”这条最短路径跑通。你如果是工厂 IT、自动化工程师或者正在做 mes系统 选型的生产负责人这套思路能让你用一台工控机加一个浏览器在两周内看到真实产线数据。它适合离散制造的加工装配场景不适合流程行业也不适合想一步到位上全模块的大集团。核心价值就一句话让每一张工单的每一个工序都有时间戳、有人、有结果。2. SimpleMES 的加工装配数据模型工单、工序与装配关系怎么建2.1 为什么加工装配场景不能照搬 ERP 的 BOM 结构ERP 的 BOM 是面向采购和成本的它告诉你一台产品需要哪些料但不告诉你这些料在哪个工位、以什么顺序装上去。SimpleMES 加工装配系统的数据模型必须多一层“工序-物料绑定”同一颗螺丝在工位 A 是拧紧动作在工位 B 可能是返修更换件。常见做法是把工单WorkOrder作为主实体下面挂工序实例OperationInstance每个工序实例再挂物料消耗记录MaterialConsumption。这样返修时你不需要改 BOM只需要在对应工序实例上追加一条返修记录原始装配关系不动。这个设计的好处是追溯链完整扫一个成品条码能反查出它经过的每一道工序、每个工位的操作员、每颗关键件的批次号。我一般会建议在工序实例上加三个字段op_seq工序序号、station_code工位编码、op_status待加工/加工中/已完成/返修中。op_status的状态机要严格不允许从“待加工”直接跳到“已完成”必须经过“加工中”否则报工时间无法计算。物料消耗记录里则要有material_batch和qty返修换件时qty可以为负表示拆下旧件。2.2 建表脚本五张核心表撑起加工装配追溯下面这段 SQL 是 SimpleMES 加工装配系统的最小可用表结构跑在 MySQL 8.0 上。注意work_order和operation_instance之间是一对多operation_instance和material_consumption也是一对多。-- 工单主表 CREATE TABLE work_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, wo_no VARCHAR(32) NOT NULL UNIQUE COMMENT 工单号, product_code VARCHAR(64) NOT NULL COMMENT 产品编码, plan_qty INT NOT NULL DEFAULT 0 COMMENT 计划数量, done_qty INT NOT NULL DEFAULT 0 COMMENT 完成数量, wo_status TINYINT NOT NULL DEFAULT 0 COMMENT 0待产 1生产中 2完工 3关闭, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_wo_no (wo_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 工序实例表 CREATE TABLE operation_instance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, wo_id BIGINT NOT NULL, op_seq INT NOT NULL COMMENT 工序序号从10开始留间隔, op_name VARCHAR(64) NOT NULL COMMENT 工序名称, station_code VARCHAR(32) NOT NULL COMMENT 工位编码, op_status TINYINT NOT NULL DEFAULT 0 COMMENT 0待加工 1加工中 2已完成 3返修中, operator_id VARCHAR(32) DEFAULT NULL, start_time DATETIME DEFAULT NULL, end_time DATETIME DEFAULT NULL, FOREIGN KEY (wo_id) REFERENCES work_order(id), INDEX idx_wo_op (wo_id, op_seq) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 物料消耗记录 CREATE TABLE material_consumption ( id BIGINT PRIMARY KEY AUTO_INCREMENT, op_inst_id BIGINT NOT NULL, material_code VARCHAR(64) NOT NULL, material_batch VARCHAR(64) DEFAULT NULL COMMENT 批次号追溯用, qty DECIMAL(10,3) NOT NULL DEFAULT 0 COMMENT 正数消耗负数退料/换件, scan_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (op_inst_id) REFERENCES operation_instance(id), INDEX idx_op_inst (op_inst_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 返修记录表 CREATE TABLE rework_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, op_inst_id BIGINT NOT NULL, rework_reason VARCHAR(255) NOT NULL, rework_action VARCHAR(255) NOT NULL COMMENT 返修动作描述, old_batch VARCHAR(64) DEFAULT NULL, new_batch VARCHAR(64) DEFAULT NULL, operator_id VARCHAR(32) NOT NULL, rework_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (op_inst_id) REFERENCES operation_instance(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 产品序列号追溯表 CREATE TABLE product_sn_trace ( id BIGINT PRIMARY KEY AUTO_INCREMENT, sn VARCHAR(64) NOT NULL UNIQUE COMMENT 成品序列号, wo_id BIGINT NOT NULL, current_op_seq INT NOT NULL DEFAULT 0, sn_status TINYINT NOT NULL DEFAULT 0 COMMENT 0在制 1完工 2返修中 3报废, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_sn (sn) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明operation_instance里的op_seq用 10、20、30 这样的间隔是为了后续插入临时工序时不至于全表改序号。material_consumption.qty允许负数这是返修换件场景的关键设计——拆下旧件记 -1装上新件记 1净消耗不变但批次追溯完整。product_sn_trace.current_op_seq记录当前走到哪道工序扫 SN 就能定位。参数说明wo_status和op_status的枚举值不要用字符串用 TINYINT 省空间且索引快。material_batch建索引因为追溯查询最频繁的就是“这批次的料装到了哪些 SN 上”。字符集统一 utf8mb4避免车间录入特殊符号时乱码。2.3 工单下发与工序报工的接口约定SimpleMES 加工装配系统对外一般暴露 REST 接口内部用 WebService 的老系统也不少。工单下发接口接收 ERP 传来的 JSON核心字段是wo_no、product_code、plan_qty和工序列表。工序列表里每道工序要有op_seq、op_name、station_code。报工接口则接收sn、op_seq、operator_id和scan_time。{ wo_no: WO20250101-001, product_code: P-8801, plan_qty: 100, operations: [ {op_seq: 10, op_name: 底板装配, station_code: ST-01}, {op_seq: 20, op_name: 水冷板安装, station_code: ST-02}, {op_seq: 30, op_name: 气密测试, station_code: ST-03} ] }报工接口收到后先根据sn查product_sn_trace校验current_op_seq是否等于请求的op_seq不等就拒绝防止跳站。然后更新operation_instance的op_status为 1加工中记录start_time。操作员点“完成”时再调一次把状态改为 2写end_time同时更新product_sn_trace.current_op_seq为下一道工序序号。这个“两次报工”的设计能拿到真实加工时长比只报一次完成要准得多。3. 加工装配报工与返修闭环从扫码到追溯的完整链路3.1 扫码报工的三个状态跃迁与时间戳计算加工装配现场最怕的是“事后补录”所以 SimpleMES 的报工必须绑定扫码动作。一个工位上的标准流程是扫工单条码 → 扫 SN → 系统校验 → 点开始 → 加工 → 点完成。对应到数据库operation_instance的状态从 0 变 1 再变 2start_time和end_time自动写入。这里有个细节如果操作员扫了 SN 但没点开始就点了完成系统应该拒绝因为start_time为空。我一般会在后端加一个校验op_status0时只允许“开始”操作op_status1时只允许“完成”或“返修”操作。时间戳计算加工时长时注意end_time - start_time得到的是秒数但车间可能有跨班次的情况。常见做法是只算自然时长不扣休息因为 MES 的定位是记录事实不是算工资。如果确实要扣休息那需要在operation_instance上加pause_time字段操作员点“暂停”时累加。但多数中小厂不需要这么细加了反而增加操作负担。3.2 返修返修模块汽车水冷板场景下的批次追溯怎么写汽车水冷板的返修有个典型场景气密测试不合格拆下水冷板换新件旧件要退回供应商分析。这时候 SimpleMES 的返修模块要做三件事记录返修原因、记录换件批次、保持原工单追溯链不断。具体操作是在operation_instance上把op_status改为 3返修中然后插入一条rework_recordold_batch填拆下的水冷板批次new_batch填新装上的批次。同时material_consumption里插两条记录一条qty-1对应旧批次一条qty1对应新批次。# 返修换件接口的核心逻辑Python SQLAlchemy 伪代码 def rework_replace(op_inst_id, old_batch, new_batch, reason, operator): op session.query(OperationInstance).get(op_inst_id) if op.op_status ! 2: raise ValueError(只有已完成的工序才能返修) # 1. 工序状态改为返修中 op.op_status 3 # 2. 写返修记录 rework ReworkRecord( op_inst_idop_inst_id, rework_reasonreason, rework_action更换水冷板, old_batchold_batch, new_batchnew_batch, operator_idoperator ) session.add(rework) # 3. 物料消耗旧件退料新件消耗 session.add(MaterialConsumption( op_inst_idop_inst_id, material_codeWL-8801, material_batchold_batch, qty-1 )) session.add(MaterialConsumption( op_inst_idop_inst_id, material_codeWL-8801, material_batchnew_batch, qty1 )) # 4. 返修完成后工序状态回到已完成 op.op_status 2 session.commit()逻辑说明这段代码的关键是“先校验再改状态”防止对未完成的工序做返修。qty-1和qty1成对出现保证净消耗不变但批次追溯能查到旧件去向和新件来源。rework_action字段写具体动作方便后续按返修类型统计。参数说明old_batch和new_batch必须来自实际扫码不能手输否则追溯就是假的。operator从登录会话取不要从前端传防止伪造。返修记录不删除只追加这是追溯系统的底线。3.3 用 WebService 对接老 ERP 的工单同步很多工厂的 ERP 还是十几年前的只支持 WebServiceSOAP。SimpleMES 加工装配系统要跟它对接常见做法是写一个定时任务每 5 分钟调一次 ERP 的getWorkOrderList方法拿到新工单后转成内部 JSON 再调自己的下发接口。WebService 的 WSDL 地址一般由 ERP 厂商提供字段映射要手工对一遍尤其是日期格式和数量单位。# 用 curl 测试 WebService 连通性SOAP 1.1 curl -X POST http://erp-host/Service.asmx \ -H Content-Type: text/xml; charsetutf-8 \ -H SOAPAction: http://tempuri.org/getWorkOrderList \ -d ?xml version1.0 encodingutf-8? soap:Envelope xmlns:soaphttp://schemas.xmlsoap.org/soap/envelope/ soap:Body getWorkOrderList xmlnshttp://tempuri.org/ dateFrom2025-01-01/dateFrom dateTo2025-01-31/dateTo /getWorkOrderList /soap:Body /soap:Envelope逻辑说明SOAPAction头必须和 WSDL 里定义的一致否则 ERP 会返回 500。日期格式看 ERP 要求有的要yyyy-MM-dd有的要yyyyMMdd。返回的 XML 用 Python 的xml.etree.ElementTree解析注意命名空间。参数说明dateFrom和dateTo建议按天增量拉不要一次拉一个月防止超时。如果 ERP 支持分页优先用分页。同步失败要有重试机制但重试次数不要超过 3 次避免把 ERP 打挂。4. SimpleMES 加工装配系统避坑五个让项目翻车的真实原因4.1 坑一工位扫码枪重复触发导致重复报工现象操作员扫一次 SN系统里出现两条报工记录done_qty多算了一个。原因扫码枪的“回车后缀”配置成了连续发送或者前端没做防抖。解决前端在扫码输入框加 500ms 防抖后端在报工接口用sn op_seq做唯一约束重复插入直接返回“已报工”。数据库层面加UNIQUE KEY uk_sn_op (sn, op_seq)最彻底。4.2 坑二返修后原工单完成数量被重复统计现象一台产品返修后重新报工work_order.done_qty加了两次。原因返修完成时又走了一遍正常报工逻辑。解决返修完成只更新operation_instance.op_status回 2不触发done_qty累加。done_qty只在首次从 1 变 2 时加一用op_status的前后值判断。4.3 坑三物料批次号手输导致追溯断链现象追溯查询时发现某批水冷板查不到装到了哪些 SN 上。原因操作员嫌扫码麻烦手工输入批次号输错了一位。解决material_batch字段只允许扫码枪输入前端设为readonly后端校验批次号是否存在于物料批次主数据表。不存在就拒绝报工。4.4 坑四WebService 同步工单时字段映射错位现象ERP 下发的工单在 SimpleMES 里产品编码变成了数量。原因WSDL 里字段顺序和实际返回不一致解析时按位置取值了。解决永远按标签名取值不要按索引。解析后先打印一条日志人工核对前三条数据再批量跑。4.5 坑五工控机浏览器缓存导致页面不刷新现象操作员报工后页面没变化以为没成功又扫了一次。原因工控机浏览器缓存了旧版 JS。解决静态资源加版本号查询串比如app.js?v20250101。或者用Cache-Control: no-cache响应头。车间工控机建议锁定浏览器版本不要自动更新。5. 把 SimpleMES 用出进阶价值追溯报表与产线节拍分析5.1 用一条 SQL 查出任意 SN 的完整加工装配履历追溯报表不需要复杂的 BI 工具一条 JOIN 查询就能把 SN 经过的工序、操作员、物料批次全拉出来。下面这条 SQL 在 MySQL 里跑输入一个 SN 就能看到全貌。SELECT t.sn, o.op_seq, o.op_name, o.station_code, o.operator_id, o.start_time, o.end_time, TIMESTAMPDIFF(SECOND, o.start_time, o.end_time) AS duration_sec, m.material_code, m.material_batch, m.qty, r.rework_reason, r.old_batch, r.new_batch FROM product_sn_trace t JOIN operation_instance o ON o.wo_id t.wo_id LEFT JOIN material_consumption m ON m.op_inst_id o.id LEFT JOIN rework_record r ON r.op_inst_id o.id WHERE t.sn SN20250101-0001 ORDER BY o.op_seq, m.scan_time;逻辑说明LEFT JOIN保证没有物料消耗或返修记录的工序也能显示。TIMESTAMPDIFF算秒数方便后续算平均节拍。ORDER BY o.op_seq保证按工序顺序排列。参数说明sn加引号如果是变量传入注意防注入。如果数据量大product_sn_trace.sn必须有索引否则这条查询会全表扫。5.2 产线节拍分析从报工时间戳算出瓶颈工位有了start_time和end_time就能算每个工位的平均加工时长。按station_code分组取AVG(duration_sec)最高的那个就是瓶颈。我一般会建议每周跑一次连续三周最高的工位就要考虑加人或者改工艺。注意要排除返修记录因为返修时长不代表正常节拍。SELECT station_code, COUNT(*) AS op_count, AVG(TIMESTAMPDIFF(SECOND, start_time, end_time)) AS avg_sec, MAX(TIMESTAMPDIFF(SECOND, start_time, end_time)) AS max_sec FROM operation_instance WHERE op_status 2 AND start_time 2025-01-01 AND end_time 2025-02-01 GROUP BY station_code ORDER BY avg_sec DESC;逻辑说明op_status 2只统计正常完成的工序排除返修中。时间范围按需改。MAX用来发现异常长尾如果某个工位max_sec特别大可能是操作员忘了点完成。参数说明avg_sec是秒除以 60 得分钟。如果产线有多个班次可以再加operator_id分组看个人差异但不要用来考核否则数据会失真。5.3 我踩过的坑和留给你的习惯SimpleMES 这类轻量 MES 加工装配系统最大的价值不是功能多而是数据真。我见过太多项目死在“操作员不愿意扫码”上最后追溯报表全是手工补录的假数据。我的习惯是上线第一周每天早会拿追溯报表点名表扬扫码最完整的工位不批评落后的。第二周开始把返修记录和批次追溯当成质量例会的固定议题。第三周产线节拍分析自然就有人来问了。技术方案再漂亮落不了地就是零。希望帮到你。本文还有配套的精品资源点击获取