ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

苦糖果MES:可落地的开源B/S架构生产执行系统

苦糖果MES:可落地的开源B/S架构生产执行系统 简介苦糖果MES系统是一款面向国内离散制造型中小企业的开源免费生产执行管理系统采用B/S架构基于J2EE技术栈开发旨在解决其在生产计划排程、工序管控、物料追溯、设备状态采集等核心环节缺乏专业且低成本数字化工具的现实痛点。资源包为ZIP格式大小49.03MB包含系统源码、数据库脚本、部署说明及基础配置文档等关键内容虽文件总数未提供但结构覆盖后端服务模块、前端界面资源与典型工厂数据样例便于快速本地部署与二次开发。已有1885人学习下载体现了中小企业对轻量级国产MES方案的迫切需求。用户可直接获取完整可运行系统框架、适配离散产线的业务模型设计思路、开箱即用的SQL初始化脚本以及结合多年智造行业经验沉淀的工艺路线与工单管理逻辑显著降低MES选型与实施门槛。1. 苦糖果MES系统不是Demo玩具而是能跑通车间报工、工序流转、设备点检的开源B/S生产执行系统你见过把“开源MES”当PPT讲的团队吗我见过三次——一次在客户现场他们用GitHub上clone下来的某Java版MES前端页面刷屏后台连数据库都没连上一次在技术交流会有人演示“开源MES支持扫码”结果扫码后弹出404还有一次是某厂IT主管拿着“免费开源”宣传页来问“能不能直接替换掉我们那套用了八年的老系统”——答案是不能除非你愿意花两周配通基础数据、改三处硬编码、重写两段设备对接逻辑。苦糖果MES系统不是这样。它从第一天就定位为「可落地的轻量级生产执行系统」B/S架构意味着产线工人用Chrome就能报工不用装客户端开源MIT协议意味着你能看到所有SQL语句、所有API路由、所有设备通信心跳包的解析逻辑免费不是噱头——它不卖License、不设用户数上限、不锁高级功能。它解决的是中小制造企业最痛的三个场景① 班组长每天手工抄录20张纸质报工单② 车间主任查某订单进度要等IT导3小时Excel③ 设备异常停机后维修记录和备件消耗还在微信里传。适合谁不是想搭个Demo应付领导检查的IT助理而是真有5条产线、30台CNC/冲压/组装设备、日均200工单、且愿意花3天部署2天调参的生产信息化负责人。它不承诺“一键上线”但承诺“每一步都有日志、每一处都能改、每一个接口都带示例”。提示这不是SaaS没有账号注册页这不是低代码平台没有拖拽表单这是用C# Vue3 SQL Server也支持MySQL写的完整MES源码包里含数据库初始化脚本、IIS部署说明、OPC UA设备接入样例、甚至还有车间看板的Vue组件源码。2. 部署前必读为什么选它不是因为“开源”而是因为它的B/S结构真能扛住产线并发2.1 B/S架构不是口号它用SignalR实现实时工单推送而非轮询很多所谓“B/S MES”本质是“伪B/S”前端用Vue/React但后端API全是HTTP短连接工人同时扫码报工时服务器瞬间涌进上百个GET请求IIS队列爆满。苦糖果MES不同——它用ASP.NET Core SignalR建立长连接通道工单下发、设备状态变更、质检结果回传全部走WebSocket。验证方法很简单打开/signalr/hubs调试模式下用浏览器控制台执行const connection new signalR.HubConnectionBuilder() .withUrl(/hub/workorderhub) // 工单中心Hub .build(); connection.start().then(() { console.log(SignalR已连接); connection.invoke(SubscribeToOrder, SO20240501-001); // 订阅工单 });注意此Hub路径在Startup.cs中定义WorkOrderHub.cs里明确写了[Authorize]必须先登录获取JWT Token才能订阅。信号通道不暴露给未认证用户这是它和某些“开源MES Demo”最根本的安全分水岭。2.2 开源≠无维护成本MIT协议下你能改什么、不能动什么MIT协议允许商用、允许修改、允许闭源但苦糖果MES的源码结构决定了你的修改边界✅ 可安全修改Controllers/WorkOrderController.cs报工逻辑、Views/Device/PointCheck.vue点检表单、Data/SqlScripts/InitDB.sql建库脚本⚠️ 修改需谨慎Services/Device/OPCUAService.csOPC UA连接池管理、Models/Entities/WorkOrder.cs实体类关联12张表❌ 禁止删改Common/Constants.cs所有业务码值如WorkOrderStatus.InProgress2、Migrations/20231001120000_Init.csEF Core迁移文件删了会导致数据库升级失败我一般会做三件事把Constants.WorkOrderStatus里的中文描述抽成资源文件zh-CN.resx方便多语言在WorkOrderController.Create()里加一行_logger.LogInformation($新工单{model.OrderNo}创建来源{model.Source})追踪数据入口把OPCUAService.Connect()的超时时间从30秒改成15秒产线设备响应慢30秒太长。2.3 免费≠零成本硬件与环境的真实开销清单项目最低要求生产建议为什么关键数据库SQL Server Express10GB限制SQL Server Standard启用AlwaysOnExpress版不支持作业调度无法自动清理7天前的设备日志Web服务器IIS 10 .NET 6 RuntimeIIS 10 .NET 6 Hosting Bundle URL Rewrite Module缺URL重写模块Vue路由history模式会404前端访问Chrome 90 / Edge 95Chrome 115启用WebAssembly车间平板常驻Chrome旧版本不支持webusb设备直连API设备对接OPC UA服务器如KepwareOPC UA服务器 本地证书.pfx开源版默认用匿名连接产线OPC UA服务器强制证书认证时会失败提示部署前务必运行PowerShell ./scripts/CheckEnv.ps1——它会检测IIS是否启用WebSocket、.NET Runtime版本、SQL Server实例名是否为MSSQLSERVER非命名实例需改appsettings.json中的ConnectionStrings:Default。3. 核心功能落地从创建第一个工单到设备点检闭环3.1 创建工单不是填表而是绑定BOM、工艺路线、设备组苦糖果MES的工单WorkOrder不是独立存在它必须关联三要素BOM版本在/bom页创建BOM注意Version字段必须唯一否则工单生成时会报错BOM version conflict工艺路线Routing在/routing页定义工序序列关键字段是OperationCode如OP001和WorkCenterCode如WC-CNC-01设备组WorkCenter在/workcenter页配置Type选Machine或AssemblyLineCapacity填理论日产能单位件/班。创建工单的POST请求体长这样{ OrderNo: SO20240501-001, ProductCode: P-00123, BomVersion: V2.1, RoutingCode: RT-P00123-2024, Quantity: 100, DueDate: 2024-05-10T00:00:00, Priority: 1 }注意RoutingCode必须存在于Routing表中且该Routing下的每个Operation必须有对应的WorkCenter。如果OP001指向WC-CNC-01但数据库里没这个设备组工单创建会返回400 Bad Request并提示Work center WC-CNC-01 not found。3.2 工人扫码报工Vue前端如何调用扫码枪并校验工单状态车间用的不是手机APP而是Windows平板USB扫码枪。扫码触发逻辑在src/views/production/Report.vuetemplate input refscanInput typetext v-modelscanValue inputonScan classsr-only autofocus / /template script export default { data() { return { scanValue: }; }, methods: { onScan() { if (this.scanValue.length 8 this.scanValue.includes(SO)) { // 假设扫码格式SO20240501-001|OP001|EMP001 const [orderNo, opCode, empId] this.scanValue.split(|); this.$http.post(/api/workorder/report, { OrderNo: orderNo, OperationCode: opCode, EmployeeId: empId, Quantity: 1, Status: Completed }).then(res { this.$message.success(报工成功${res.data.Message}); this.scanValue ; // 清空输入框准备下一次扫码 }).catch(err { this.$message.error(报工失败${err.response.data.Message || 网络错误}); }); } } } }; /script关键点input事件比change更及时扫码枪输出是连续字符流v-model绑定后立即触发sr-only类让输入框视觉隐藏但键盘焦点可用autofocus确保页面加载后光标自动落在输入框——工人无需点击屏幕。3.3 设备点检闭环从计划生成到异常上报的完整链路点检不是填表而是带状态机的流程计划生成系统每天00:00根据PointCheckPlan表生成当日点检任务存入PointCheckTask表StatusPending工人执行扫码进入/pointcheck/task/123页面显示CheckItems如“主轴温度≤65℃”、“冷却液液位≥80%”每项有Pass/Fail/NA按钮异常上报选Fail后弹出DefectReportModal必须填写DefectCode下拉选来自DefectCode字典表和PhotoUrl调用平板摄像头拍照自动派单提交后系统调用DefectService.CreateMaintenanceOrder()生成维修工单并推送给指定维修组。后端关键逻辑在Controllers/PointCheckController.cs[HttpPost(report)] public async TaskIActionResult Report([FromBody] PointCheckReportDto dto) { var task await _context.PointCheckTasks .FirstOrDefaultAsync(x x.Id dto.TaskId); if (task.Status ! PointCheckTaskStatus.Pending) // 只允许报未开始的任务 return BadRequest(Task is not in Pending status); // 更新任务状态 task.Status PointCheckTaskStatus.Completed; task.CompletedAt DateTime.UtcNow; // 处理缺陷 if (dto.HasDefect) { var defect new DefectRecord { TaskId dto.TaskId, DefectCode dto.DefectCode, PhotoUrl dto.PhotoUrl, CreatedAt DateTime.UtcNow }; _context.DefectRecords.Add(defect); // 自动创建维修工单 await _maintenanceService.CreateFromDefect(defect); // 此方法发SignalR通知维修组 } await _context.SaveChangesAsync(); return Ok(new { Message Report submitted }); }血泪经验DefectCode必须提前在/admin/defectcode页录入否则CreateFromDefect()会因外键约束失败PhotoUrl上传路径默认是/uploads/defect/需在IIS中为此目录开启写权限否则SaveChangesAsync()会抛IOException。4. 避坑指南那些让部署卡在第三天的五个真实翻车现场4.1 现象IIS部署后首页空白F12看Network全是404原因Vue Router用history模式但IIS未安装URL Rewrite Module或web.config里缺少重写规则。解决下载并安装 IIS URL Rewrite Module 确认web.config中有以下规则位于system.webServerrewriterules内rule nameVueRouter stopProcessingtrue match url.* / conditions logicalGroupingMatchAll add input{REQUEST_FILENAME} matchTypeIsFile negatetrue / add input{REQUEST_FILENAME} matchTypeIsDirectory negatetrue / /conditions action typeRewrite url/ / /rule注意此规则必须放在rules节点最底部否则会被其他规则拦截。4.2 现象扫码报工返回401 Unauthorized但登录态正常原因ASP.NET Core JWT认证中间件未启用或appsettings.json中Jwt:Key长度不足32字节AES-256要求。解决检查Startup.cs中services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)是否被注释运行openssl rand -base64 32生成32字节密钥填入appsettings.jsonJwt: { Key: your-32-byte-base64-key-here, Issuer: kugua-mes, Audience: kugua-mes }提示密钥不能含特殊字符如/若生成含这些字符重新执行命令直到得到纯字母数字组合。4.3 现象设备点检照片上传失败日志报Access to the path D:\kugua\uploads\defect\ is denied原因IIS应用池身份默认ApplicationPoolIdentity对uploads目录无写权限。解决右键D:\kugua\uploads→ 属性 → 安全 → 编辑 → 添加输入IIS AppPool\DefaultAppPool应用池名需与IIS中一致勾选修改和写入权限 → 确定。4.4 现象OPC UA设备连接超时日志显示Could not establish trust for endpoint原因产线OPC UA服务器启用了证书验证但苦糖果MES默认用匿名连接。解决在appsettings.json中启用证书模式OpcUa: { UseCertificate: true, CertificatePath: certs/client.pfx, CertificatePassword: your-password }将.pfx证书文件放入/certs/目录与wwwroot同级确保证书私钥导出时勾选“导出私钥”且密码不含中文或空格。4.5 现象工单状态不更新SignalR连接正常但workorderhub无消息推送原因WorkOrderHub的SubscribeToOrder方法未正确注册或前端未传Token。解决检查WorkOrderHub.cs中OnConnectedAsync是否调用await Groups.AddToGroupAsync(Context.ConnectionId, orderNo)前端连接时必须带Tokenconst connection new signalR.HubConnectionBuilder() .withUrl(/hub/workorderhub, { accessTokenFactory: () localStorage.getItem(token) // 登录后存的JWT }) .build();注意localStorage.getItem(token)必须是完整JWT字符串不能是Bearer xxx前缀。5. 进阶技巧用OPC UA实时数据驱动车间看板绕过数据库轮询5.1 为什么不用数据库查设备状态延迟太高车间看板要求“秒级刷新”但SQL查询设备表DeviceStatus有天然瓶颈每次查询需走EF Core ORM层生成SQL网络往返若10台设备每秒查一次数据库连接池迅速耗尽更糟的是设备状态变更可能发生在两次查询之间造成“假离线”。苦糖果MES的解法是让OPC UA服务器主动推送——设备状态变OPC UA发DataChangeNotificationMES服务端监听并广播给看板前端。5.2 实现步骤四步打通OPC UA→SignalR→Vue看板Step 1在OPCUAService中订阅设备节点Services/Device/OPCUAService.cs里添加public async Task SubscribeToDeviceStatus(string nodeId) { var subscription new Subscription(_session, 1000); // 1000ms刷新间隔 subscription.Notification (s, e) { foreach (var notification in e.NotificationMessages) { foreach (var item in notification.NotificationData.OfTypeDataChangeNotification()) { foreach (var value in item.MonitoredItems) { // value.Value是Variant类型需转为bool var status Convert.ToBoolean(value.Value.WrappedValue); // 广播给所有看板客户端 _hubContext.Clients.All.SendAsync(DeviceStatusChanged, new { NodeId nodeId, Status status, Timestamp DateTime.UtcNow }); } } } }; await subscription.SubscribeAsync(new[] { nodeId }); // 如ns2;sDevice001.Status }Step 2前端看板页监听SignalR事件src/views/dashboard/Realtime.vuescript export default { mounted() { this.initSignalR(); }, methods: { initSignalR() { this.connection new signalR.HubConnectionBuilder() .withUrl(/hub/dashboardhub) .build(); this.connection.on(DeviceStatusChanged, (data) { // 更新Vuex store或本地data const device this.devices.find(d d.NodeId data.NodeId); if (device) { device.Status data.Status; device.LastUpdate data.Timestamp; } }); this.connection.start(); } } }; /scriptStep 3看板UI用颜色编码状态div v-fordevice in devices :keydevice.NodeId :class[device-card, { online: device.Status, offline: !device.Status }] h3{{ device.Name }}/h3 p状态{{ device.Status ? 运行中 : 已停机 }}/p /div style scoped .device-card.online { border-left: 4px solid #4CAF50; } /* 绿色 */ .device-card.offline { border-left: 4px solid #f44336; } /* 红色 */ /styleStep 4避免SignalR消息风暴——加节流若设备每秒发10次状态变更前端会收到10次DeviceStatusChanged导致UI频繁重绘。加节流// 在initSignalR中 let throttleTimer; this.connection.on(DeviceStatusChanged, (data) { clearTimeout(throttleTimer); throttleTimer setTimeout(() { // 执行UI更新 this.updateDeviceStatus(data); }, 100); // 100ms内只处理最后一次 });从那以后我每次部署新产线看板都强制走一遍OPC UA订阅测试用UA Expert手动改设备节点值看前端是否1秒内变色。如果延迟2秒立刻查OPCUAService里的subscription.PublishingInterval是否被误设为5000应为1000而不是怪网络或IIS。希望帮到你。本文还有配套的精品资源点击获取
返回列表