编辑
2026-01-04
C#
00

目录

🎯 制造业的数字化痛点
💡 MRP系统架构设计
🏗️ 核心模块划分
🎨 界面美化利器:Spectre.Console
🔥 核心算法实现
📈 智能库存预测算法
🎪 动态表格展示
⚡ 实时监控与自动化
🔄 多线程任务调度
🚨 智能预警系统
运行效果
🛠️ 常见坑点与解决方案
❌ 坑点1:Timer内存泄漏
❌ 坑点2:时间计算精度问题
❌ 坑点3:控制台编码问题
🚀 性能优化与扩展建议
📊 数据结构优化
🔌 扩展接口设计
🎯 总结与展望

你是否曾经为生产计划的混乱而头疼?库存积压和缺料风险让你夜不能寐?作为一名C#开发者,我将带你用代码构建一套完整的MRP(物料需求计划)系统。这不仅仅是一个技术演示,更是一个能够真正解决制造业痛点的实战项目。在这篇文章中,我们将使用C#和Spectre.Console框架,从BOM结构设计到实时库存预警,一步步搭建一个功能完备的智能制造系统,当然这里只是给一个设计逻辑与最小实例。

🎯 制造业的数字化痛点

在传统制造企业中,生产计划往往面临三大核心难题:

📊 库存预测不准确:无法精确预知何时会缺料,导致生产中断或库存积压

⏰ 信息滞后严重:Excel表格满天飞,数据更新不及时,决策总是慢半拍

🔗 部门协同困难:生产、采购、仓储各自为战,缺乏统一的数据视图

这些问题的本质是缺乏一个实时、智能的物料需求计划系统。而C#凭借其强大的面向对象特性和丰富的生态系统,恰好是构建此类系统的理想选择。

💡 MRP系统架构设计

🏗️ 核心模块划分

一个完整的MRP系统应该包含以下核心模块:

c#
// MRP预测记录 - 系统的数据核心 public class MRPForecastRecord { public string MaterialCode { get; set; } // 物料编码 public DateTime ForecastTime { get; set; } // 预测时间 public decimal DemandQuantity { get; set; } // 需求数量 public decimal ProjectedStock { get; set; } // 预测库存 public decimal ASNQuantity { get; set; } // ASN到货数量 public string Status { get; set; } // 库存状态 } // 简化的BOM结构 - 生产配方管理 public class SimpleBOM { public string ProductCode { get; set; } = "PROD001"; public Dictionary<string, decimal> MaterialUsage { get; set; } = new Dictionary<string, decimal> { { "MAT001", 1.5m }, // 每个产品需要1.5KG原料001 { "MAT002", 0.8m }, // 每个产品需要0.8M原料002 { "MAT003", 2.0m } // 每个产品需要2.0PCS原料003 }; }

🎨 界面美化利器:Spectre.Console

传统的Console应用往往界面单调,用户体验差。Spectre.Console为我们提供了丰富的UI组件:

c#
private void ShowStartupBanner() { var rule = new Rule("[bold blue]MRP表格输出系统[/]") .RuleStyle("blue") .LeftJustified(); AnsiConsole.Write(rule); var infoPanel = new Panel($"[green]📦 产品:[/] {_bom.ProductCode}\n[yellow]⏰ 更新频率:[/] 30秒\n[cyan]📊 预测周期:[/] 24小时") .Border(BoxBorder.Rounded) .Header("系统信息") .HeaderAlignment(Justify.Center); AnsiConsole.Write(infoPanel); }

💡 实战技巧:使用Markup语法可以轻松实现彩色文本,[bold blue] 表示加粗蓝色文字,大大提升用户体验。

🔥 核心算法实现

📈 智能库存预测算法

MRP系统的核心是库存预测算法。我们需要基于生产计划和供应计划,精确计算未来每个时间点的库存状态:

