前言
工业现场总少不了各种仪表、泵阀和温度液位数据,可每个项目都从头写一套监控界面实在不划算。大部分时间都花在重复的通讯调试和界面布局上,真正花在业务逻辑上的精力反而被压缩了。
本文推荐一个 WPF 开发的通用 SCADA 模板项目,把 Modbus TCP 通讯、报警管理、参数读写、趋势导出这些基础能力都集成进去,下次接到新项目时直接拿过来就能用。
项目介绍
项目是一个面向工业自动化监控的桌面应用程序,核心定位是"拿来即用的 SCADA 起点"。它内置了 Modbus TCP 客户端,支持读写保持寄存器和输入寄存器,涵盖了常用的整型、浮点型数据解析。
界面部分模拟了一个小型工艺流程的监控总览,包含温度显示、泵阀状态、报警列表、参数配置面板和趋势数据导出功能。无论是做设备测试、数据采集,还是搭建正式的监控系统,这个模板都能省掉不少重复劳动。
项目功能
项目特点
开箱即用:项目包含了完整的视图模型、服务层和模拟数据,Clone 下来就能直接运行
通讯抽象清晰:Modbus 读写操作被封装成独立的 Service,调用方不需要关心 TCP 连接细节和报文组拆
数据编解码统一:通过 ModbusValueCodec 静态类处理字节序(大端/小端、字序/字节序)和数据类型的转换
异步操作安全:所有 Modbus 操作都带有忙状态锁,界面不会卡死,避免重复点击触发并发通讯冲突
实时数据刷新:每秒自动更新温度、液位等过程量,模拟运行状态下数据动态变化
配置可迁移:点位映射、寄存器地址、数据类型、倍率等信息都保存在 JSON 配置文件中,便于跨机器迁移
交互反馈及时:每个操作都有对应的操作消息提示,通讯失败时会有明确的错误信息展示
项目技术
项目代码
1、Modbus 服务层
通讯层的核心是 ModbusTcpClientService,它封装了 NModbus4 的 ModbusIpMaster,提供了读写寄存器的异步方法。
调用方只需要传入 IP、端口、站号、寄存器区和起始地址即可,不用关心连接池和异常重试细节。
publicasync Task<ushort[]> ReadRegistersAsync(string host,int port,byte unitId, ModbusRegisterArea area,ushort startAddress,ushort length){await EnsureConnectedAsync(host, port);return area == ModbusRegisterArea.HoldingRegister ? await master.ReadHoldingRegistersAsync(unitId, startAddress, length) : await master.ReadInputRegistersAsync(unitId, startAddress, length);}2、数据编解码器
工业通讯中最头疼的就是字节序问题。ModbusValueCodec 把不同数据类型的编码解码逻辑集中处理,支持大端/小端字节序、字序调整,以及 Float32、Int16、UInt16 的相互转换。
publicstaticdoubleDecode(ushort[] registers, ModbusPoint point){var bytes = registers.SelectMany(BitConverter.GetBytes).ToArray();// 根据 ByteOrder 调整字节序列// 根据 WordOrder 调整字序列// 根据 DataType 调用对应的转换方法return rawValue * point.Scale;}3、主视图模型
MainViewModel 承担了界面状态管理和业务逻辑编排。它维护了设备集合、报警消息、Modbus 点位列表,以及启动/停止、参数读写、报警确认等命令。所有界面操作最终都会收敛到这个类中,便于后续维护和单元测试。
privateasync Task ExecuteModbusAsync(Func<Task> action){try { IsModbusBusy = true;await action(); }catch (Exception ex) { OperationMessage = $"Modbus TCP 操作失败:{ex.Message}"; }finally { IsModbusBusy = false; }}4、配置持久化
系统设置和点位映射通过 JSON 文件保存,加载时反序列化到内存对象,保存时从界面状态生成文档结构。这样在不同设备之间迁移配置就变得非常简单。
publicsealedclassModbusSettingsDocument{publicstring Host { get; set; } = "127.0.0.1";publicint Port { get; set; } = 502;publicint UnitId { get; set; } = 1;public List<ModbusPointSettings> Points { get; set; } = new();}项目效果
运行起来后,主界面分为几个核心区域:
顶部状态栏:显示当前时间、系统运行状态(运行中/已停止)、操作消息提示
顶部导航栏:监控总览、参数配置、报警记录、趋势曲线、设备维护、系统设置六个页面入口

监控总览:展示两路温度数值、液位百分比、泵阀开关状态、设备列表(含状态指示灯)和开关量状态

参数配置页面:列出所有 Modbus 点位,可修改数值后点击"写入参数"下发到设备,也可以点击"读取参数"从设备拉取最新值

报警记录页面:表格展示报警时间、设备名称、报警内容,每条报警右侧有确认按钮,顶部有一键全部确认

趋势曲线页面:可以选择 1/2/4/8/12/24 小时时间范围,点击导出生成 CSV 文件

设备维护页面:点击生成工单,系统自动扫描所有非正常状态的设备,生成包含设备名称、状态、描述和当前值的文本工单

界面配色以深色为基调,状态指示灯用绿色(正常)、黄色(警告)、红色(故障)、灰色(离线)区分,看起来比较符合工业监控软件的调性。

项目源码
项目结构如下
├── xxxx.csproj├── App.xaml / App.xaml.cs├── MainWindow.xaml / MainWindow.xaml.cs├── Views/ # 页面视图│ ├── OverviewPage.xaml│ ├── ParametersPage.xaml│ ├── AlarmsPage.xaml│ ├── TrendsPage.xaml│ ├── MaintenancePage.xaml│ └── SettingsPage.xaml├── ViewModels/ # 视图模型│ ├── MainViewModel.cs│ ├── ObservableObject.cs│ └── RelayCommand.cs / AsyncRelayCommand.cs├── Models/ # 数据模型│ ├── DeviceStatus.cs│ ├── MessageItem.cs│ ├── OptionItem.cs│ ├── ParameterItem.cs│ ├── ModbusPoint.cs│ └── ModbusSettingsDocument.cs├── Services/ # 服务层│ ├── ModbusTcpClientService.cs│ └── ModbusValueCodec.cs├── Infrastructure/ # 基础设施│ ├── StatusKind.cs│ ├── ModbusEnums.cs│ └── ObservableObject.cs└── Config/ # 配置文件(运行时生成) └── modbus-settings.json源码中已经包含了完整的模拟数据,在没有实际 PLC 的情况下也可以直接运行体验。如果接入真实设备,只需要修改 ModbusHost、ModbusPort 和 ModbusUnitId,并调整点位地址和数据类型即可。
总结
这个 SCADA 模板项目提供一个干净、可扩展的基础框架。它把工业监控系统中常见的几个痛点——通讯封装、数据解析、状态管理、配置持久化——都做了比较合理的抽象。
实际使用时,可以根据项目需要添加新的协议(如 OPC UA、MQTT)、增加历史数据存储(SQLite 或时序数据库)、完善用户权限管理等功能。框架的 MVVM 结构让这些扩展变得相对容易,不会牵一发而动全身。
如果大家正准备做一个工业监控相关的项目,可以从这套模板入手,把精力集中在业务逻辑和工艺算法上,而不是从零开始折腾通讯和界面。
夜雨聆风