工业 MES 系统源代码 Wpf C# WPF开发。 WPF MES 上位机产线执行系统。 1, 完整纯源代码; 2, AGV自动调度; 3, SQLSERVER数据库。 带附加文件。 4, WPF各种技术应用。 5, 数据库技术应用。 6, DTU数据传输。 7, TCP IP SOCKET技术应用。 8, EXCEL数据查询与导出。 9, 各种库位的管理。 10,重要是多线程技术应用。

最近在车间蹲了三个月搞出来的WPF版MES系统,今天挑几个硬核技术点跟大伙唠唠。这系统不仅要扛住产线实时数据轰炸,还得玩转AGV调度、库位管理这些骚操作,代码里全是实战干货。

UI与业务解耦玩到飞起

WPF的数据绑定和MVVM模式必须得整明白了。看这段库存看板的XAML:

<ItemsControl ItemsSource="{Binding Bins}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Background="{Binding Status,Converter={StaticResource StatusToBrush}}">
                <StackPanel>
                    <TextBlock Text="{Binding Code}" FontWeight="Bold"/>
                    <TextBlock Text="{Binding MaterialName}"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

对应的ViewModel直接上ObservableCollection动态刷新:

public class WarehouseVM : INotifyPropertyChanged
{
    private ObservableCollection<Bin> _bins = new();
    public ObservableCollection<Bin> Bins
    {
        get => _bins;
        set => SetField(ref _bins, value);
    }
    
    // 库位状态变化时触发界面更新
    private void OnAGVMoved(AGVMessage msg)
    {
        var targetBin = Bins.FirstOrDefault(b => b.Code == msg.TargetBin);
        targetBin.Status = BinStatus.Occupied;
    }
}

这种数据驱动的方式比直接操作UI控件优雅多了,特别是当AGV调度触发库位状态变化时,界面自动跟着跑,真香!

多线程生存指南

产线数据采集每秒上百条记录,不上多线程分分钟卡成狗。看这个数据采集器的双缓冲队列:

private ConcurrentQueue<DeviceData> _dataBuffer = new();
private readonly object _syncLock = new();

// 采集线程
void CollectData()
{
    while (!_cancellationToken.IsCancellationRequested)
    {
        var rawData = _plc.Read(); // 耗时IO操作
        _dataBuffer.Enqueue(ParseData(rawData));
        
        if (_dataBuffer.Count >= 100)
        {
            lock (_syncLock)
            {
                // 批量插入数据库
                var temp = _dataBuffer.ToArray();
                _db.BulkInsert(temp);
                _dataBuffer.Clear();
            }
        }
    }
}

// UI定时器显示最新数据
void RefreshTimer_Tick(object sender, EventArgs e)
{
    lock (_syncLock)
    {
        var latest = _dataBuffer.LastOrDefault();
        CurrentSpeed = latest?.Speed ?? 0;
    }
}

这里用了生产者-消费者模式,采集线程疯狂灌数据,UI线程定时取最新值。注意那个双重锁——既要保证批量插入的原子性,又要避免UI卡顿。

工业 MES 系统源代码 Wpf C# WPF开发。 WPF MES 上位机产线执行系统。 1, 完整纯源代码; 2, AGV自动调度; 3, SQLSERVER数据库。 带附加文件。 4, WPF各种技术应用。 5, 数据库技术应用。 6, DTU数据传输。 7, TCP IP SOCKET技术应用。 8, EXCEL数据查询与导出。 9, 各种库位的管理。 10,重要是多线程技术应用。

TCP/IP通讯的坑

跟AGV小车通讯用Socket,最头疼的就是粘包问题。咱们自定义了个简单协议:

// 协议格式:4字节长度头 + JSON内容
async Task HandleClient(TcpClient client)
{
    var stream = client.GetStream();
    var headerBuffer = new byte[4];
    
    while (true)
    {
        // 读包头
        await stream.ReadExactlyAsync(headerBuffer, 0, 4);
        int bodyLength = BitConverter.ToInt32(headerBuffer, 0);
        
        // 读包体
        var bodyBuffer = new byte[bodyLength];
        await stream.ReadExactlyAsync(bodyBuffer, 0, bodyLength);
        
        var json = Encoding.UTF8.GetString(bodyBuffer);
        var cmd = JsonConvert.DeserializeObject<AGVCommand>(json);
        
        // 扔到任务队列处理
        _commandQueue.Add(cmd);
    }
}

ReadExactlyAsync是自己封装的,必须读满指定字节数,否则在网卡的时候容易出幺蛾子。处理完的命令直接扔进队列,由专门的调度线程处理,避免阻塞网络IO。

Excel导出神操作

车间主任最爱要Excel报表,用EPPlus库比Interop强十条街:

public void ExportToExcel(IEnumerable<ProductionRecord> records)
{
    using var package = new ExcelPackage();
    var sheet = package.Workbook.Worksheets.Add("生产报表");
    
    // 设置标题样式
    sheet.Cells[1,1].Value = "日期";
    sheet.Cells[1,2].Value = "良品率";
    using(var range = sheet.Cells[1,1,1,2])
    {
        range.Style.Font.Bold = true;
        range.Style.Fill.PatternType = ExcelFillStyle.Solid;
        range.Style.Fill.BackgroundColor.SetColor(Color.LightBlue);
    }
    
    // 填充数据
    int row = 2;
    foreach (var r in records)
    {
        sheet.Cells[row, 1].Value = r.Date.ToString("yyyy-MM-dd");
        sheet.Cells[row, 2].Value = r.YieldRate;
        row++;
    }
    
    // 自适应列宽
    sheet.Cells[sheet.Dimension.Address].AutoFitColumns();
    File.WriteAllBytes("Report.xlsx", package.GetAsByteArray());
}

这比用COM组件快得多,而且不怕进程残留。注意样式设置要放在循环外面,否则生成大文件时内存会爆炸。

搞工业软件最刺激的就是各种突发状况——上周五AGV小车突然集体抽风,后来发现是线程池被数据库查询堵死了。赶紧上了SemaphoreSlim做限流:

private SemaphoreSlim _dbThrottler = new(10, 10); // 最大10并发

async Task QueryYieldRate()
{
    await _dbThrottler.WaitAsync();
    try
    {
        using var conn = new SqlConnection(_connStr);
        await conn.QueryAsync<YieldData>("SELECT...");
    }
    finally
    {
        _dbThrottler.Release();
    }
}

现在系统稳如老狗,管你几百台设备同时发数据,数据库连接数稳稳的。下次有机会再聊聊怎么用Redis做实时看板的二级缓存,那又是另一个血泪故事了。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