『7x24小时有问必答』

前言

这两年我们在自动化产线项目里用 WPF 做了一套上位机监控系统,通过 Modbus RTU 协议跟 PLC 通信,实时采集灌装设备的温度、液位、阀门状态、生产节拍这些关键参数。整个过程走下来感觉这个组合还挺顺手的,这里把设计和实现过程做个记录。

项目介绍

智能灌装生产监控系统是一套基于 WPF + .NET 8 开发的工业自动化上位机软件。它通过 Modbus RTU 协议与产线 PLC 保持实时通信,对灌装设备的运行状态、填充精度、温度液位、阀门动作等关键参数进行毫秒级采集与可视化展示。
项目采用 CommunityToolkit.Mvvm 作为 MVVM 框架,配合 FreeSql 做数据持久化,图表部分用 LiveCharts 呈现生产过程趋势。

项目功能

设备状态实时监控:通过 Modbus RTU 协议采集 PLC 寄存器数据,实时显示灌装设备的运行模式、启停状态、故障代码等信息
关键工艺参数展示:温度、液位、压力、填充精度等模拟量数据以数值和仪表盘形式呈现,更新频率可配置
生产节拍统计:实时计算并展示当前产线的生产速度、累计产量、批次完成进度
阀门与执行器动作监控:显示各灌装阀、清洗阀、进料阀的开关状态及动作次数
实时趋势曲线:基于 LiveCharts 绘制温度、液位、填充量等参数的变化曲线,支持多测点叠加对比
数据存储与回放:通过 FreeSql 将采集数据写入本地数据库,支持历史数据查询和曲线回放
异常告警机制:当参数超限或设备状态异常时,界面闪烁提示并记录告警日志

项目特点

基于 Modbus RTU 工业标准协议,兼容主流 PLC 品牌
毫秒级数据采集与刷新,满足产线实时监控需求
采用 MVVM 架构,界面与业务逻辑分离,后期维护和功能扩展成本低
深色主题 UI,适合工业现场长时间盯屏的操作环境
模块化设计,采集、存储、界面各层解耦,方便替换或升级局部组件
轻量化部署,一台工控机即可覆盖单条产线的监控需求
支持自定义采集周期和报警阈值,适应不同产线的工艺要求

项目技术

技术组件
用途说明
.NET 8
运行时框架
WPF
桌面 UI 框架,支持 XAML 样式定制和数据绑定
CommunityToolkit.Mvvm
MVVM 框架,负责 ViewModel 和命令绑定
FreeSql
ORM 工具,用于采集数据的本地持久化存储
Serilog
结构化日志记录,便于问题排查
Modbus RTU
与 PLC 通信的工业总线协议
LiveCharts
实时趋势曲线绘制

项目代码

主窗口采用上下和左右混合布局,顶部是标题栏和状态区,左侧是导航菜单,右侧 ContentControl 承载各功能页面视图。
<Window  x:Class="SmartFillMonitor.MainWindow"

             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

             xmlns:UserControls="clr-namespace:SmartFillMonitor.UserControls"

             Title="智能灌装生产监控系统"

             Height="720"  Width="1280"

             Background="{StaticResource AppBgDark}"

             WindowStyle="None"

             AllowsTransparency="True">

       <Grid>

             <Grid.RowDefinitions>

                   <RowDefinition  Height="100"/>

                   <RowDefinition  Height="*"/>

             <!--Grid.RowDefinitions-->

             <Grid.ColumnDefinitions>

                   <ColumnDefinition  Width="220"/>

                   <ColumnDefinition  Width="*"/>

             <!--Grid.ColumnDefinitions-->

             <!-- 左侧导航菜单 -->

             <UserControls:NavigationMenu  Grid.Column="0"  Grid.RowSpan="2"/>

             <!-- 顶部标题栏 -->

             <Border  Grid.Column="1"  Grid.Row="0"  Background="{StaticResource AppBgHeader}">

                   <Grid  Margin="20,0">

                         <Grid.ColumnDefinitions>

                               <ColumnDefinition  Width="*"/>

                               <ColumnDefinition  Width="Auto"/>

                         <!--Grid.ColumnDefinitions-->

                         <!-- 当前批次信息 -->

                         <TextBlock  Grid.Column="0"  Foreground="{StaticResource AppTextMuted}"  VerticalAlignment="Center">

                               <Run  Text="当前生产批次:"/>

                               <Run  Text="{Binding CurrentBatchNo}"  FontWeight="SemiBold"/>

                         <!--TextBlock-->

                         <!-- 状态指示器 -->

                         <StackPanel  Grid.Column="1"  Orientation="Horizontal"  VerticalAlignment="Center">

                               <UserControls:StatusIndicator  StatusText="PLC在线"  IsOnline="{Binding IsPlcConnected}"  Margin="0,0,20,0"/>

                               <TextBlock  Text="{Binding CurrentTime}"  Foreground="{StaticResource AppTextMuted}"  FontFamily="Consolas"  FontSize="14"  VerticalAlignment="Center"  Margin="0,0,20,0"/>

                               <UserControls:StatusLight  State="{Binding MainContent.IndicatorState, Mode=TwoWay}"  Margin="0,0,20,0"  VerticalAlignment="Center"/>

                         <!--StackPanel-->

                   <!--Grid-->

             <!--Border-->

             <!-- 主内容区 -->

             <ContentControl  Grid.Column="1"  Grid.Row="1"  Background="{StaticResource AppBgDark}"  Margin="20"  Content="{Binding MainContent}"/>

       <!--Grid-->