c#
private List<MRPForecastRecord> CalculateMaterialForecast(string materialCode, DateTime startTime, int forecastHours) { var records = new List<MRPForecastRecord>(); var currentStock = _materialStocks[materialCode].OnHandQuantity; var materialUsagePerUnit = _bom.MaterialUsage[materialCode]; for (int hour = 1; hour <= forecastHours; hour++) { var forecastTime = startTime.AddHours(hour); // 🎯 关键算法:计算生产需求 var production = _productionPlan .Where(p => Math.Abs((p.ProductionTime - forecastTime).TotalMinutes) < 60) .FirstOrDefault(); decimal demandQty = production?.PlannedQuantity * materialUsagePerUnit ?? 0; // 🚚 计算ASN到货量 var asn = _asnDeliveries .Where(a => a.MaterialCode == materialCode && Math.Abs((a.PlannedArrivalTime - forecastTime).TotalMinutes) < 60) .FirstOrDefault(); decimal asnQty = asn?.Quantity ?? 0; // 📊 预测库存 = 当前库存 - 需求 + 到货 currentStock = currentStock - demandQty + asnQty; // 只记录关键时间点 if (demandQty > 0 || asnQty > 0 || currentStock < _materialStocks[materialCode].SafetyStock) { records.Add(new MRPForecastRecord { MaterialCode = materialCode, ForecastTime = forecastTime, DemandQuantity = demandQty, ProjectedStock = currentStock, ASNQuantity = asnQty, Status = GetForecastStatus(currentStock, _materialStocks[materialCode].SafetyStock) }); } } return records; }

⚡ 性能优化要点

  • 使用LINQ的Where和FirstOrDefault进行高效筛选
  • 只记录有意义的时间点,避免大量冗余数据
  • 采用滑动窗口算法,保证预测的实时性

🎪 动态表格展示

数据可视化是MRP系统的重要组成部分。Spectre.Console的Table组件让我们能够创建专业级的数据展示:

c#
private void RunMRPForecastTable(object state) { // 获取预测数据 var forecastRecords = new List<MRPForecastRecord>(); foreach (var material in _materialStocks.Values) { var records = CalculateMaterialForecast(material.MaterialCode, DateTime.Now, 24); forecastRecords.AddRange(records); } // 🎯 智能筛选:优先显示风险记录 var riskRecords = forecastRecords .Where(r => r.ProjectedStock < _materialStocks[r.MaterialCode].SafetyStock || r.ProjectedStock < 0) .OrderBy(r => r.ForecastTime) .Take(10) .ToList(); // 创建动态表格 var table = new Table() .Border(TableBorder.Rounded) .BorderColor(riskRecords.Any() ? Color.Red : Color.Blue) .Title(riskRecords.Any() ? "[bold red]⚠️ 风险预警记录[/]" : "[bold green]📊 MRP预测记录[/]"); // 添加列定义 table.AddColumn(new TableColumn("[bold cyan]零件号[/]").Centered()); table.AddColumn(new TableColumn("[bold cyan]预测时间[/]").Centered()); table.AddColumn(new TableColumn("[bold cyan]需求数量[/]").RightAligned()); table.AddColumn(new TableColumn("[bold cyan]预测库存[/]").RightAligned()); table.AddColumn(new TableColumn("[bold cyan]ASN数量[/]").RightAligned()); table.AddColumn(new TableColumn("[bold cyan]状态[/]").Centered()); // 填充数据并设置样式 foreach (var record in riskRecords.Any() ? riskRecords : forecastRecords.Take(10)) { table.AddRow( $"[bold]{record.MaterialCode}[/]", $"{record.ForecastTime:MM-dd HH:mm}", $"{record.DemandQuantity:F1}", GetStockMarkup(record.ProjectedStock, _materialStocks[record.MaterialCode].SafetyStock), $"{record.ASNQuantity:F1}", GetStatusMarkup(record.Status) ); } AnsiConsole.Write(table); }

🎨 UI设计技巧

  • 使用条件样式:风险数据用红色边框,正常数据用蓝色边框
  • 数值对齐:数字列右对齐,文本列居中对齐,提升可读性
  • 状态标记:用emoji和颜色快速传达库存状态

⚡ 实时监控与自动化

🔄 多线程任务调度

MRP系统需要同时处理多个实时任务:生产模拟、ASN到货、库存预测。使用Timer实现精确的任务调度:

c#
public SimpleMRPSimulator() { // 初始化各种组件... // 🏭 每1分钟模拟生产消耗 _productionTimer = new Timer(SimulateProduction, null, TimeSpan.FromSeconds(5), TimeSpan.FromMinutes(1)); // 📊 每30秒执行MRP预测并更新表格 _forecastTimer = new Timer(RunMRPForecastTable, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30)); // 📦 每3分钟模拟ASN到货 _asnTimer = new Timer(SimulateASNArrival, null, TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(3)); }

⏰ 定时器使用要点

  • 设置不同的启动延时,避免任务冲突
  • 合理设置执行间隔,平衡实时性和系统负载
  • 记得在程序退出时正确释放Timer资源

🚨 智能预警系统

当库存出现风险时,系统应该立即发出预警。我们使用分级预警策略:

c#
private string GetForecastStatus(decimal projectedStock, decimal safetyStock) { if (projectedStock < 0) return "零料"; // 🔴 最高级别:库存为负 else if (projectedStock < safetyStock * 0.5m) return "极低"; // 🟠 高级别:低于安全库存50% else if (projectedStock < safetyStock) return "偏低"; // 🟡 中级别:低于安全库存 else return "正常"; // 🟢 正常状态 } private string GetStatusMarkup(string status) { return status switch { "零料" => "[red]🔴 零料[/]", "极低" => "[red]🟠 极低[/]", "偏低" => "[yellow]🟡 偏低[/]", "正常" => "[green]🟢 正常[/]", _ => "[dim]⚪ 未知[/]" }; }

运行效果

image.png

image.png

🛠️ 常见坑点与解决方案

❌ 坑点1:Timer内存泄漏

c#
// ❌ 错误做法:忘记释放Timer public class BadExample { private Timer _timer; public BadExample() { _timer = new Timer(Callback, null, 0, 1000); // 程序退出时Timer没有被释放,造成内存泄漏 } } // ✅ 正确做法:实现IDisposable接口 public void Stop() { _productionTimer?.Dispose(); _forecastTimer?.Dispose(); _asnTimer?.Dispose(); }

❌ 坑点2:时间计算精度问题

c#
// ❌ 错误做法:直接比较DateTime可能因为精度问题匹配失败 var exactMatch = _productionPlan.Where(p => p.ProductionTime == forecastTime); // ✅ 正确做法:使用时间窗口匹配 var windowMatch = _productionPlan .Where(p => Math.Abs((p.ProductionTime - forecastTime).TotalMinutes) < 60);

❌ 坑点3:控制台编码问题

c#
// ✅ 在Main方法中设置UTF-8编码,确保emoji正常显示 static async Task Main(string[] args) { Console.OutputEncoding = System.Text.Encoding.UTF8; // ...其他代码 }

🚀 性能优化与扩展建议

📊 数据结构优化

对于大规模数据处理,建议使用以下优化策略:

c#
// 使用Dictionary提高查询性能 private readonly Dictionary<string, MaterialStock> _materialStocks; // 使用LINQ的延迟执行和索引 var riskRecords = forecastRecords .Where(r => r.ProjectedStock < _materialStocks[r.MaterialCode].SafetyStock) .OrderBy(r => r.ForecastTime) .Take(10) // 只取前10条,避免处理大量数据 .ToList();

🔌 扩展接口设计

c#
// 为未来扩展预留接口 public interface IMRPDataProvider { Task<List<ProductionPlan>> GetProductionPlanAsync(DateTime startTime, DateTime endTime); Task<List<ASNDelivery>> GetASNDeliveriesAsync(string materialCode); Task UpdateMaterialStockAsync(string materialCode, decimal quantity); }

🎯 总结与展望

通过这个完整的MRP系统实战项目,我们不仅学习了C#在企业级应用中的实际运用,更重要的是掌握了以下三个核心技能:

🏗️ 系统架构设计能力:学会了如何将复杂的业务需求拆解为可维护的代码模块,采用面向对象的设计思想构建稳健的系统架构。

⚡ 实时数据处理技术:掌握了Timer多线程调度、LINQ高效查询、以及内存管理等关键技术,这些技能在任何实时系统开发中都至关重要。

🎨 用户体验优化实践:通过Spectre.Console框架的运用,我们看到了如何让原本枯燥的控制台应用变得生动有趣,这种用户体验思维同样适用于Web和桌面应用开发。

这套MRP系统虽然是一个演示项目,但其核心思想和技术架构完全可以应用到真实的生产环境中。你可以将其扩展为Web API、集成数据库、添加用户权限管理等功能,打造一个完整的企业级解决方案。

💭 互动讨论:你在实际项目中遇到过类似的实时数据处理需求吗?或者你对这个MRP系统还有哪些功能扩展的想法?欢迎在评论区分享你的经验和建议。如果这篇文章对你有帮助,别忘了转发给更多需要的同行!

相关信息

通过网盘分享的文件:AppMRPSimulator.zip 链接: https://pan.baidu.com/s/15PUzBjNNArRlEHW6JLQf6g?pwd=h65h 提取码: h65h --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!