本文还有配套的精品资源点击获取简介这是一款用VB.NET开发的Windows桌面生产管理程序主要面向中小制造企业或教学实践场景能完成从客户订单输入到生成具体生产工单的全流程辅助。系统内置主生产计划MPS制定界面可按产品、时间周期设定计划产量通过MRP运算模块自动展开物料需求考虑现有库存、在途采购和安全库存输出采购建议与自制件计划提供可视化日历排程视图支持拖拽调整工单执行时间工单管理模块涵盖新建、查询、状态更新与工序关联基础资料部分维护产品BOM物料清单、工序路线、供应商及仓库信息。所有数据本地存储于SQL Server数据库文件.MDF/.LDF程序采用WinForms架构含完整窗体设计如frmMain主界面、frmMRP运算页、frmBillManage工单编辑页等、资源文件.resx、图标mrp.ico及项目配置.vbproj可直接用Visual Studio 2019打开编译运行无需额外部署服务器适合毕业设计参考或轻量级车间调度试用。1. 项目概述为什么一个“小工具”能扛起车间调度的半边天你可能见过太多标榜“智能制造”的SaaS系统动辄几十万授权费、半年上线周期、还要配专职IT对接——但回到真实的小型机加工车间、模具厂或者电子组装作坊里老板最常问的一句话是“今天那批A-203外壳到底哪天能下线BOM里缺的那颗定制电容采购说三天后到能不能卡着点排进产线”这种问题不需要AI预测模型也不需要大数据中台它只需要一个能快速响应、逻辑透明、改起来不费劲的本地小工具。我做的这个VB.NET生产计划小工具就是冲着这个“最后一公里”的调度痛点来的。它不是ERP不碰财务模块不连MES底层设备但它把MPS主生产计划、MRP物料需求计划、工单跟踪这三个制造执行中最关键、最常被手工Excel折腾得焦头烂额的环节用一套轻量、可控、可调试的桌面程序串了起来。关键词里的“VB.NET生产工具”不是技术怀旧——而是因为WinForms在中小制造场景里有不可替代的优势部署就是双击exe数据全存在本地SQL Server .MDF文件里没有网络依赖没有云同步延迟车间电脑断网两小时照样能查库存、调工单、重跑MRP。而“MPS排程”和“MRP运算”这两个词在这里不是教科书定义而是具体到界面按钮你在frmMPS窗体里填好产品编码、计划周期、目标产量点“生成计划”系统就按你设定的安全库存、提前期、批量规则自动算出每周该投产多少再切到frmMRP选中某周的MPS计划行点“展开物料”它立刻遍历BOM树扣减当前库存、在途采购单、已分配量最后告诉你螺丝M5×20还差842颗需在下周二前下单自制件“支架组件”要启动生产建议开工日是下周一早8点。这不是黑箱输出所有计算逻辑都写在VB代码里你可以打开MRP.vb文件一行行看到它是怎么递归遍历DataSet.Tables(“BOM”)、怎么调用GetStockBalance()函数、怎么按工序路线反推开工时间的。“工单管理”也不是简单增删改查——每个工单Bill对象绑定了产品、BOM版本、工序路线、计划开工/完工时间、实际报工状态还能拖拽到frmCalendarMan的日历视图上实时调整排程松手那一刻关联的物料预留、产能占用、前后工序逻辑都会自动刷新。“BOM管理”更是整个系统的骨架它不是静态表格而是带层级关系、支持多版本、可关联工序路线和替代料的动态结构你在frmBase里维护完一个三级BOMMRP运算时就能完整展开三层子件的需求。这套工具真正落地的价值在于它把抽象的MRP理论转化成了车间主任看得懂、班组长改得动、老板信得过的操作流。它适合两类人一类是本科毕业设计的学生——源码结构清晰从数据库连接字符串在DataBase.vb里明文配置、DataSet填充逻辑LoadDataFromTable()方法、到窗体事件处理如btnRunMRP_Click全是可追踪、可打断点、可修改的VB.NET原生实现比抄一个Web框架的毕业设计更能吃透生产计划的本质逻辑另一类是年营收千万级以下的制造企业——没有IT团队不想被厂商绑定但又实在受不了每天靠三四个Excel表来回粘贴核对这时候把它编译成exe扔进共享盘给计划员配个权限三天就能跑通从订单到工单的闭环。我亲眼见过一家做汽车线束的小厂用它把原来平均3天的计划编制周期压缩到2小时内关键是——当客户临时加单时计划员不再需要打电话问仓库、发微信催采购、再手动改Excel而是直接在frmProductsDemand里录入新需求点一下“重算MRP”5秒后就知道哪些物料会短缺、哪些工单要顺延、顺延后会不会撞上模具保养窗口期。这才是生产工具该有的样子不炫技但每一步都踩在痛点上。2. 整体架构与设计思路为什么选择WinForms本地SQL Server而不是Web或云方案很多人第一反应是“都2024年了还用WinForms是不是太老了”这个问题我被问过不下二十次每次我都先反问一句“你们车间的电脑装的是Windows 10还是Windows 11有没有稳定的内网IT人员会不会配置IIS或Docker计划员平均年龄多大他们愿意每天打开浏览器、输网址、等页面加载还是更习惯双击桌面上那个带齿轮图标的程序”答案几乎都是后者。所以这个架构选择根本不是技术情怀而是对真实使用场景的妥协与尊重——WinForms在这里不是“落后”而是“精准匹配”。整个系统采用分层设计但刻意避免过度工程化。最底层是数据访问层DAL全部封装在DataBase.vb这个模块里。它不使用Entity Framework这类ORM而是直接用SqlClient DataSet组合。为什么因为DataSet天然支持离线编辑、变更跟踪、批量提交这对生产计划场景至关重要。比如在frmBillManage里修改一个工单的多个工序计划时间DataSet会自动记录哪些行被Modified点击“保存”时只提交变更部分而不是整张表刷一遍。更重要的是DataSet的XML序列化能力让“导出计划快照”功能变得极其简单——一键生成一个包含当前所有工单、物料需求、库存状态的XML文件发给供应商或存档备查不用额外写JSON序列化逻辑。而数据库选型锁定为SQL Server LocalDB通过.mdf/.ldf文件部署原因很实在它完全免费安装包只有几十MB静默安装命令一行搞定SqlLocalDB.exe create MRPBook -s且与Visual Studio深度集成——学生打开.sln文件F5直接运行无需单独装SQL Server企业部署时把mrpbook_Data.MDF复制到目标机器修改连接字符串指向本地路径程序就能读写彻底规避了MySQL驱动兼容性、PostgreSQL服务配置、SQLite并发锁这些在车间环境里极易翻车的坑。中间是业务逻辑层BLL它没有独立成DLL而是以Module形式散落在各个功能模块里比如MRPLogic.vb、MPSLogic.vb、BillScheduler.vb。这种“扁平化”设计是有意为之。在教学或轻量部署场景过度分层反而增加理解成本。学生看代码时想搞懂MRP怎么算直接打开MRPLogic.vb里面核心的CalculateMRP()函数参数就三个MPS计划行ID、计算起始日期、是否包含在途采购。函数内部逻辑清晰先用SQL查出该产品对应的所有BOM子件含层级深度再循环遍历每一层对每个子件调用GetNetRequirement()——这个函数才是真正的“心脏”它计算净需求的公式是净需求 毛需求 - 可用库存 - 在途采购 安全库存其中“可用库存”会动态扣减掉已被其他工单预留的数量通过查询BillItem表中的ReservedQty字段实现。所有参数如提前期LeadTime、最小批量MinLotSize、安全库存SafetyStock都来自BOM表和物料主数据表确保计算结果与实际业务规则一致。这种把业务规则硬编码在VB函数里的做法在大型系统里是反模式但在这个小工具里它带来了无与伦比的可调试性——你可以在CalculateMRP()里打个断点鼠标悬停就能看到每一层BOM展开后的毛需求值、当前库存值、计算出的净需求值比看任何报表都直观。最上层是表现层UI也就是那些frmXXX窗体。WinForms的优势在这里被放大所有窗体都基于同一个基类frmBase统一处理菜单栏、状态栏、用户权限虽然简易但支持“计划员”和“管理员”两级、以及最重要的——数据绑定。比如frmBillList的DataGridView它的DataSource直接绑定到一个BindingSource而BindingSource的DataSource是DataSet.Tables(“Bill”)。这意味着当你在frmBillManage里修改了某个工单的状态Status字段只要调用BindingSource.EndEdit()列表页的网格就会实时刷新无需手动ReloadData()。日历排程frmCalendarMan更是WinForms的绝杀场景它用MonthCalendar控件作为底图上面叠加Panel容器模拟“工单卡片”卡片位置由工单的PlanStartDate和PlanEndDate计算得出宽度代表工期。拖拽时监听MouseMove事件实时更新Panel.Left属性松手时触发MouseUp调用ScheduleHelper.AdjustBillDate()方法这个方法不仅更新工单表的日期字段还会自动检查是否与同一工序的其他工单时间冲突并弹出友好提示“警告与工单#B2023-087时间重叠是否强制覆盖”——这种毫秒级的交互反馈是任何Web前端在局域网环境下都难以稳定提供的。最后必须提一下那个看似矛盾的“资源包目录树”里面混着.py文件如main.py、frmMRP.py和.sql文件。这其实是项目演进的真实痕迹。最早版本确实是用PythonTkinter写的原型验证了MRP算法逻辑后来为了稳定性、Windows集成度和学生上手便利性才用VB.NET重写。那些.py文件被保留下来不是为了运行而是作为算法参考——比如你想对比两种MRP展开策略可以直接看Python版的递归实现而生产管理系统.sql文件则是数据库初始化脚本它创建了完整的表结构Product产品主数据、BOM物料清单含ParentID/ChildID/Quantity/Level字段、Bill工单主表、BillItem工单子项关联工序和物料、Inventory库存含WarehouseID、OnHandQty、InTransitQty等。所有外键约束、索引如在Bill表的ProductID和Status字段建复合索引都经过实测优化确保在万级工单数据量下MRP运算仍能在10秒内完成。这种“不追求最新潮只选择最稳妥”的架构哲学正是它能在真实车间存活下来的根本原因。3. 核心模块深度解析MPS、MRP、工单与BOM如何环环相扣要真正用好这个工具不能只停留在“点按钮出结果”的层面必须理解四大核心模块——MPS、MRP、工单、BOM——是如何像齿轮一样咬合转动的。它们不是孤立功能而是一个闭环MPS定目标MRP拆任务工单承执行BOM是血脉。下面我带你一层层拨开代码看它们怎么协同工作。3.1 MPS制定不是填数字而是设规则打开frmMPS窗体表面看只是个GridView加几个文本框但背后藏着生产计划的“宪法”。MPS的核心不是“我要生产多少”而是“我凭什么能生产这么多”。所以窗体里最关键的不是“计划产量”输入框而是右上角的“计划参数设置”按钮。点开它你会看到四个决定性的规则累计提前期Cumulative Lead Time这是整个MPS的锚点。比如产品A的BOM有三级A→B外购件→C自制件→D原材料。B的采购提前期是7天C的自制提前期是3天D的采购提前期是5天。那么A的累计提前期就是73515天。这意味着如果你要在6月30日交付A那么最晚6月15日就必须启动C的生产而C的生产又依赖D的到货所以D的采购订单最晚6月10日就要发出。这个值不是拍脑袋而是从BOM表里递归查询所有子件的LeadTime字段累加而来代码在MPSLogic.vb的GetCumulativeLeadTime()函数里。时界Time Fence分为需求时界Demand Time Fence和计划时界Planning Time Fence。需求时界内比如未来7天客户订单具有刚性不能随意更改或取消时界外则可以滚动调整。这个规则直接影响MRP运算——时界内的需求MRP会严格按订单日期展开时界外的则按MPS计划的“计划时栅”如每周一对齐。代码实现很简单在CalculateMPS()函数里对每个需求行判断If demandDate Now.AddDays(7) Then ...然后走不同的分支逻辑。批量规则Lot Sizing Rule系统预置了四种固定批量Fixed Order Quantity、经济批量EOQ、按需批量Lot-for-Lot、最小最大Min-Max。选择“按需批量”最常见即MRP算出多少就采购/生产多少但如果你的螺丝供应商要求每次最少订1000颗那就选“固定批量”系统会在净需求基础上向上取整到1000的倍数。这个规则直接写在BOM表的LotSizeRule字段MRP运算时读取并应用。安全库存Safety Stock不是全局统一值而是按物料分类设定。比如通用标准件螺丝、电阻设为5%而定制模具设为0%因为无法替代。这个值存储在Inventory表的SafetyStock字段MRP计算净需求时公式里明确加上它NetReq GrossReq - OnHand - InTransit SafetyStock。理解了这些再看frmMPS的GridView就完全不同了。每一行不是一个静态计划而是一个“规则实例”。当你双击某行进入编辑修改的不仅是“计划产量”更是触发了一次完整的规则重校验系统会重新计算该产品在计划周期内的累计提前期是否足够、时界内是否有冲突订单、批量规则是否导致实际投产量与计划量偏差过大如果偏差超10%会弹窗提示“建议调整计划产量”。这才是MPS制定的精髓——它不是计划员的主观意志而是业务规则在数据上的投影。3.2 MRP运算从BOM树到采购建议的七步推演MRP是整个系统的“大脑”它的运算过程在frmMRP窗体里被浓缩成一个按钮btnRunMRP但背后是严谨的七步推演。我把它拆解出来因为这是最容易出错、也最需要理解的部分第一步锁定源头。MRP不是全库扫描而是从一个具体的MPS计划行开始。你在GridView里选中某行比如产品A2024年6月第3周计划产量500台这就是MRP的“根节点”。代码里通过selectedMPSRow.Item(MPSID)获取ID。第二步展开BOM树。调用BOMHelper.ExpandBOMTree(mpsID)这个函数会递归查询BOM表构建一棵内存树。关键点在于它只展开“有效”的BOM版本Status’Active’且自动过滤掉用量为0的子件比如某些配置项。树的每个节点包含子件编码、用量、层级、是否自制IsMake、提前期。例如A的子件B用量2B的子件C用量1那么C对A的总用量就是2×12。第三步计算毛需求Gross Requirement。对BOM树的每个节点计算其在计划周期内比如6月15日-6月21日的毛需求。公式是毛需求 父件毛需求 × 本层用量。这里有个陷阱如果父件是自制件如B它的毛需求不是来自MPS而是来自上层A的计划所以需要按B的提前期把需求时间向前推移。代码里用DateTime.AddBusinessDays(-leadTime)实现且自动跳过周末和节假日节假日数据存在Calendar表里。第四步汇总可用库存。对每个子件查询Inventory表汇总所有仓库的OnHandQty现有库存但要扣减掉ReservedQty已被其他工单预留的数量。这个“可用库存”是动态的不是静态数字。比如螺丝M5×20总库存1000颗但工单#B2023-087已预留300颗那么MRP看到的可用库存就是700颗。第五步识别在途采购。查询PurchaseOrder表筛选状态为“In Transit”在途且预计到货日期在计划周期内的采购单累加OrderQty。这里的关键是“在途”的定义——它必须是已审批、已发货、但未入库的单据。系统通过PurchaseOrder.Status字段和ExpectedArrivalDate字段双重判断。第六步计算净需求与建议。对每个子件执行核心公式净需求 毛需求 - 可用库存 - 在途采购 安全库存。如果净需求≤0说明库存充足建议为“无需行动”如果0则根据批量规则生成建议- 按需批量建议采购/生产量 净需求- 固定批量建议量 Ceiling(净需求 / LotSize) * LotSize- 经济批量调用CalculateEOQ(demandRate, setupCost, holdingCost)函数计算第七步生成采购/生产建议表。最终输出到frmMRP的DataGridView里列包括子件编码、描述、毛需求、可用库存、在途采购、净需求、建议动作采购/自制、建议数量、建议下单/开工日期由净需求日期减去提前期得出。这个表不是终点而是起点——你可以勾选几行右键“生成采购申请”系统会自动生成PurchaseRequest记录或者勾选自制件点“转为工单”直接跳转到frmBillManage创建新工单。提示MRP运算耗时主要在BOM树展开和库存汇总。实测发现当BOM层级超过5级或单产品子件数超200时运算会变慢。我的优化方案是在BOMHelper类里加了缓存首次展开后将树结构序列化为XML存入临时文件后续相同MPSID的运算直接读取缓存速度提升3倍。这个技巧没写在文档里但代码注释里有标记TODO: Add cache for BOM tree学生可以自己补上。3.3 工单管理从纸面指令到数字工单的进化工单Bill是连接计划与执行的桥梁也是最容易被忽视的模块。很多系统把工单做成简单的“生产任务单”但这个工具里它是一个活的、带状态机的对象。打开frmBillList你会看到工单状态列有五种颜色标识灰色草稿、蓝色已下达、绿色进行中、橙色暂停、红色已完成。这不只是视觉效果而是背后完整的状态流转逻辑。每个工单Bill表关联三个核心实体-产品Product决定BOM版本和工艺路线。-工序路线Routing存储在Routing表包含工序编号、名称、标准工时、所需设备、前置工序。比如“外壳喷涂”工序标准工时2小时设备是“喷漆线#3”前置工序是“外壳清洗”。-工单子项BillItem这是工单的“血肉”。它不仅记录要生产的数量还记录每个工序的计划开始/结束时间、实际报工时间、报工人、合格数量、报废数量。当你在frmBillManage里新建一个工单系统会自动根据Routing表生成所有BillItem记录并按标准工时和设备产能初步排定计划时间调用Scheduler.CalculateSchedule()。工单的“智能”体现在两个细节1.动态产能检查当你在日历视图frmCalendarMan拖拽一个工单到新时间系统不会盲目接受。它会调用CapacityChecker.CheckCapacity(billID, newStartDate, newEndDate)这个函数查询Equipment表获取设备#3的可用时段再扫描BillItem表找出所有在同一设备上、时间重叠的其他工单。如果冲突弹窗显示“设备#3在6月18日9:00-11:00已被工单#B2023-088占用建议调整至6月19日”。这比人工排程表可靠得多。工序级追溯工单不是“黑盒”。点击任意工单行的“查看工序”按钮会弹出子窗体展示该工单所有工序的详细状态计划时间、实际开工时间、实际完工时间、报工人、首检结果、巡检记录。这些数据都来自BillItem表而BillItem的Status字段有精细定义NotStarted未开始、InProcess进行中、Completed完成、Rejected拒收。当某个工序状态变为Completed系统会自动检查其后置工序如果所有前置都已完成则将后置工序状态设为InProcess并邮件通知对应班组长——这个自动化逻辑写在BillItem.UpdateStatus()方法里。注意工单的“实际报工”功能frmBillManage里的“报工”按钮是故意设计得极简。它只记录“谁、什么时候、完成了多少件”不采集设备参数、不拍照留痕。因为调研发现车间工人最反感复杂的移动端报工流程他们宁愿用纸质单也不愿对着手机点五六次。所以这个工具的报工就是点击、输入数量、确认——3秒完成。复杂的数据分析留给计划员在后台用查询功能做。3.4 BOM管理为什么BOM是生产系统的“DNA”BOM物料清单常被误解为一张静态的“零件清单”但在这个工具里它是整个系统的“DNA”决定了MPS能否制定、MRP能否运算、工单能否生成。打开frmBase里的BOM管理页你会发现它远比想象中复杂。首先BOM是多版本的。同一个产品A可能有V1.0旧模具、V2.0新模具少一个螺丝、V3.0环保材料版。BOM表里有Version字段且每个版本有独立的生效日期EffectiveDate。MRP运算时系统会自动选取“计划日期”前最新生效的版本。代码逻辑在BOMHelper.GetActiveBOMVersion(productID, planDate)里用一条SQL搞定SELECT TOP 1 * FROM BOM WHERE ProductIDpid AND EffectiveDatedate ORDER BY EffectiveDate DESC。其次BOM是带替代料的。比如螺丝M5×20主用品牌是A但当A缺货时可用品牌B替代用量比例是1:1。BOM表里有AlternatePart字段存储替代料编码。MRP运算到这一层时会先尝试用主料如果主料净需求0且库存不足再检查替代料库存如果替代料充足则建议采购/生产替代料。这个逻辑在CalculateNetRequirement()函数的末尾分支里。第三BOM是可关联工序的。BOM表里有个RoutingID字段指向Routing表。这意味着当你为产品A选择V2.0 BOM时系统自动关联到“新模具版”的工序路线其中“外壳成型”工序的标准工时是1.2小时而V1.0版本是1.5小时——这直接影响工单的排程精度。最后BOM的层级校验是强制的。在保存BOM时系统会递归检查是否存在循环引用比如A用BB又用A如果检测到立即报错“BOM结构错误检测到循环引用请检查子件关系”。这个校验在BOMHelper.ValidateBOMTree()里实现用DFS深度优先搜索算法遍历时间复杂度O(n)但对车间级BOM通常50层毫无压力。实操心得BOM录入是系统上线最耗时的环节。我建议不要一次性录完所有产品而是抓重点——先录清楚TOP20产品的BOM占销量80%其他长尾产品先用“暂定版”占位。另外BOM里的用量单位必须统一比如全部用“个”不要混用“PCS”、“EA”否则MRP计算会出错。这个教训是我帮一家客户上线时踩的坑他们BOM里螺丝用“个”胶水用“ML”结果MRP把10ML胶水当成10个胶水来计算库存差点导致停产。4. 实操全流程从零开始跑通一次完整生产计划闭环现在我们把前面所有的模块串联起来走一遍真实的生产计划闭环。假设你是某家小型电机厂的计划员今天接到销售部的紧急需求“客户要200台YD-120电机6月30日前必须交付”。下面是你用这个工具完成计划的全过程每一步都对应具体的窗体操作和背后的代码逻辑。4.1 第一步录入客户订单与基础资料准备5分钟你打开程序主界面frmMain。首先确认基础资料是否完备- 点击菜单“基础资料”→“产品管理”检查YD-120是否存在。如果不存在点击“新增”填入编码、名称、规格如“三相异步1.5KW”、单位“台”。注意这里的“单位”必须与BOM用量单位一致否则后续计算全错。- 点击“BOM管理”找到YD-120确认V1.0版本已启用且BOM结构正确顶层YD-120子件包括定子用量1、转子用量1、外壳用量1、轴承用量2……一直展开到原材料铜线、硅钢片。- 点击“工序路线”确认YD-120的V1.0路由存在包含“定子绕线”、“转子压铸”、“整机装配”等工序且每个工序的设备、工时都已填写。一切就绪后进入需求录入- 点击菜单“计划管理”→“需求计划”frmProductsDemand。- 在GridView里点击“新增”填入产品编码YD-120需求日期2024-06-30需求数量200客户名称XX公司备注加急订单。- 点击“保存”。此时系统在ProductsDemand表里插入一条记录并自动标记DemandTypeCustomerOrder。提示需求录入时“需求日期”不是交货日而是客户要求的“完成日”。系统会根据累计提前期自动倒推出MPS的“计划日期”。比如YD-120的累计提前期是12天那么它的MPS计划日期就是2024-06-186月30日往前推12个工作日。4.2 第二步制定主生产计划MPS3分钟切换到“MPS制定”frmMPS。点击“刷新”GridView会列出所有未纳入MPS的客户订单按需求日期排序。你看到YD-120的订单在第一行。勾选这一行点击“生成MPS”。系统弹出对话框“请选择计划周期”你选择“2024年6月第3周6月17日-6月23日”。点击确定。系统执行MPSLogic.GenerateMPSFromDemand()函数它读取该订单的需求数量200按计划周期对齐因为6月30日交付计划必须在6月23日前完成所以200台全部放入6月第3周并在MPS表里插入一行ProductIDYD-120PlanWeek2024-W3PlanQty200Status’Draft’。你双击这一行进入编辑检查“计划参数”累计提前期自动显示为12时界设为7天即6月17日-6月23日为计划时界批量规则是“按需批量”安全库存为0电机是按单生产不设安全库存。确认无误点击“保存”。4.3 第三步运行MRP运算生成物料需求1分钟切换到“MRP运算”frmMRP。在左侧树形视图里展开“2024年6月”找到“第3周”再展开YD-120。选中YD-120这一行代表整个MPS计划点击“展开物料”按钮。系统开始运算。进度条走到100%后右侧DataGridView显示出结果。你重点关注几行轴承型号6204毛需求400200台×2个可用库存150净需求250建议采购250个建议下单日2024-06-106月17日计划日减7天采购提前期。铜线规格Φ0.8mm毛需求120kg根据BOM用量折算可用库存85kg净需求35kg建议采购35kg。关键发现定子自制件净需求200台建议“自制”建议开工日2024-06-126月17日减5天自制提前期。这意味着你需要为定子创建一个新工单。4.4 第四步创建并排程自制件工单4分钟在frmMRP的DataGridView里找到“定子”这一行右键点击选择“为选定物料创建工单”。系统自动跳转到frmBillManage并预填好产品定子数量200计划开工日2024-06-12计划完工日2024-06-14根据工序路线的标准工时和设备产能自动计算。你检查工序路线定子的路由有“硅钢片冲压”2小时、“绕线”3小时、“浸漆烘干”8小时。系统已按顺序排好时间。点击“保存”工单#B2024-0612-001创建成功并自动关联到YD-120的MPS计划。切换到“日历管理”frmCalendarMan你看到新工单卡片出现在6月12日的“冲压设备”栏下。你觉得时间太紧想把“浸漆烘干”挪到6月13日下午。于是你拖拽卡片到新位置松手——系统弹窗“设备#2烘干炉在6月13日14:00-22:00空闲调整成功。”同时工单的计划完工日自动更新为2024-06-15。4.5 第五步生成采购申请与跟踪执行2分钟回到frmMRP勾选所有“建议采购”的行轴承、铜线等右键“生成采购申请”。系统在PurchaseRequest表里创建一条记录状态为“待审批”并邮件通知采购主管。下午采购主管登录系统用管理员账号在“采购管理”模块里审批该申请状态变为“In Purchase”并生成采购订单PurchaseOrder表。同时你打开frmBillList筛选状态为“InProcess”的工单看到#B2024-0612-001正在执行。班组长在车间用平板电脑打开程序WinForms应用可打包为UWP适配平板点击“报工”输入“冲压工序完成100件”确认。系统立即更新BillItem表并将工单状态推进到下一工序“绕线”。4.6 第六步闭环验证与动态调整持续进行一周后你发现轴承供应商通知6204型号缺货要延期到7月5日。这时- 你打开frmMRP找到轴承行右键“更新在途采购”将预计到货日改为2024-07-05。- 点击“重算MRP”系统重新计算由于轴承延迟定子的开工日必须推迟进而导致YD-120的交付日无法保证6月30日。系统在结果表顶部高亮显示“警告YD-120交付风险当前计划交付日2024-07-08”。- 你立刻在frmMPS里将YD-120的MPS计划从“6月第3周”调整到“7月第1周”并通知销售部与客户协商。整个闭环从接单到预警耗时不到15分钟。而这一切都建立在WinForms的即时响应、本地数据库的零延迟、以及VB.NET代码对业务逻辑的透明封装之上。它不承诺颠覆制造业但承诺让每一次计划调整都变得清晰、可控、可追溯。5. 常见问题与避坑指南那些只有亲手调试过才会知道的细节在帮学生调试毕业设计、为企业部署这套工具的过程中我整理了一份高频问题清单。这些问题往往不会出现在官方文档里但每一个都曾让我或用户卡住半天。分享出来帮你绕过那些看不见的坑。5.1 数据库连接失败不是密码错了而是路径权限问题现象程序启动时报错“无法打开数据库 ‘mrpbook_Data.MDF’”或“拒绝访问文件”。真相SQL Server LocalDB默认以当前用户身份运行而.mdf文件如果放在Program Files或桌面等受保护路径LocalDB进程没有写入权限。这不是连接字符串的问题Data Source(LocalDB)\MSSQLLocalDB;AttachDbFilename|DataDirectory|\mrpbook_Data.MDF;...是正确的而是Windows文件系统权限问题。解决方案1. 将mrpbook_Data.MDF和mrpbook_Log.LDF文件复制到非系统目录比如C:\MRPBook\Data\。2. 右键该文件夹 → “属性” → “安全”选项卡 → “编辑” → “添加” → 输入NT Service\MSSQL$SQLEXPRESS如果是Express版或NT Service\MSSQLLocalDBLocalDB版→ 勾选“完全控制”→ 确定。3. 修改连接字符串中的AttachDbFilename路径为新位置例如AttachDbFilenameC:\MRPBook\Data\mrpbook_Data.MDF。实操心得我最初也以为是SQL Server没装好折腾了两小时重装LocalDB。后来用Process Monitor监控程序行为才发现是Access Denied错误。记住.MDF文件永远放在用户有完全控制权的目录下这是WinFormsLocalDB部署的铁律。5.2 MRP运算结果为0BOM用量单位与库存单位不匹配现象MRP运行后所有子件的“净需求”都是0明明库存显示只有50个毛需求是100个。真相BOM表里的Quantity字段和Inventory表里的OnHandQty字段单位不一致。比如BOM里写“轴承用量2”意思是2个但Inventory里“轴承库存”却记为“2000克”因为采购入库时按重量计量。MRP计算时毛需求 - 可用库存变成了2 - 2000结果是负数被截断为0。解决方案1. 统一所有物料的计量单位。在Product表里为每个物料定义UnitOfMeasureUOM如“个”、“千克”、“米”。2. 在BOM表和Inventory表里增加UOM字段并确保BOM用量和库存数量使用同一UOM。3. 在MRP计算前加入单位转换逻辑If bom.UOM inventory.UOM Then convertedQty ConvertUnit(inventory.OnHandQty, inventory.UOM, bom.UOM)。这个ConvertUnit函数需要你根据物料的密度、长度等参数预先配置转换率。注意这个坑在导入历史数据时最易发生。建议上线前用SQL脚本检查SELECT b.ProductID, b.Quantity, i.OnHandQty FROM BOM b JOIN Inventory i ON b.ChildIDi.ProductID WHERE b.UOM ! i.UOM把结果导出人工核对。5.3 日历拖拽失效不是代码bug而是DPI缩放惹的祸现象在高分辨率屏幕如2K、4K的Windows 10/11上frmCalendarMan的日历视图可以显示但拖拽工单卡片时鼠标位置与卡片位置严重偏移仿佛“隔空操作”。真相WinForms默认不支持高DPI缩放。当系统DPI设置为125%或150%时控件的实际像素尺寸被放大但鼠标坐标的获取e.X, e.Y仍是逻辑坐标导致计算出的卡片新位置错误。解决方案在项目属性 → “应用程序” → “视图DPI感知”中勾选“启用DPI感知”。然后在frmCalendarMan的构造函数里添加强制缩放适配Public Sub New() InitializeComponent() 启用DPI感知 SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint, True) 获取当前DPI缩放因子 Dim dpiScale As Single Me.CreateGraphics().DpiX / 96.0F 对所有Panel控件应用缩放 For Each pnl As Panel In Me.Controls.OfType(Of Panel)() pnl.Width CInt(pnl.Width * dpiScale) pnl.Height CInt(pnl.Height * dpiScale) pnl.Left CInt(pnl.Left * dpiScale) pnl.Top CInt(pnl.Top * dpiScale) Next End Sub提示这个适配在低DPI屏幕100%下也完全兼容所以建议所有WinForms项目都加上。学生做毕业设计时如果答辩用的是学校投影仪通常是100% DPI而开发用的是自己的高分屏125%不加这个答辩现场必出洋相。5.4 工单状态不更新事件订阅漏掉了现象在frmBillManage里修改了工单状态点击“保存”但frmBillList的GridView没有刷新还是显示旧状态。真相WinForms的数据绑定依赖于BindingSource的事件通知。如果在frmBillManage里你直接修改了DataSet.Tables(“Bill”).Rows(0)(“Status”)但没有调用BindingSource.EndEdit()或DataSet.AcceptChanges()那么BindingSource不知道数据已变自然不会刷新UI。解决方案所有对DataSet的修改必须遵循标准流程 正确做法 Dim row As DataRow ds.Tables(Bill).Rows.Find(billID) row(Status) Completed bsBill.EndEdit() 通知BindingSource ds.AcceptChanges() 提交到DataSet 如果需要立即刷新列表再调用 bsBill.ResetBindings(False)避坑口诀改DataRow → 调EndEdit → 再AcceptChanges → 最后ResetBindings。少一步UI就不同步。5.5 中文乱码与字体模糊资源文件.resx的隐藏陷阱现象程序在某些电脑上窗体文字显示为方块乱码或字体边缘发虚、看不清。真相.resx资源文件在编译时会将字符串按系统默认编码ANSI存储。如果开发机是中文WindowsGBK编码而目标机是英文WindowsASCII或者反之就会乱码。字体模糊则是WinForms默认使用GDI渲染抗锯齿不佳。解决方案1.解决乱码在Visual Studio中右键每个.resx文件 → “属性” → 将“复制到输出目录”设为“始终复制”并在项目文件.vbproj里添加xml PropertyGroup PreferredUILangzh-CN/PreferredUILang GenerateEncodingUTF-8/GenerateEncoding /PropertyGroup确保所有字符串资源以UTF-8编码保存。2.解决字体模糊在每个窗体的Load事件里添加vb Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load 启用高质量文本渲染 Me.SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint, True) 设置字体为微软雅黑清晰锐利 Me.Font New Font(Microsoft YaHei, 9.0F, FontStyle.Regular, GraphicsUnit.Point, 134) End Sub最后一个小技巧如果企业要部署到老旧XP系统虽然不推荐记得在项目属性 → “应用程序” → “目标框架”里将.NET Framework版本降为4.0并在安装包里捆绑.NET 4.0运行库。现代VS默认是4.7.2以上XP不支持。这套工具的价值从来不在它有多炫酷而在于它把生产计划里那些“应该如此”的常识变成了“确实如此”的代码。当你亲手修复了一个DPI缩放bug当你第一次看到MRP准确算出缺料清单当你拖拽工单时听到那声清脆的“滴”提示音——那一刻你触摸到的不是VB.NET语法而是制造业最朴素的逻辑输入、处理、输出环环相扣不容马虎。本文还有配套的精品资源点击获取简介这是一款用VB.NET开发的Windows桌面生产管理程序主要面向中小制造企业或教学实践场景能完成从客户订单输入到生成具体生产工单的全流程辅助。系统内置主生产计划MPS制定界面可按产品、时间周期设定计划产量通过MRP运算模块自动展开物料需求考虑现有库存、在途采购和安全库存输出采购建议与自制件计划提供可视化日历排程视图支持拖拽调整工单执行时间工单管理模块涵盖新建、查询、状态更新与工序关联基础资料部分维护产品BOM物料清单、工序路线、供应商及仓库信息。所有数据本地存储于SQL Server数据库文件.MDF/.LDF程序采用WinForms架构含完整窗体设计如frmMain主界面、frmMRP运算页、frmBillManage工单编辑页等、资源文件.resx、图标mrp.ico及项目配置.vbproj可直接用Visual Studio 2019打开编译运行无需额外部署服务器适合毕业设计参考或轻量级车间调度试用。本文还有配套的精品资源点击获取
VB.NET写的桌面版生产计划小工具,支持MPS排程、MRP运算和工单跟踪
本文还有配套的精品资源点击获取简介这是一款用VB.NET开发的Windows桌面生产管理程序主要面向中小制造企业或教学实践场景能完成从客户订单输入到生成具体生产工单的全流程辅助。系统内置主生产计划MPS制定界面可按产品、时间周期设定计划产量通过MRP运算模块自动展开物料需求考虑现有库存、在途采购和安全库存输出采购建议与自制件计划提供可视化日历排程视图支持拖拽调整工单执行时间工单管理模块涵盖新建、查询、状态更新与工序关联基础资料部分维护产品BOM物料清单、工序路线、供应商及仓库信息。所有数据本地存储于SQL Server数据库文件.MDF/.LDF程序采用WinForms架构含完整窗体设计如frmMain主界面、frmMRP运算页、frmBillManage工单编辑页等、资源文件.resx、图标mrp.ico及项目配置.vbproj可直接用Visual Studio 2019打开编译运行无需额外部署服务器适合毕业设计参考或轻量级车间调度试用。1. 项目概述为什么一个“小工具”能扛起车间调度的半边天你可能见过太多标榜“智能制造”的SaaS系统动辄几十万授权费、半年上线周期、还要配专职IT对接——但回到真实的小型机加工车间、模具厂或者电子组装作坊里老板最常问的一句话是“今天那批A-203外壳到底哪天能下线BOM里缺的那颗定制电容采购说三天后到能不能卡着点排进产线”这种问题不需要AI预测模型也不需要大数据中台它只需要一个能快速响应、逻辑透明、改起来不费劲的本地小工具。我做的这个VB.NET生产计划小工具就是冲着这个“最后一公里”的调度痛点来的。它不是ERP不碰财务模块不连MES底层设备但它把MPS主生产计划、MRP物料需求计划、工单跟踪这三个制造执行中最关键、最常被手工Excel折腾得焦头烂额的环节用一套轻量、可控、可调试的桌面程序串了起来。关键词里的“VB.NET生产工具”不是技术怀旧——而是因为WinForms在中小制造场景里有不可替代的优势部署就是双击exe数据全存在本地SQL Server .MDF文件里没有网络依赖没有云同步延迟车间电脑断网两小时照样能查库存、调工单、重跑MRP。而“MPS排程”和“MRP运算”这两个词在这里不是教科书定义而是具体到界面按钮你在frmMPS窗体里填好产品编码、计划周期、目标产量点“生成计划”系统就按你设定的安全库存、提前期、批量规则自动算出每周该投产多少再切到frmMRP选中某周的MPS计划行点“展开物料”它立刻遍历BOM树扣减当前库存、在途采购单、已分配量最后告诉你螺丝M5×20还差842颗需在下周二前下单自制件“支架组件”要启动生产建议开工日是下周一早8点。这不是黑箱输出所有计算逻辑都写在VB代码里你可以打开MRP.vb文件一行行看到它是怎么递归遍历DataSet.Tables(“BOM”)、怎么调用GetStockBalance()函数、怎么按工序路线反推开工时间的。“工单管理”也不是简单增删改查——每个工单Bill对象绑定了产品、BOM版本、工序路线、计划开工/完工时间、实际报工状态还能拖拽到frmCalendarMan的日历视图上实时调整排程松手那一刻关联的物料预留、产能占用、前后工序逻辑都会自动刷新。“BOM管理”更是整个系统的骨架它不是静态表格而是带层级关系、支持多版本、可关联工序路线和替代料的动态结构你在frmBase里维护完一个三级BOMMRP运算时就能完整展开三层子件的需求。这套工具真正落地的价值在于它把抽象的MRP理论转化成了车间主任看得懂、班组长改得动、老板信得过的操作流。它适合两类人一类是本科毕业设计的学生——源码结构清晰从数据库连接字符串在DataBase.vb里明文配置、DataSet填充逻辑LoadDataFromTable()方法、到窗体事件处理如btnRunMRP_Click全是可追踪、可打断点、可修改的VB.NET原生实现比抄一个Web框架的毕业设计更能吃透生产计划的本质逻辑另一类是年营收千万级以下的制造企业——没有IT团队不想被厂商绑定但又实在受不了每天靠三四个Excel表来回粘贴核对这时候把它编译成exe扔进共享盘给计划员配个权限三天就能跑通从订单到工单的闭环。我亲眼见过一家做汽车线束的小厂用它把原来平均3天的计划编制周期压缩到2小时内关键是——当客户临时加单时计划员不再需要打电话问仓库、发微信催采购、再手动改Excel而是直接在frmProductsDemand里录入新需求点一下“重算MRP”5秒后就知道哪些物料会短缺、哪些工单要顺延、顺延后会不会撞上模具保养窗口期。这才是生产工具该有的样子不炫技但每一步都踩在痛点上。2. 整体架构与设计思路为什么选择WinForms本地SQL Server而不是Web或云方案很多人第一反应是“都2024年了还用WinForms是不是太老了”这个问题我被问过不下二十次每次我都先反问一句“你们车间的电脑装的是Windows 10还是Windows 11有没有稳定的内网IT人员会不会配置IIS或Docker计划员平均年龄多大他们愿意每天打开浏览器、输网址、等页面加载还是更习惯双击桌面上那个带齿轮图标的程序”答案几乎都是后者。所以这个架构选择根本不是技术情怀而是对真实使用场景的妥协与尊重——WinForms在这里不是“落后”而是“精准匹配”。整个系统采用分层设计但刻意避免过度工程化。最底层是数据访问层DAL全部封装在DataBase.vb这个模块里。它不使用Entity Framework这类ORM而是直接用SqlClient DataSet组合。为什么因为DataSet天然支持离线编辑、变更跟踪、批量提交这对生产计划场景至关重要。比如在frmBillManage里修改一个工单的多个工序计划时间DataSet会自动记录哪些行被Modified点击“保存”时只提交变更部分而不是整张表刷一遍。更重要的是DataSet的XML序列化能力让“导出计划快照”功能变得极其简单——一键生成一个包含当前所有工单、物料需求、库存状态的XML文件发给供应商或存档备查不用额外写JSON序列化逻辑。而数据库选型锁定为SQL Server LocalDB通过.mdf/.ldf文件部署原因很实在它完全免费安装包只有几十MB静默安装命令一行搞定SqlLocalDB.exe create MRPBook -s且与Visual Studio深度集成——学生打开.sln文件F5直接运行无需单独装SQL Server企业部署时把mrpbook_Data.MDF复制到目标机器修改连接字符串指向本地路径程序就能读写彻底规避了MySQL驱动兼容性、PostgreSQL服务配置、SQLite并发锁这些在车间环境里极易翻车的坑。中间是业务逻辑层BLL它没有独立成DLL而是以Module形式散落在各个功能模块里比如MRPLogic.vb、MPSLogic.vb、BillScheduler.vb。这种“扁平化”设计是有意为之。在教学或轻量部署场景过度分层反而增加理解成本。学生看代码时想搞懂MRP怎么算直接打开MRPLogic.vb里面核心的CalculateMRP()函数参数就三个MPS计划行ID、计算起始日期、是否包含在途采购。函数内部逻辑清晰先用SQL查出该产品对应的所有BOM子件含层级深度再循环遍历每一层对每个子件调用GetNetRequirement()——这个函数才是真正的“心脏”它计算净需求的公式是净需求 毛需求 - 可用库存 - 在途采购 安全库存其中“可用库存”会动态扣减掉已被其他工单预留的数量通过查询BillItem表中的ReservedQty字段实现。所有参数如提前期LeadTime、最小批量MinLotSize、安全库存SafetyStock都来自BOM表和物料主数据表确保计算结果与实际业务规则一致。这种把业务规则硬编码在VB函数里的做法在大型系统里是反模式但在这个小工具里它带来了无与伦比的可调试性——你可以在CalculateMRP()里打个断点鼠标悬停就能看到每一层BOM展开后的毛需求值、当前库存值、计算出的净需求值比看任何报表都直观。最上层是表现层UI也就是那些frmXXX窗体。WinForms的优势在这里被放大所有窗体都基于同一个基类frmBase统一处理菜单栏、状态栏、用户权限虽然简易但支持“计划员”和“管理员”两级、以及最重要的——数据绑定。比如frmBillList的DataGridView它的DataSource直接绑定到一个BindingSource而BindingSource的DataSource是DataSet.Tables(“Bill”)。这意味着当你在frmBillManage里修改了某个工单的状态Status字段只要调用BindingSource.EndEdit()列表页的网格就会实时刷新无需手动ReloadData()。日历排程frmCalendarMan更是WinForms的绝杀场景它用MonthCalendar控件作为底图上面叠加Panel容器模拟“工单卡片”卡片位置由工单的PlanStartDate和PlanEndDate计算得出宽度代表工期。拖拽时监听MouseMove事件实时更新Panel.Left属性松手时触发MouseUp调用ScheduleHelper.AdjustBillDate()方法这个方法不仅更新工单表的日期字段还会自动检查是否与同一工序的其他工单时间冲突并弹出友好提示“警告与工单#B2023-087时间重叠是否强制覆盖”——这种毫秒级的交互反馈是任何Web前端在局域网环境下都难以稳定提供的。最后必须提一下那个看似矛盾的“资源包目录树”里面混着.py文件如main.py、frmMRP.py和.sql文件。这其实是项目演进的真实痕迹。最早版本确实是用PythonTkinter写的原型验证了MRP算法逻辑后来为了稳定性、Windows集成度和学生上手便利性才用VB.NET重写。那些.py文件被保留下来不是为了运行而是作为算法参考——比如你想对比两种MRP展开策略可以直接看Python版的递归实现而生产管理系统.sql文件则是数据库初始化脚本它创建了完整的表结构Product产品主数据、BOM物料清单含ParentID/ChildID/Quantity/Level字段、Bill工单主表、BillItem工单子项关联工序和物料、Inventory库存含WarehouseID、OnHandQty、InTransitQty等。所有外键约束、索引如在Bill表的ProductID和Status字段建复合索引都经过实测优化确保在万级工单数据量下MRP运算仍能在10秒内完成。这种“不追求最新潮只选择最稳妥”的架构哲学正是它能在真实车间存活下来的根本原因。3. 核心模块深度解析MPS、MRP、工单与BOM如何环环相扣要真正用好这个工具不能只停留在“点按钮出结果”的层面必须理解四大核心模块——MPS、MRP、工单、BOM——是如何像齿轮一样咬合转动的。它们不是孤立功能而是一个闭环MPS定目标MRP拆任务工单承执行BOM是血脉。下面我带你一层层拨开代码看它们怎么协同工作。3.1 MPS制定不是填数字而是设规则打开frmMPS窗体表面看只是个GridView加几个文本框但背后藏着生产计划的“宪法”。MPS的核心不是“我要生产多少”而是“我凭什么能生产这么多”。所以窗体里最关键的不是“计划产量”输入框而是右上角的“计划参数设置”按钮。点开它你会看到四个决定性的规则累计提前期Cumulative Lead Time这是整个MPS的锚点。比如产品A的BOM有三级A→B外购件→C自制件→D原材料。B的采购提前期是7天C的自制提前期是3天D的采购提前期是5天。那么A的累计提前期就是73515天。这意味着如果你要在6月30日交付A那么最晚6月15日就必须启动C的生产而C的生产又依赖D的到货所以D的采购订单最晚6月10日就要发出。这个值不是拍脑袋而是从BOM表里递归查询所有子件的LeadTime字段累加而来代码在MPSLogic.vb的GetCumulativeLeadTime()函数里。时界Time Fence分为需求时界Demand Time Fence和计划时界Planning Time Fence。需求时界内比如未来7天客户订单具有刚性不能随意更改或取消时界外则可以滚动调整。这个规则直接影响MRP运算——时界内的需求MRP会严格按订单日期展开时界外的则按MPS计划的“计划时栅”如每周一对齐。代码实现很简单在CalculateMPS()函数里对每个需求行判断If demandDate Now.AddDays(7) Then ...然后走不同的分支逻辑。批量规则Lot Sizing Rule系统预置了四种固定批量Fixed Order Quantity、经济批量EOQ、按需批量Lot-for-Lot、最小最大Min-Max。选择“按需批量”最常见即MRP算出多少就采购/生产多少但如果你的螺丝供应商要求每次最少订1000颗那就选“固定批量”系统会在净需求基础上向上取整到1000的倍数。这个规则直接写在BOM表的LotSizeRule字段MRP运算时读取并应用。安全库存Safety Stock不是全局统一值而是按物料分类设定。比如通用标准件螺丝、电阻设为5%而定制模具设为0%因为无法替代。这个值存储在Inventory表的SafetyStock字段MRP计算净需求时公式里明确加上它NetReq GrossReq - OnHand - InTransit SafetyStock。理解了这些再看frmMPS的GridView就完全不同了。每一行不是一个静态计划而是一个“规则实例”。当你双击某行进入编辑修改的不仅是“计划产量”更是触发了一次完整的规则重校验系统会重新计算该产品在计划周期内的累计提前期是否足够、时界内是否有冲突订单、批量规则是否导致实际投产量与计划量偏差过大如果偏差超10%会弹窗提示“建议调整计划产量”。这才是MPS制定的精髓——它不是计划员的主观意志而是业务规则在数据上的投影。3.2 MRP运算从BOM树到采购建议的七步推演MRP是整个系统的“大脑”它的运算过程在frmMRP窗体里被浓缩成一个按钮btnRunMRP但背后是严谨的七步推演。我把它拆解出来因为这是最容易出错、也最需要理解的部分第一步锁定源头。MRP不是全库扫描而是从一个具体的MPS计划行开始。你在GridView里选中某行比如产品A2024年6月第3周计划产量500台这就是MRP的“根节点”。代码里通过selectedMPSRow.Item(MPSID)获取ID。第二步展开BOM树。调用BOMHelper.ExpandBOMTree(mpsID)这个函数会递归查询BOM表构建一棵内存树。关键点在于它只展开“有效”的BOM版本Status’Active’且自动过滤掉用量为0的子件比如某些配置项。树的每个节点包含子件编码、用量、层级、是否自制IsMake、提前期。例如A的子件B用量2B的子件C用量1那么C对A的总用量就是2×12。第三步计算毛需求Gross Requirement。对BOM树的每个节点计算其在计划周期内比如6月15日-6月21日的毛需求。公式是毛需求 父件毛需求 × 本层用量。这里有个陷阱如果父件是自制件如B它的毛需求不是来自MPS而是来自上层A的计划所以需要按B的提前期把需求时间向前推移。代码里用DateTime.AddBusinessDays(-leadTime)实现且自动跳过周末和节假日节假日数据存在Calendar表里。第四步汇总可用库存。对每个子件查询Inventory表汇总所有仓库的OnHandQty现有库存但要扣减掉ReservedQty已被其他工单预留的数量。这个“可用库存”是动态的不是静态数字。比如螺丝M5×20总库存1000颗但工单#B2023-087已预留300颗那么MRP看到的可用库存就是700颗。第五步识别在途采购。查询PurchaseOrder表筛选状态为“In Transit”在途且预计到货日期在计划周期内的采购单累加OrderQty。这里的关键是“在途”的定义——它必须是已审批、已发货、但未入库的单据。系统通过PurchaseOrder.Status字段和ExpectedArrivalDate字段双重判断。第六步计算净需求与建议。对每个子件执行核心公式净需求 毛需求 - 可用库存 - 在途采购 安全库存。如果净需求≤0说明库存充足建议为“无需行动”如果0则根据批量规则生成建议- 按需批量建议采购/生产量 净需求- 固定批量建议量 Ceiling(净需求 / LotSize) * LotSize- 经济批量调用CalculateEOQ(demandRate, setupCost, holdingCost)函数计算第七步生成采购/生产建议表。最终输出到frmMRP的DataGridView里列包括子件编码、描述、毛需求、可用库存、在途采购、净需求、建议动作采购/自制、建议数量、建议下单/开工日期由净需求日期减去提前期得出。这个表不是终点而是起点——你可以勾选几行右键“生成采购申请”系统会自动生成PurchaseRequest记录或者勾选自制件点“转为工单”直接跳转到frmBillManage创建新工单。提示MRP运算耗时主要在BOM树展开和库存汇总。实测发现当BOM层级超过5级或单产品子件数超200时运算会变慢。我的优化方案是在BOMHelper类里加了缓存首次展开后将树结构序列化为XML存入临时文件后续相同MPSID的运算直接读取缓存速度提升3倍。这个技巧没写在文档里但代码注释里有标记TODO: Add cache for BOM tree学生可以自己补上。3.3 工单管理从纸面指令到数字工单的进化工单Bill是连接计划与执行的桥梁也是最容易被忽视的模块。很多系统把工单做成简单的“生产任务单”但这个工具里它是一个活的、带状态机的对象。打开frmBillList你会看到工单状态列有五种颜色标识灰色草稿、蓝色已下达、绿色进行中、橙色暂停、红色已完成。这不只是视觉效果而是背后完整的状态流转逻辑。每个工单Bill表关联三个核心实体-产品Product决定BOM版本和工艺路线。-工序路线Routing存储在Routing表包含工序编号、名称、标准工时、所需设备、前置工序。比如“外壳喷涂”工序标准工时2小时设备是“喷漆线#3”前置工序是“外壳清洗”。-工单子项BillItem这是工单的“血肉”。它不仅记录要生产的数量还记录每个工序的计划开始/结束时间、实际报工时间、报工人、合格数量、报废数量。当你在frmBillManage里新建一个工单系统会自动根据Routing表生成所有BillItem记录并按标准工时和设备产能初步排定计划时间调用Scheduler.CalculateSchedule()。工单的“智能”体现在两个细节1.动态产能检查当你在日历视图frmCalendarMan拖拽一个工单到新时间系统不会盲目接受。它会调用CapacityChecker.CheckCapacity(billID, newStartDate, newEndDate)这个函数查询Equipment表获取设备#3的可用时段再扫描BillItem表找出所有在同一设备上、时间重叠的其他工单。如果冲突弹窗显示“设备#3在6月18日9:00-11:00已被工单#B2023-088占用建议调整至6月19日”。这比人工排程表可靠得多。工序级追溯工单不是“黑盒”。点击任意工单行的“查看工序”按钮会弹出子窗体展示该工单所有工序的详细状态计划时间、实际开工时间、实际完工时间、报工人、首检结果、巡检记录。这些数据都来自BillItem表而BillItem的Status字段有精细定义NotStarted未开始、InProcess进行中、Completed完成、Rejected拒收。当某个工序状态变为Completed系统会自动检查其后置工序如果所有前置都已完成则将后置工序状态设为InProcess并邮件通知对应班组长——这个自动化逻辑写在BillItem.UpdateStatus()方法里。注意工单的“实际报工”功能frmBillManage里的“报工”按钮是故意设计得极简。它只记录“谁、什么时候、完成了多少件”不采集设备参数、不拍照留痕。因为调研发现车间工人最反感复杂的移动端报工流程他们宁愿用纸质单也不愿对着手机点五六次。所以这个工具的报工就是点击、输入数量、确认——3秒完成。复杂的数据分析留给计划员在后台用查询功能做。3.4 BOM管理为什么BOM是生产系统的“DNA”BOM物料清单常被误解为一张静态的“零件清单”但在这个工具里它是整个系统的“DNA”决定了MPS能否制定、MRP能否运算、工单能否生成。打开frmBase里的BOM管理页你会发现它远比想象中复杂。首先BOM是多版本的。同一个产品A可能有V1.0旧模具、V2.0新模具少一个螺丝、V3.0环保材料版。BOM表里有Version字段且每个版本有独立的生效日期EffectiveDate。MRP运算时系统会自动选取“计划日期”前最新生效的版本。代码逻辑在BOMHelper.GetActiveBOMVersion(productID, planDate)里用一条SQL搞定SELECT TOP 1 * FROM BOM WHERE ProductIDpid AND EffectiveDatedate ORDER BY EffectiveDate DESC。其次BOM是带替代料的。比如螺丝M5×20主用品牌是A但当A缺货时可用品牌B替代用量比例是1:1。BOM表里有AlternatePart字段存储替代料编码。MRP运算到这一层时会先尝试用主料如果主料净需求0且库存不足再检查替代料库存如果替代料充足则建议采购/生产替代料。这个逻辑在CalculateNetRequirement()函数的末尾分支里。第三BOM是可关联工序的。BOM表里有个RoutingID字段指向Routing表。这意味着当你为产品A选择V2.0 BOM时系统自动关联到“新模具版”的工序路线其中“外壳成型”工序的标准工时是1.2小时而V1.0版本是1.5小时——这直接影响工单的排程精度。最后BOM的层级校验是强制的。在保存BOM时系统会递归检查是否存在循环引用比如A用BB又用A如果检测到立即报错“BOM结构错误检测到循环引用请检查子件关系”。这个校验在BOMHelper.ValidateBOMTree()里实现用DFS深度优先搜索算法遍历时间复杂度O(n)但对车间级BOM通常50层毫无压力。实操心得BOM录入是系统上线最耗时的环节。我建议不要一次性录完所有产品而是抓重点——先录清楚TOP20产品的BOM占销量80%其他长尾产品先用“暂定版”占位。另外BOM里的用量单位必须统一比如全部用“个”不要混用“PCS”、“EA”否则MRP计算会出错。这个教训是我帮一家客户上线时踩的坑他们BOM里螺丝用“个”胶水用“ML”结果MRP把10ML胶水当成10个胶水来计算库存差点导致停产。4. 实操全流程从零开始跑通一次完整生产计划闭环现在我们把前面所有的模块串联起来走一遍真实的生产计划闭环。假设你是某家小型电机厂的计划员今天接到销售部的紧急需求“客户要200台YD-120电机6月30日前必须交付”。下面是你用这个工具完成计划的全过程每一步都对应具体的窗体操作和背后的代码逻辑。4.1 第一步录入客户订单与基础资料准备5分钟你打开程序主界面frmMain。首先确认基础资料是否完备- 点击菜单“基础资料”→“产品管理”检查YD-120是否存在。如果不存在点击“新增”填入编码、名称、规格如“三相异步1.5KW”、单位“台”。注意这里的“单位”必须与BOM用量单位一致否则后续计算全错。- 点击“BOM管理”找到YD-120确认V1.0版本已启用且BOM结构正确顶层YD-120子件包括定子用量1、转子用量1、外壳用量1、轴承用量2……一直展开到原材料铜线、硅钢片。- 点击“工序路线”确认YD-120的V1.0路由存在包含“定子绕线”、“转子压铸”、“整机装配”等工序且每个工序的设备、工时都已填写。一切就绪后进入需求录入- 点击菜单“计划管理”→“需求计划”frmProductsDemand。- 在GridView里点击“新增”填入产品编码YD-120需求日期2024-06-30需求数量200客户名称XX公司备注加急订单。- 点击“保存”。此时系统在ProductsDemand表里插入一条记录并自动标记DemandTypeCustomerOrder。提示需求录入时“需求日期”不是交货日而是客户要求的“完成日”。系统会根据累计提前期自动倒推出MPS的“计划日期”。比如YD-120的累计提前期是12天那么它的MPS计划日期就是2024-06-186月30日往前推12个工作日。4.2 第二步制定主生产计划MPS3分钟切换到“MPS制定”frmMPS。点击“刷新”GridView会列出所有未纳入MPS的客户订单按需求日期排序。你看到YD-120的订单在第一行。勾选这一行点击“生成MPS”。系统弹出对话框“请选择计划周期”你选择“2024年6月第3周6月17日-6月23日”。点击确定。系统执行MPSLogic.GenerateMPSFromDemand()函数它读取该订单的需求数量200按计划周期对齐因为6月30日交付计划必须在6月23日前完成所以200台全部放入6月第3周并在MPS表里插入一行ProductIDYD-120PlanWeek2024-W3PlanQty200Status’Draft’。你双击这一行进入编辑检查“计划参数”累计提前期自动显示为12时界设为7天即6月17日-6月23日为计划时界批量规则是“按需批量”安全库存为0电机是按单生产不设安全库存。确认无误点击“保存”。4.3 第三步运行MRP运算生成物料需求1分钟切换到“MRP运算”frmMRP。在左侧树形视图里展开“2024年6月”找到“第3周”再展开YD-120。选中YD-120这一行代表整个MPS计划点击“展开物料”按钮。系统开始运算。进度条走到100%后右侧DataGridView显示出结果。你重点关注几行轴承型号6204毛需求400200台×2个可用库存150净需求250建议采购250个建议下单日2024-06-106月17日计划日减7天采购提前期。铜线规格Φ0.8mm毛需求120kg根据BOM用量折算可用库存85kg净需求35kg建议采购35kg。关键发现定子自制件净需求200台建议“自制”建议开工日2024-06-126月17日减5天自制提前期。这意味着你需要为定子创建一个新工单。4.4 第四步创建并排程自制件工单4分钟在frmMRP的DataGridView里找到“定子”这一行右键点击选择“为选定物料创建工单”。系统自动跳转到frmBillManage并预填好产品定子数量200计划开工日2024-06-12计划完工日2024-06-14根据工序路线的标准工时和设备产能自动计算。你检查工序路线定子的路由有“硅钢片冲压”2小时、“绕线”3小时、“浸漆烘干”8小时。系统已按顺序排好时间。点击“保存”工单#B2024-0612-001创建成功并自动关联到YD-120的MPS计划。切换到“日历管理”frmCalendarMan你看到新工单卡片出现在6月12日的“冲压设备”栏下。你觉得时间太紧想把“浸漆烘干”挪到6月13日下午。于是你拖拽卡片到新位置松手——系统弹窗“设备#2烘干炉在6月13日14:00-22:00空闲调整成功。”同时工单的计划完工日自动更新为2024-06-15。4.5 第五步生成采购申请与跟踪执行2分钟回到frmMRP勾选所有“建议采购”的行轴承、铜线等右键“生成采购申请”。系统在PurchaseRequest表里创建一条记录状态为“待审批”并邮件通知采购主管。下午采购主管登录系统用管理员账号在“采购管理”模块里审批该申请状态变为“In Purchase”并生成采购订单PurchaseOrder表。同时你打开frmBillList筛选状态为“InProcess”的工单看到#B2024-0612-001正在执行。班组长在车间用平板电脑打开程序WinForms应用可打包为UWP适配平板点击“报工”输入“冲压工序完成100件”确认。系统立即更新BillItem表并将工单状态推进到下一工序“绕线”。4.6 第六步闭环验证与动态调整持续进行一周后你发现轴承供应商通知6204型号缺货要延期到7月5日。这时- 你打开frmMRP找到轴承行右键“更新在途采购”将预计到货日改为2024-07-05。- 点击“重算MRP”系统重新计算由于轴承延迟定子的开工日必须推迟进而导致YD-120的交付日无法保证6月30日。系统在结果表顶部高亮显示“警告YD-120交付风险当前计划交付日2024-07-08”。- 你立刻在frmMPS里将YD-120的MPS计划从“6月第3周”调整到“7月第1周”并通知销售部与客户协商。整个闭环从接单到预警耗时不到15分钟。而这一切都建立在WinForms的即时响应、本地数据库的零延迟、以及VB.NET代码对业务逻辑的透明封装之上。它不承诺颠覆制造业但承诺让每一次计划调整都变得清晰、可控、可追溯。5. 常见问题与避坑指南那些只有亲手调试过才会知道的细节在帮学生调试毕业设计、为企业部署这套工具的过程中我整理了一份高频问题清单。这些问题往往不会出现在官方文档里但每一个都曾让我或用户卡住半天。分享出来帮你绕过那些看不见的坑。5.1 数据库连接失败不是密码错了而是路径权限问题现象程序启动时报错“无法打开数据库 ‘mrpbook_Data.MDF’”或“拒绝访问文件”。真相SQL Server LocalDB默认以当前用户身份运行而.mdf文件如果放在Program Files或桌面等受保护路径LocalDB进程没有写入权限。这不是连接字符串的问题Data Source(LocalDB)\MSSQLLocalDB;AttachDbFilename|DataDirectory|\mrpbook_Data.MDF;...是正确的而是Windows文件系统权限问题。解决方案1. 将mrpbook_Data.MDF和mrpbook_Log.LDF文件复制到非系统目录比如C:\MRPBook\Data\。2. 右键该文件夹 → “属性” → “安全”选项卡 → “编辑” → “添加” → 输入NT Service\MSSQL$SQLEXPRESS如果是Express版或NT Service\MSSQLLocalDBLocalDB版→ 勾选“完全控制”→ 确定。3. 修改连接字符串中的AttachDbFilename路径为新位置例如AttachDbFilenameC:\MRPBook\Data\mrpbook_Data.MDF。实操心得我最初也以为是SQL Server没装好折腾了两小时重装LocalDB。后来用Process Monitor监控程序行为才发现是Access Denied错误。记住.MDF文件永远放在用户有完全控制权的目录下这是WinFormsLocalDB部署的铁律。5.2 MRP运算结果为0BOM用量单位与库存单位不匹配现象MRP运行后所有子件的“净需求”都是0明明库存显示只有50个毛需求是100个。真相BOM表里的Quantity字段和Inventory表里的OnHandQty字段单位不一致。比如BOM里写“轴承用量2”意思是2个但Inventory里“轴承库存”却记为“2000克”因为采购入库时按重量计量。MRP计算时毛需求 - 可用库存变成了2 - 2000结果是负数被截断为0。解决方案1. 统一所有物料的计量单位。在Product表里为每个物料定义UnitOfMeasureUOM如“个”、“千克”、“米”。2. 在BOM表和Inventory表里增加UOM字段并确保BOM用量和库存数量使用同一UOM。3. 在MRP计算前加入单位转换逻辑If bom.UOM inventory.UOM Then convertedQty ConvertUnit(inventory.OnHandQty, inventory.UOM, bom.UOM)。这个ConvertUnit函数需要你根据物料的密度、长度等参数预先配置转换率。注意这个坑在导入历史数据时最易发生。建议上线前用SQL脚本检查SELECT b.ProductID, b.Quantity, i.OnHandQty FROM BOM b JOIN Inventory i ON b.ChildIDi.ProductID WHERE b.UOM ! i.UOM把结果导出人工核对。5.3 日历拖拽失效不是代码bug而是DPI缩放惹的祸现象在高分辨率屏幕如2K、4K的Windows 10/11上frmCalendarMan的日历视图可以显示但拖拽工单卡片时鼠标位置与卡片位置严重偏移仿佛“隔空操作”。真相WinForms默认不支持高DPI缩放。当系统DPI设置为125%或150%时控件的实际像素尺寸被放大但鼠标坐标的获取e.X, e.Y仍是逻辑坐标导致计算出的卡片新位置错误。解决方案在项目属性 → “应用程序” → “视图DPI感知”中勾选“启用DPI感知”。然后在frmCalendarMan的构造函数里添加强制缩放适配Public Sub New() InitializeComponent() 启用DPI感知 SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint, True) 获取当前DPI缩放因子 Dim dpiScale As Single Me.CreateGraphics().DpiX / 96.0F 对所有Panel控件应用缩放 For Each pnl As Panel In Me.Controls.OfType(Of Panel)() pnl.Width CInt(pnl.Width * dpiScale) pnl.Height CInt(pnl.Height * dpiScale) pnl.Left CInt(pnl.Left * dpiScale) pnl.Top CInt(pnl.Top * dpiScale) Next End Sub提示这个适配在低DPI屏幕100%下也完全兼容所以建议所有WinForms项目都加上。学生做毕业设计时如果答辩用的是学校投影仪通常是100% DPI而开发用的是自己的高分屏125%不加这个答辩现场必出洋相。5.4 工单状态不更新事件订阅漏掉了现象在frmBillManage里修改了工单状态点击“保存”但frmBillList的GridView没有刷新还是显示旧状态。真相WinForms的数据绑定依赖于BindingSource的事件通知。如果在frmBillManage里你直接修改了DataSet.Tables(“Bill”).Rows(0)(“Status”)但没有调用BindingSource.EndEdit()或DataSet.AcceptChanges()那么BindingSource不知道数据已变自然不会刷新UI。解决方案所有对DataSet的修改必须遵循标准流程 正确做法 Dim row As DataRow ds.Tables(Bill).Rows.Find(billID) row(Status) Completed bsBill.EndEdit() 通知BindingSource ds.AcceptChanges() 提交到DataSet 如果需要立即刷新列表再调用 bsBill.ResetBindings(False)避坑口诀改DataRow → 调EndEdit → 再AcceptChanges → 最后ResetBindings。少一步UI就不同步。5.5 中文乱码与字体模糊资源文件.resx的隐藏陷阱现象程序在某些电脑上窗体文字显示为方块乱码或字体边缘发虚、看不清。真相.resx资源文件在编译时会将字符串按系统默认编码ANSI存储。如果开发机是中文WindowsGBK编码而目标机是英文WindowsASCII或者反之就会乱码。字体模糊则是WinForms默认使用GDI渲染抗锯齿不佳。解决方案1.解决乱码在Visual Studio中右键每个.resx文件 → “属性” → 将“复制到输出目录”设为“始终复制”并在项目文件.vbproj里添加xml PropertyGroup PreferredUILangzh-CN/PreferredUILang GenerateEncodingUTF-8/GenerateEncoding /PropertyGroup确保所有字符串资源以UTF-8编码保存。2.解决字体模糊在每个窗体的Load事件里添加vb Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load 启用高质量文本渲染 Me.SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint, True) 设置字体为微软雅黑清晰锐利 Me.Font New Font(Microsoft YaHei, 9.0F, FontStyle.Regular, GraphicsUnit.Point, 134) End Sub最后一个小技巧如果企业要部署到老旧XP系统虽然不推荐记得在项目属性 → “应用程序” → “目标框架”里将.NET Framework版本降为4.0并在安装包里捆绑.NET 4.0运行库。现代VS默认是4.7.2以上XP不支持。这套工具的价值从来不在它有多炫酷而在于它把生产计划里那些“应该如此”的常识变成了“确实如此”的代码。当你亲手修复了一个DPI缩放bug当你第一次看到MRP准确算出缺料清单当你拖拽工单时听到那声清脆的“滴”提示音——那一刻你触摸到的不是VB.NET语法而是制造业最朴素的逻辑输入、处理、输出环环相扣不容马虎。本文还有配套的精品资源点击获取简介这是一款用VB.NET开发的Windows桌面生产管理程序主要面向中小制造企业或教学实践场景能完成从客户订单输入到生成具体生产工单的全流程辅助。系统内置主生产计划MPS制定界面可按产品、时间周期设定计划产量通过MRP运算模块自动展开物料需求考虑现有库存、在途采购和安全库存输出采购建议与自制件计划提供可视化日历排程视图支持拖拽调整工单执行时间工单管理模块涵盖新建、查询、状态更新与工序关联基础资料部分维护产品BOM物料清单、工序路线、供应商及仓库信息。所有数据本地存储于SQL Server数据库文件.MDF/.LDF程序采用WinForms架构含完整窗体设计如frmMain主界面、frmMRP运算页、frmBillManage工单编辑页等、资源文件.resx、图标mrp.ico及项目配置.vbproj可直接用Visual Studio 2019打开编译运行无需额外部署服务器适合毕业设计参考或轻量级车间调度试用。本文还有配套的精品资源点击获取