1. 项目背景与核心需求
在工业自动化领域,上位机系统作为连接操作人员与底层设备的桥梁,其稳定性和响应速度直接影响生产效率。传统WinForm开发模式在复杂界面交互和数据绑定方面存在明显短板,这正是我们选择WPF+MVVM架构的主要原因。
MVVMLight框架作为轻量级MVVM实现方案,完美解决了WPF应用中数据绑定与业务逻辑分离的问题。通过其Messenger组件实现松耦合通信,ViewModelLocator提供视图模型定位服务,特别适合需要频繁更新UI的PLC监控场景。
PLC通讯方面,工业现场通常采用以下几种协议:
- Modbus RTU/TCP(通用协议)
- Siemens S7(西门子专有)
- Mitsubishi MC(三菱专有)
- Omron FINS(欧姆龙专有)
伺服控制则涉及:
- 脉冲控制(基础定位)
- 总线控制(EtherCAT/CANopen)
- 速度/扭矩模式切换
2. 环境搭建与框架配置
2.1 开发环境准备
xml复制<PackageReference Include="MvvmLightLibsStd10" Version="5.4.1" />
<PackageReference Include="HslCommunication" Version="8.0.2" />
推荐使用Visual Studio 2019+社区版,需安装.NET Desktop Development工作负载。HslCommunication库提供了跨厂商PLC通讯支持,实测在汇川、三菱等品牌PLC上表现稳定。
2.2 MVVMLight核心组件初始化
csharp复制static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// 注册PLC服务
SimpleIoc.Default.Register<IPlcService, SiemensS7Service>();
// 注册视图模型
SimpleIoc.Default.Register<MainViewModel>();
}
关键提示:ViewModelLocator应配置为静态资源,在App.xaml中全局引用:
xaml复制<Application.Resources> <vm:ViewModelLocator x:Key="Locator"/> </Application.Resources>
3. PLC通讯层实现
3.1 通讯协议封装
建议采用抽象工厂模式实现多协议支持:
csharp复制public interface IPlcService
{
bool Connect(string ip, int port);
short ReadInt16(string address);
void Write(string address, object value);
}
// 西门子S7实现示例
public class SiemensS7Service : IPlcService
{
private Plc _plc;
public bool Connect(string ip, int port)
{
_plc = new Plc(CpuType.S71200, ip, port);
return _plc.Open().IsSuccess;
}
public short ReadInt16(string address)
{
var result = _plc.Read(address);
return result.Value;
}
}
3.2 实时数据绑定
通过MVVMLight的属性绑定实现UI自动更新:
csharp复制public class MainViewModel : ViewModelBase
{
private readonly IPlcService _plcService;
private short _currentValue;
public short CurrentValue
{
get => _currentValue;
set => Set(ref _currentValue, value);
}
public MainViewModel(IPlcService plcService)
{
_plcService = plcService;
Task.Run(async () => {
while(true)
{
CurrentValue = _plcService.ReadInt16("DB1.DBW0");
await Task.Delay(100);
}
});
}
}
4. 伺服运动控制实现
4.1 运动指令封装
csharp复制public class ServoController
{
private readonly IPlcService _plcService;
public ServoController(IPlcService plcService)
{
_plcService = plcService;
}
public void MoveAbsolute(int axis, double position)
{
_plcService.Write($"DB{axis}.DBD0", (float)position);
_plcService.Write($"M{axis * 10}.0", true);
}
public bool IsAxisReady(int axis)
{
return _plcService.ReadInt16($"DB{axis}.DBX1.0") == 1;
}
}
4.2 多轴同步控制
利用C#的async/await实现复杂运动序列:
csharp复制public async Task PerformMultiAxisMoveAsync()
{
var tasks = new List<Task>();
for(int i=0; i<3; i++)
{
int axis = i; // 闭包陷阱规避
tasks.Add(Task.Run(() => {
_servoController.MoveAbsolute(axis, _targetPositions[axis]);
while(!_servoController.IsAxisReady(axis))
Thread.Sleep(10);
}));
}
await Task.WhenAll(tasks);
}
5. 异常处理与调试技巧
5.1 PLC通讯超时处理
csharp复制public short SafeReadInt16(string address)
{
try
{
return _plcService.ReadInt16(address);
}
catch(TimeoutException ex)
{
Messenger.Default.Send(new NotificationMessage("PLC通讯超时"));
return 0;
}
}
5.2 运动控制安全机制
csharp复制public void EmergencyStop()
{
// 立即停止所有轴
Parallel.For(0, _axisCount, i => {
_plcService.Write($"DB{i}.DBX2.0", true);
});
// 状态复位
Task.Delay(500).ContinueWith(_ => {
Parallel.For(0, _axisCount, i => {
_plcService.Write($"DB{i}.DBX2.0", false);
});
});
}
6. 性能优化实践
6.1 批量读取优化
csharp复制public Dictionary<string, object> BatchRead(params string[] addresses)
{
var results = new Dictionary<string, object>();
var batch = _plc.CreateBatchRead();
foreach(var addr in addresses)
{
batch.Add<short>(addr, val => results[addr] = val);
}
batch.Execute();
return results;
}
6.2 UI渲染优化
xaml复制<DataGrid VirtualizingPanel.IsVirtualizing="True"
EnableRowVirtualization="True"
VirtualizingPanel.VirtualizationMode="Recycling">
7. 典型问题解决方案
7.1 跨线程UI更新
csharp复制DispatcherHelper.Initialize();
...
Messenger.Default.Register<NotificationMessage>(this, msg => {
DispatcherHelper.CheckBeginInvokeOnUI(() => {
MessageBox.Show(msg.Notification);
});
});
7.2 PLC地址映射管理
建议使用JSON配置文件管理地址映射:
json复制{
"Axis1": {
"Position": "DB1.DBD0",
"Ready": "DB1.DBX1.0",
"Error": "DB1.DBX1.1"
}
}
8. 项目部署注意事项
- 目标机器需安装对应版本的.NET运行时
- 工业现场建议使用Windows 10 IoT Enterprise LTSC版本
- 网络配置需关闭防火墙或添加端口例外
- 建议使用工业级交换机连接PLC与上位机
实际部署中遇到过因网络风暴导致的通讯中断问题,最终通过以下方式解决:
- 设置交换机端口流量限制
- 启用QoS优先级标记
- 将PLC通讯流量与其他网络隔离
