news 2026/4/18 9:11:11

采用C#WPF语言设计的上位机,与西门子plc通讯,采用MVVMLight框架。 实时显示报警...

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
采用C#WPF语言设计的上位机,与西门子plc通讯,采用MVVMLight框架。 实时显示报警...

采用C#WPF语言设计的上位机,与西门子plc通讯,采用MVVMLight框架。 实时显示报警信息,实时趋势图显示,生产数据自动保存到数据库。 实现伺服控制,手动IO控制。

一、项目基础信息与技术框架

本项目是一套基于C# WPF开发的西门子PLC上位机系统,核心目标是实现与西门子PLC的通信交互,同时提供实时监控、设备控制、数据存储与报警管理等功能。系统采用MVVMLight框架搭建架构,遵循MVVM设计模式,基于.NET Framework 4.7.2开发,集成SQLite嵌入式数据库(无需额外配置),整体为单机运行模式,无需客户端部署。

采用C#WPF语言设计的上位机,与西门子plc通讯,采用MVVMLight框架。 实时显示报警信息,实时趋势图显示,生产数据自动保存到数据库。 实现伺服控制,手动IO控制。

从代码结构来看,项目包含两个核心工程:Carlden.SiemensPLC(上位机主工程,负责界面展示、业务逻辑与数据处理)和PlcService(PLC通信服务工程,负责底层通信协议封装),总计78个代码文件,模块划分清晰,各文件职责明确。

二、核心工程与关键文件功能解读

(一)PlcService工程:PLC通信核心

该工程仅含S7PlcService.cs一个核心文件,基于Sharp7组件实现西门子PLC的S7协议通信,采用单例模式确保通信连接的唯一性与稳定性。

  1. 单例实例管理:通过GetInstance方法创建全局唯一的通信实例,使用双重检查锁定机制(lock关键字)保证多线程环境下的线程安全,避免重复创建连接对象。
  2. PLC连接逻辑Connect方法通过异步任务(Task.Run)循环尝试与PLC建立连接,需传入PLC的IP地址、机架号(Rack)和槽号(Slot)。连接成功后,启动后台线程执行RefreshValues方法;连接失败时,每500毫秒重试一次。
  3. 数据读写操作
    - 数据读取:RefreshValues方法在连接状态下,循环读取PLC的DB1块数据(从偏移量0开始,读取长度276字节),读取间隔10毫秒,通过SendDataEvent事件将读取到的字节数组传递给上层业务模块。
    - 数据写入:提供两类写入方法,一是WriteBitDBWriteBit用于写入位状态(如控制IO点的通断),二是DBWrite用于写入字节数组(如伺服参数配置),均通过lock机制确保写入操作的线程安全。

(二)Carlden.SiemensPLC工程:上位机主程序

该工程是上位机的核心,按功能划分为基础组件、数据转换、数据访问、数据模型、自定义控件、视图与视图模型七大模块,各模块功能如下:

1. Base模块:基础组件支撑
  • CommandBase.cs:实现ICommand接口,封装界面命令逻辑,通过DoExecute委托绑定按钮点击、鼠标操作等界面交互事件,是MVVM模式中视图与视图模型解耦的关键。
  • GlobalOperation.cs:全局操作类,同样采用单例模式。在构造函数中初始化S7PlcService实例,调用Connect方法建立与PLC的连接(默认IP为192.168.0.102,可根据“说明.txt”改为127.0.0.1用于本地测试),并订阅SendDataEvent事件接收PLC数据,再通过自身的SendDataEvent将数据传递给其他业务模块。
  • IOStateConvert.cs:实现IValueConverter接口,将IO点的布尔状态(true/false)转换为界面显示的颜色(激活时为石灰绿Brushes.LimeGreen,未激活时为灰色Brushes.Gray),用于IO状态监控界面的直观展示。
  • MessengerExtension.cs:对MVVMLight框架的Messenger类进行扩展,简化消息的发送与注册流程,支持泛型数据传递,方便不同模块间的通信(如主界面与视图模型间的窗口控制指令传递)。
  • NotifyPropertyBase.cs:实现INotifyPropertyChanged接口,提供RaisePropertyChanged方法用于属性变更通知,Set方法用于统一处理属性赋值与通知逻辑,是所有视图模型类的基类,确保界面能实时响应数据变化。
