1. 项目背景与核心需求
去年接手了一个工业自动化改造项目,客户需要一套能够实时监控生产线状态的上位机系统。作为主力开发,我选择了C# WPF+MVVMLight的技术方案,主要解决以下几个痛点:
- 产线数据可视化程度低 - 操作员需要频繁查看PLC寄存器值
- 设备控制方式单一 - 只能通过HMI按钮操作
- 历史数据追溯困难 - 依赖人工记录生产日志
- 报警响应不及时 - 故障发生时无法主动提醒
系统最终实现了与西门子S7-1200 PLC的稳定通信,包含实时数据监控、趋势分析、报警管理、设备控制四大核心模块。下面分享具体实现中的关键技术细节。
2. 技术架构设计
2.1 整体技术栈选型
选择WPF而非WinForms主要基于三点考虑:
- 数据绑定机制更完善,适合频繁更新的工业数据场景
- 矢量图形支持更好,便于实现自定义控件(如趋势图)
- 现代化UI开发体验(XAML+MVVM)
mermaid复制graph TD
A[WPF界面] -->|绑定| B[ViewModel]
B -->|调用| C[PLC服务层]
C -->|读写| D[西门子PLC]
B -->|操作| E[SQLite数据库]
注意:实际项目中应避免过度设计,通信模块需要特别注意线程安全问题
2.2 MVVMLight框架实践
在ViewModel层我们做了这些关键设计:
- 使用
ObservableObject基类实现属性通知 - 通过
Messenger实现跨模块通信(如报警触发) - 自定义
RelayCommand处理UI交互
csharp复制// 典型ViewModel结构
public class MonitorViewModel : ObservableObject
{
private double _currentPressure;
public double CurrentPressure
{
get => _currentPressure;
set => Set(ref _currentPressure, value);
}
public RelayCommand ResetAlarmCommand { get; }
public MonitorViewModel()
{
ResetAlarmCommand = new RelayCommand(() => {
// 处理复位逻辑
});
}
}
3. PLC通信实现
3.1 通信方案对比
测试了三种西门子PLC通信方案:
| 方案 | 协议支持 | 性能 | 稳定性 | 开发难度 |
|---|---|---|---|---|
| LibNoDave | S7 | 中 | 高 | 高 |
| S7NetPlus | S7 | 高 | 中 | 中 |
| Sharp7 | S7 | 高 | 高 | 低 |
最终选择Sharp7因为:
- 异步API设计更友好
- 支持.NET Standard 2.0
- 社区维护活跃
3.2 关键通信代码
csharp复制// PLC连接管理
public class PlcService : IDisposable
{
private S7Client _client = new S7Client();
private Timer _pollingTimer;
public void Connect(string ip, int rack, int slot)
{
int result = _client.ConnectTo(ip, rack, slot);
if (result == 0)
{
_pollingTimer = new Timer(100); // 100ms轮询周期
_pollingTimer.Elapsed += PollData;
_pollingTimer.Start();
}
}
private void PollData(object sender, ElapsedEventArgs e)
{
byte[] buffer = new byte[1024];
_client.DBRead(1, 0, buffer.Length, buffer);
// 触发数据更新事件...
}
}
踩坑记录:初期未加锁导致的多线程冲突问题,后来通过ReaderWriterLockSlim解决
4. 数据可视化实现
4.1 实时趋势图
使用LiveCharts实现的关键配置:
xml复制<lvc:CartesianChart Series="{Binding SeriesCollection}">
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding DateTimeFormatter}"/>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
数据更新策略:
- 固定保留最近200个数据点
- 采用环形缓冲区减少内存分配
- UI更新限制在30FPS
4.2 报警指示灯
通过DataTemplate实现状态可视化:
xml复制<DataTemplate DataType="{x:Type local:AlarmItem}">
<Border Background="{Binding Level, Converter={StaticResource AlarmLevelToBrush}}">
<TextBlock Text="{Binding Message}"/>
</Border>
</DataTemplate>
5. 数据库设计
SQLite表结构设计:
sql复制CREATE TABLE ProductionData (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
ProductCode TEXT,
Pressure REAL,
Thickness REAL,
IsDefective BOOLEAN
);
CREATE TABLE AlarmLog (
Id INTEGER PRIMARY KEY,
Code INTEGER,
StartTime DATETIME,
EndTime DATETIME,
Acknowledged BOOLEAN
);
使用EF Core时的优化点:
- 启用WAL模式提升并发性能
- 配置适当的PRAGMA设置
- 采用批量插入代替单条提交
6. 部署与性能优化
6.1 安装包制作
使用WiX Toolset制作MSI安装包:
- 自动安装.NET Framework 4.7.2
- 创建桌面快捷方式
- 注册系统服务(可选)
6.2 性能调优
通过BenchmarkDotNet测试发现:
- 数据绑定是性能瓶颈
- 不必要的UI更新消耗资源
优化措施:
- 对频繁更新的属性实现延迟通知
- 虚拟化长列表控件
- 禁用动画效果
7. 典型问题排查
7.1 通信中断问题
现象:运行一段时间后PLC连接断开
排查步骤:
- 检查物理连接
- 确认PLC没有进入STOP模式
- 分析Sharp7日志
- 最终发现是防火墙规则问题
7.2 内存泄漏问题
使用DotMemory定位到:
- 未注销的事件处理器
- 静态集合持续增长
- 解决方案:
- 实现IDisposable
- 使用WeakEventManager
8. 扩展功能实现
8.1 OPC UA支持
通过OPCFoundation.NETStandard库:
csharp复制var endpoint = new Uri("opc.tcp://localhost:48010");
using (var client = new UAClient())
{
client.Connect(endpoint);
var nodeId = "ns=2;s=MyVariable";
var value = client.ReadValue(nodeId);
}
8.2 WebAPI集成
添加ASP.NET Core WebAPI项目:
- 提供RESTful接口查询数据
- 使用SignalR实现实时推送
- JWT身份验证
9. 项目总结
经过三个月的开发和调试,系统已稳定运行在客户现场。几个关键数据:
- 平均通信延迟 < 50ms
- 可同时监控200+数据点
- 7x24小时无故障运行
后续改进方向:
- 增加机器学习异常检测
- 支持移动端查看
- 完善配置工具
开发此类系统的建议:
- 通信模块要做充分测试
- 界面响应速度是用户体验关键
- 日志系统要完善
- 预留足够的扩展接口