<!--Window-->

顶部状态栏包含了当前批次号、PLC 连接状态、运行时间以及一个自定义状态指示灯。
这里用了一个小技巧:MainContent.IndicatorState  这个绑定路径表示先在 MainWindowViewModel 中找 MainContent 属性,再在 MainContent 返回的对象里找 IndicatorState 属性。
MainContent 通过依赖注入初始化为 DashBoardViewModel 的实例,这样状态指示灯的颜色就能跟随仪表盘页面的采集质量动态变化。
Modbus RTU 通信部分封装在独立的服务类中,负责维护串口连接、读写寄存器和异常处理。
public  classModbusRtuService  :  IModbusService

{

       private  SerialPort _serialPort;

       private  ILogger _logger;

       public  bool  Connect(string  portName,  int  baudRate, Parity parity,  int  dataBits, StopBits stopBits)

      {

             try

            {

                  _serialPort =  new  SerialPort(portName, baudRate, parity, dataBits, stopBits);

                  _serialPort.Open();

                  _logger.Information($"Modbus RTU 连接成功:{portName}");

                   returntrue;

            }

             catch  (Exception ex)

            {

                  _logger.Error(ex,  $"Modbus RTU 连接失败:{portName}");

                   returnfalse;

            }

      }

       public  ushort[]  ReadHoldingRegisters(byte  slaveId,  ushort  startAddress,  ushort  length)

      {

             // 实际项目中通过 NModbus 库实现

             // ...

      }

}

项目效果

主界面采用深色主题,适合工业现场低照度环境。左侧导航菜单包含"仪表盘""设备监控""数据查询""报警记录"等入口,切换流畅。
仪表盘页面集中展示了灌装设备的核心参数:温度、液位、填充量等模拟量用仪表盘控件呈现,数值变化时指针平滑转动;设备状态区域用不同颜色的指示灯标识运行、待机、故障三种模式;底部是实时趋势曲线,温度、压力等参数随时间变化一目了然。

顶部状态栏的 PLC 在线指示灯和数据库写入状态让操作人员随时了解系统健康度,右侧的登录和退出按钮方便不同班次的人员切换操作权限。

项目源码

运行前需要在  appsettings.json  中配置 Modbus RTU 的串口参数(端口号、波特率、校验位等),以及数据库连接字符串。

总结

这套灌装监控系统从立项到上线大概用了两个月,最花时间的反而不是界面,而是 Modbus 通信的稳定性和数据解析的准确性——工业现场的各种干扰和异常情况比实验室里复杂得多。
好在 WPF 的数据绑定机制让界面和逻辑能拆得比较干净,后续设备增删测点或者调整采集逻辑的时候,不用大动干戈。如果大家也在做类似的上位机项目,可以试试这套技术组合。

关键词

最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。也可以加入微信公众号[DotNet技术匠]  社区,与其他热爱技术的同行一起交流心得,共同成长!
作者:小码编匠
出处:gitee.com/smallcore/DotNetCore
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!

END

推荐阅读

觉得有收获?不妨分享让更多人受益
关注「DotNet技术匠」,共同提升技术实力

收藏
点赞
分享
在看

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码