2. ConvertBase模块:数据格式转换
  • Bit.cs:提供字节与位数组的相互转换、位状态读取与修改功能。例如,ToArray方法将PLC返回的字节数组转换为布尔型位数组(用于解析IO状态),GetValue方法读取指定字节中某一位的状态,SetBitClearBit方法用于修改指定位的状态。
  • ByteArray.cs:封装字节数组的动态管理功能,提供Add(添加单个字节或字节数组)、Clear(清空数组)等方法,方便处理PLC通信中的字节数据。
  • Int.cs:实现16位整数与字节数组的转换,支持高低字节反转(适配西门子PLC的字节序),提供FromByteArray(字节数组转整数)、ToByteArray(整数转字节数组)等方法,用于处理PLC中的整数型数据。
3. DAL模块:数据访问层
  • ExcelHelper.cs:负责将生产数据导出为Excel文件(支持.xls、.xlsx、.csv三种格式)。通过SaveToExcel方法将ProductModel列表转换为DataTable,再创建对应的Excel工作簿与工作表,设置表头样式(黄色背景、居中对齐、边框)和内容样式,支持列宽自适应、首行筛选与冻结,最后通过文件流写入指定路径。
  • Helper/SQLHelp.cs:封装SQLite数据库的基础操作,提供Update(执行增删改SQL语句)、GetSingleResult(获取单一查询结果)、GetReader(获取结果集读取器)方法,连接字符串指定数据库路径为“DataBase/myDB.db”,无需额外配置数据库服务。
  • ProductServices.cs:针对生产数据的数据库操作封装,AddData方法将生产数据(条码、压力、厚度等)插入Product表,QueryData方法根据起始时间与结束时间查询指定时间段的生产数据,返回ProductModel列表供界面展示。
  • XmlHelper.cs:读取XML配置文件中的报警信息,通过ReadXML方法加载“systemData/WarningInfo.xml”文件,解析其中的报警描述内容,返回字符串列表,为报警管理模块提供配置数据。
4. Model模块:数据模型定义
  • MessageShow.cs:管理报警信息的展示逻辑。在构造函数中获取MinitorViewModel实例,订阅其SendData委托,接收报警数据(包含报警信息与时间的字典),再通过UI线程(Application.Current.Dispatcher.BeginInvoke)更新WarningList集合(ObservableCollection),用于绑定报警列表界面。
  • ProductModel.cs:定义生产数据模型,包含序号(Id)、条码(BarCode)、压力(Pressure)、厚度(Thickness)、检测结果(TestResult)、时间(Time)六个属性,对应数据库Product表的字段与界面展示的数据结构。
  • WarningModel.cs:定义报警信息模型,包含序号(Id)、报警内容(Message)、报警时间(AlarmTime)三个属性,用于封装单条报警数据,绑定报警列表界面。
5. MyClass模块:自定义可视化控件
  • MyBezier.cs:自定义UI元素,用于绘制贝塞尔曲线,实现伺服运动轨迹的可视化。在构造函数中获取ParamSetViewModel实例,订阅其Func委托获取伺服位置数据,通过CompositionTarget.Rendering事件实时刷新绘制内容,DrawBezier方法绘制轨迹点与连线。
  • MyCanvas.cs:自定义画布控件,继承自Canvas,内部维护DrawingVisual对象用于绘制图形。plot方法通过UI线程绘制两点间的连线,支持伺服运动路径的实时绘制,VisualChildrenCountGetVisualChild方法确保自定义绘制内容能被WPF渲染系统识别。
