你是否曾经为生产计划的混乱而头疼?库存积压和缺料风险让你夜不能寐?作为一名C#开发者,我将带你用代码构建一套完整的MRP(物料需求计划)系统。这不仅仅是一个技术演示,更是一个能够真正解决制造业痛点的实战项目。在这篇文章中,我们将使用C#和Spectre.Console框架,从BOM结构设计到实时库存预警,一步步搭建一个功能完备的智能制造系统,当然这里只是给一个设计逻辑与最小实例。
在传统制造企业中,生产计划往往面临三大核心难题:
📊 库存预测不准确:无法精确预知何时会缺料,导致生产中断或库存积压
⏰ 信息滞后严重:Excel表格满天飞,数据更新不及时,决策总是慢半拍
🔗 部门协同困难:生产、采购、仓储各自为战,缺乏统一的数据视图
这些问题的本质是缺乏一个实时、智能的物料需求计划系统。而C#凭借其强大的面向对象特性和丰富的生态系统,恰好是构建此类系统的理想选择。
一个完整的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
};
}
传统的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;
}
⚡ 性能优化要点:
数据可视化是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设计技巧:
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));
}
⏰ 定时器使用要点:
当库存出现风险时,系统应该立即发出预警。我们使用分级预警策略:
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]⚪ 未知[/]"
};
}


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();
}
c#// ❌ 错误做法:直接比较DateTime可能因为精度问题匹配失败
var exactMatch = _productionPlan.Where(p => p.ProductionTime == forecastTime);
// ✅ 正确做法:使用时间窗口匹配
var windowMatch = _productionPlan
.Where(p => Math.Abs((p.ProductionTime - forecastTime).TotalMinutes) < 60);
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 许可协议。转载请注明出处!