6. View模块:界面视图
  • 该模块包含多个视图文件,均为用户控件(UserControl),仅负责界面布局与控件绑定,无业务逻辑。例如:
  • MinitorView.xaml:实时监控界面,包含DataGrid控件(绑定生产数据)与趋势图控件(绑定实时数据集合);
  • ManualView.xaml:手动控制界面,包含按钮控件(绑定手动IO控制命令);
  • ParamsSettingView.xaml:伺服参数设置界面,包含文本框(显示/输入参数)、自定义画布(展示伺服位置);
  • HistoryView.xaml:历史数据查询界面,包含日期选择控件、查询/导出按钮、DataGrid控件(展示查询结果)。
  • 各视图的后台代码(.xaml.cs)仅负责初始化界面,将DataGrid等控件的ItemsSource绑定到对应视图模型的集合属性(如HistoryView绑定HistoryViewModelProductsList)。
7. ViewModel模块:业务逻辑核心
  • CalculatorViewModel.cs:计算器视图模型,处理参数输入逻辑。通过ButtonClick命令响应计算器按钮点击,支持数字、小数点输入,以及删除(←)、清空(C)、确认、取消操作,确认后通过SetValueEvent委托将输入值传递给参数设置模块,通过NotifyWindowEnent委托控制计算器窗口的显示/隐藏。
  • HistoryViewModel.cs:历史数据视图模型,管理历史数据的查询与导出。BtnSearch命令触发查询操作,先校验起始时间与结束时间的合法性(结束时间不能小于开始时间),再调用ProductServicesQueryData方法获取数据,更新ProductsList集合;BtnSave命令调用ExcelHelperSaveToExcel方法将查询结果导出为Excel。
  • IOViewModel.cs:IO状态监控视图模型,订阅GlobalOperationSendDataEvent接收PLC数据,从字节数组中提取输入(I区)与输出(O区)数据,转换为布尔型数组(IStatusOStatus),通过属性变更通知更新界面IO状态显示。
  • MainViewModel.cs:主界面视图模型,负责页面切换与窗口控制。TabChangedCommand命令根据传入的视图类型字符串,通过反射创建对应的视图实例,更新MainContent属性实现页面切换;WindowsChanged命令通过Messenger发送窗口控制指令(最小化、最大化、关闭、切换皮肤),与主窗口(MainWindow)通信。
  • ManualViewModel.cs:手动控制视图模型,获取S7PlcService实例并订阅SendDataEvent接收IO状态数据,维护OStatus集合更新界面输出状态。提供MouseDownMouseUp命令,分别在鼠标按下时向PLC写入“1”(激活IO点)、鼠标松开时写入“0”(关闭IO点)。
  • MinitorViewModel.cs:实时监控视图模型,是业务逻辑最复杂的模块。获取S7PlcService实例并订阅SendDataEvent,解析PLC数据(提取压力、厚度、检测结果、报警状态等),更新实时数据属性(Data1Data2等)与趋势图数据集合(Data1Collect等);监控报警状态位,通过Warning方法整理报警信息,调用SendData委托传递给MessageShow;当检测到生产数据更新时,调用ShowDataGrid方法更新界面DataGrid,并调用ProductServicesAddData方法将数据存入数据库。
  • ParamSetViewModel.cs:伺服参数设置视图模型,获取S7PlcService实例并订阅SendDataEvent,解析PLC中的伺服参数(X/Y轴速度、位置等),更新对应的属性(VelocityXPointionY等);提供MouseClick命令触发计算器输入参数,MouseDown/MouseUp命令控制伺服运动;ClickEvent方法将目标位置数据写入PLC,实现伺服定位控制;同时通过Func委托将伺服位置数据传递给MyBezier用于轨迹绘制。
  • ViewModelLocator.cs:视图模型定位器,基于依赖注入(SimpleIoc)管理所有视图模型的实例,为视图提供统一的视图模型访问入口。在构造函数中注册所有视图模型类型,通过属性(如MainMinitor)暴露视图模型实例,方便视图绑定。

三、核心业务流程

(一)PLC通信与数据流转

  1. 程序启动时,GlobalOperation单例初始化,调用S7PlcServiceConnect方法建立与PLC的连接;
  2. 连接成功后,S7PlcServiceRefreshValues方法循环读取PLC数据,通过SendDataEvent传递给GlobalOperation
  3. GlobalOperation通过自身的SendDataEvent将数据传递给IOViewModelMinitorViewModel等业务模块;
  4. 各模块解析数据,更新对应属性,通过NotifyPropertyBase的属性变更通知刷新界面。

(二)生产数据存储与导出

  1. MinitorViewModel解析到生产数据更新(检测结果标志位激活)时,调用ShowDataGrid方法更新界面DataGrid,并调用ProductServicesAddData方法将数据插入SQLite数据库;
  2. 用户在HistoryView界面选择时间范围并点击“查询”,HistoryViewModel调用ProductServicesQueryData方法获取历史数据,更新ProductsList集合,界面DataGrid实时展示;
  3. 用户点击“导出”按钮,HistoryViewModel调用ExcelHelperSaveToExcel方法,将ProductsList数据导出为Excel文件。

(三)报警管理

  1. 程序启动时,MinitorViewModel调用XmlHelperReadXML方法读取XML中的报警配置信息;
  2. MinitorViewModel解析PLC的报警状态位,通过Warning方法整理当前报警信息(包含报警内容与时间);
  3. 通过SendData委托将报警数据传递给MessageShowMessageShow更新WarningList集合,报警列表界面实时展示。

四、部署与配置说明

  1. 环境要求:需安装.NET Framework 4.7.2及以上版本,无需额外安装数据库(SQLite为嵌入式数据库);
  2. PLC连接配置:修改GlobalOperation类构造函数中_s7PlcService.Connect的IP参数,将默认的192.168.0.102改为目标PLC的IP(本地测试改为127.0.0.1);
  3. 编译运行:通过Visual Studio还原NuGet包(依赖包已在packages.config中定义),编译解决方案后,运行Carlden.SiemensPLC工程的输出exe文件即可启动程序。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:30:46

Python入门篇【异常】

Python【异常】 文章目录Python【异常】一、Bug定义二、捕获异常2.1.基本捕获异常2.2.指定捕获异常提示:以下是本篇文章正文内容,下面案例可供参考 一、Bug定义 早期计算机采用大量继电器工作,马克二型计算机就是这样的。1945年9月9日&#…

作者头像 李华
网站建设 2026/4/18 3:36:23

Thread.sleep()与Object.wait()的区别解析

文章目录Thread.sleep() 与 Object.wait() 的区别解析一、引言:线程的基本操作二、Thread.sleep() 的详解1. 基本概念2. 示例代码3. 核心特点三、Object.wait() 的详解1. 基本概念2. 示例代码3. 核心特点四、Thread.sleep() 和 Object.wait() 的区别1. 调用方式2. 锁…

作者头像 李华
网站建设 2026/4/18 3:33:50

2025年热门的AI Agent方向及国内外代表产品

2025年作为Agent发展元年,Manus敲响了AI 应用大发展的号角,各种Agent应用层出不穷。这些智能体基本框架是怎样的,哪些更有前景? 最近在海外看到一张「2025 年 AI Agent 技术趋势全景图」,觉得非常精彩,分享…

作者头像 李华
网站建设 2026/4/17 21:42:14

一文读懂 MCP、RAG、Agent

前言 最近,AI 圈被三个词刷屏了 ——MCP、RAG、Agent!几乎每天都有新的相关工具冒出来,各大技术论坛、行业群聊得热火朝天。但不少朋友一看到这些术语就犯迷糊:它们到底是啥?能干啥?和我们普通人又有啥关系…

作者头像 李华
网站建设 2026/4/18 5:25:44

一文了解 AI Agent:创业者必看,要把AI当回事

当我们谈论 AI Agent 时,与其陷入技术细节的迷宫,不如回归一个更本质的问题: 所谓 "智能体",究竟在重构什么? 若以第一性原理拆解,所有智能系统的进化,本质上都是对 "认知 - 行动…

作者头像 李华
网站建设 2026/4/18 8:30:41

基于PLC的具有球速可调可显示得分的乒乓球比赛设计

基于PLC的球速可调与得分显示乒乓球比赛系统设计 第一章 绪论 乒乓球作为普及性极强的体育项目,其训练与比赛设备的智能化水平直接影响训练效果与赛事体验。传统乒乓球训练多依赖人工发球,存在球速不稳定、发球模式单一、得分统计繁琐等问题,…

作者头像 李华