ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

WinCC动态查询报警记录并导出Excel的完整方案

2026/9/16 5:35:41 拓冰建站 浏览量
WinCC动态查询报警记录并导出Excel的完整方案 搞上位机的兄弟肯定遇到过这个需求操作员说不想每次都找工程师导报警想自己在画面上选个时间段、勾个报警类型点一下查询报警记录就出来了最好还能一键导成Excel。这就是典型的WinCC动态选择报警记录场景。这个需求看起来不起眼做起来却有不少门道。WinCC自带的报警控件确实能看记录但筛选项固定、样式不好改、导出格式也死板碰上要做交接班报表、故障统计分析的时候根本不够用。我前段时间在项目里完整做了一套“动态选择报警记录”功能把查询界面、SQL拼接、表格展示、Excel导出全跑通了今天把整个方案和踩过的坑都写出来。案例基于经典WinCC V7.xV7.4/V7.5测试通过如果你用的是博途里的WinCC RT Advanced后面我会专门说差异。1. 方案对比与选型思路1.1 三种常见实现路线对比做报警记录动态查询我见过的大概有三条路用WinCC自带报警控件、用SQL直查数据库、用WinCC报表系统。先看对比。方案开发量灵活性导出能力适用场景自带Alarm Control控件小一般受控件筛选框架限制打印布局格式固定快速查看现场简单筛选SQL直查表格控件中偏大高条件可任意组合可完全自定义导出Excel交接班报表、统计分析的复杂需求WinCC报表系统中弱适合定时打印报表模板格式标准固定格式的日报、月报自动生成很多项目一开始图省事用自带控件做到后面发现导出格式调不动筛选项加不了又推倒重来。我的建议是只要操作员明确提出了“自己选条件、自己导数据”这类需求就一步到位走SQL直查。1.2 为什么选“SQL直查表格控件”这个案例的核心诉求是“动态选择”也就是查询条件在运行时不固定由操作员现场决定。WinCC报警控件的筛选虽然能通过脚本设置但它的筛选逻辑是封装好的很多字段不能随意组合比如“按设备名称模糊查只查未确认的报警时间范围”这种组合控件就不太好使。SQL直查的思路很简单WinCC的报警记录本来就是往SQL Server数据库里写的我直接用VBS脚本拼接SQL语句去查这张表把结果填到画面里的表格控件上。这样查询条件完全由我代码控制想怎么组合就怎么组合展示样式也能完全跟着项目风格走。这个方案还有一个隐藏好处查询结果可以很方便地二次加工。我在项目里除了展示还用同一套查出来的记录做了简单的统计计算比如每种报警类型出现次数、每日报警趋势这些用自带控件是做不了的。1.3 先说清楚这个方案的边界不是所有WinCC环境都适合用SQL直查。经典WinCC V7.x的项目库就是SQL Server库报警归档在里面直查没问题。但博途里的WinCC RT Advanced报警归档不一定落SQL库这招就不太好使了优先用报警控件加脚本筛选或者做成运行时导出。另外要注意SQL直查读取的是已经归档的报警记录实时性有延迟。如果你想做“当前正在发生的报警”实时列表那应该用WinCC的报警控件或者变量连接而不是查数据库。我做完这套功能后最大的体会是方案本身不复杂真正花时间的是摸清报警库的表结构以及处理好各种边界情况。下面重点讲这两块。2. 报警数据从哪来库、表、字段定位2.1 WinCC报警归档的数据落点经典WinCC项目装好后SQL Server会创建一个以项目名命名的数据库默认实例名通常是.\WinCC。报警记录就存在这个库里不在单独的外部数据库。很多教程会让你新建一个外部SQL库再用脚本往里写数据方便做报表。这个思路没问题适合跨系统集中管理。但在这个案例里报警数据本来就实时写进项目库了直接查项目库少一层中转数据最全也省心。只有当你的报表系统需要和其他产线数据做关联分析时才值得单独建库同步。2.2 用SQL Profiler一招定位真实表名与字段这里必须说一个坑WinCC不同版本、不同补丁下报警归档的表名和字段名是有差异的。网上搜到的教程里直接写死表名比如ALGV2_SIMATIC_...换台机器可能就不一样。最稳妥的办法是现场定位不要凭记忆猜表名。定位方法很简单三步在服务器上打开SQL Server Management Studio连接到.\WinCC。启动SQL Profiler选择WinCC项目库作为跟踪数据库。在WinCC运行画面里打开自带的报警控件手动做一次时间范围筛选。这时切回Profiler就能看到WinCC自己执行的那条SELECT语句表名、字段名全在里面。照着这个写自己的SQL绝对错不了。我第一次做这个案例时就是靠这个方法发现目标表名并不是网上教程里说的那个而是带了一长串GUID后缀。要是直接照搬网上的表名查出来的数据肯定是空的。2.3 理解关键字段时间、消息文本、设备、状态拿到真实表结构后重点找这几类字段。WinCC的报警归档表字段很多但不是每个都用得上。时间字段报警到达时间、确认时间、离开时间。注意底层可能存的不是datetime类型而是整数秒或者字符串格式查询时要做转换。消息文本字段报警显示的文字内容比如“1号电机过载”。设备或变量名字段报警来自哪个变量可以用来过滤具体设备。状态字段报警当前状态是否已确认、是否已离开。项目里具体字段名以你查到的为准。我这边案例中时间字段底层是整数秒存储显示层才转成日期时间。所以SQL里做时间范围过滤时不能直接写where arrive_time between 2024-01-01 and 2024-01-02得先把前端传过来的时间转成对应的整数格式或者在SQL里用DATEADD函数转换。这里分享一个通用判断方法如果你不确定字段是否可以直接用datetime比较就先在Management Studio里看几条数据的原始值。如果是几百位的大数字就是时间戳如果是20240101123000这种字符串就按字符串截取比较。这个案例里我用的方案是前端拿到的起始、结束时间在VBS里先格式化成和目标字段一致的格式再拼接进WHERE条件。2.4 连接字符串的配置要点VBS里用ADODB连接项目库连接字符串长这样Dim connStr connStr ProviderSQLOLEDB;Data Source.\WinCC;Initial CatalogMyPlant;Integrated SecuritySSPI几个关键点Data Source用.\WinCC表示本机WinCC默认实例。如果连接远程服务器换成服务器IP和端口比如192.168.1.10,1433。Initial Catalog换成你的项目数据库名注意区分大小写。Integrated SecuritySSPI用Windows集成认证。WinCC运行系统和SQL Server在同一台机器上时一般没问题但如果SQL Server做过权限收紧可能需要单独建一个只读账号用User ID和Password方式连接。我建议生产环境建个只读账号别用sa安全第一。排错提示如果VBS脚本报“未找到提供程序”检查系统里有没有SQLOLEDB驱动。WinCC自带的运行环境一般都有但有些精简系统可能会缺换成ProviderSQLNCLI11试试。3. 动态查询界面与VBS脚本落地3.1 画面布局与对象组态查询画面我按“上条件、中表格、下按钮”的布局来做操作员看起来直观代码也好维护。上边是筛选条件区域放了这些对象开始时间、结束时间用WinCC的“日期时间”输入控件或者直接用IOField绑定字符串变量。报警类型一个下拉框ComboBox选项有“全部、上限报警、下限报警、设备报警、系统报警”。确认状态下拉框选项有“全部、已确认、未确认”。设备编号文本框支持模糊输入可以填“01”查所有设备号带01的报警也可以留空查全部。中间放一个表格控件我用的MSFlexGrid行数和列数在脚本里动态设置。有些精简系统没有这个控件也可以用ListView但列表样式在WinCC运行画面里美化起来不如网格方便。下边放三个按钮查询、导出Excel、清空条件。3.2 动态拼接WHERE条件从下拉框到SQL这是整个案例最核心的脚本逻辑。按钮的Click事件里写VBS思路是先收集界面上的条件再拼SQL字符串。核心代码结构如下Dim strSQL, strWhere, strTimeFrom, strTimeTo strWhere 时间条件必选避免全表扫描 strTimeFrom ScreenItems(IOTimeFrom).Text strTimeTo ScreenItems(IOTimeTo).Text If strTimeFrom Or strTimeTo Then MsgBox 请选择开始时间和结束时间 Exit Sub End If strWhere WHERE 到达时间字段 FormatTimeStamp(strTimeFrom) strWhere strWhere AND 到达时间字段 FormatTimeStamp(strTimeTo) 报警类型下拉框选项转条件 Dim strType strType ScreenItems(CBType).Text Select Case strType Case 上限报警 strWhere strWhere AND 消息编号字段 LIKE 1% Case 下限报警 strWhere strWhere AND 消息编号字段 LIKE 2% Case 设备报警 strWhere strWhere AND 消息编号字段 LIKE 3% End Select 确认状态用确认时间字段是否为空来判断 Dim strState strState ScreenItems(CBState).Text If strState 已确认 Then strWhere strWhere AND 确认时间字段 IS NOT NULL ElseIf strState 未确认 Then strWhere strWhere AND 确认时间字段 IS NULL End If 设备编号模糊查询 Dim strDevice strDevice Trim(ScreenItems(IODevice).Text) If strDevice Then strWhere strWhere AND 设备字段 LIKE % strDevice % End If strSQL SELECT 到达时间字段, 消息文本字段, 设备字段, 确认时间字段 FROM 报警表名 strWhere ORDER BY 到达时间字段 DESC注意几个细节时间条件是必选的这个一定要做成强制限制。报警表动辄几十上百万条数据没有时间边界直接查询画面会卡死数据库也会被拖垮。拼接字符串时日期时间统一转换成字段底层存储的格式比如统一成YYYYMMDDHHMMSS的字符串这样SQL里可以直接比较。用MsgBox做条件校验虽然简单粗暴但很有效。操作员忘选时间时弹个提示比按钮没反应好得多。3.3 查询结果填充到表格控件SQL拼好之后用ADODB执行查询再把结果一条条填进MSFlexGrid。这一步我封装了一个子过程方便后面刷新和导出复用。Sub FillGrid(sqlText) Dim cn, rs, i, j Set cn CreateObject(ADODB.Connection) cn.ConnectionString ProviderSQLOLEDB;Data Source.\WinCC;Initial CatalogMyPlant;Integrated SecuritySSPI cn.Open Set rs CreateObject(ADODB.Recordset) rs.CursorLocation 3 adUseClient rs.Open sqlText, cn, 3, 1 adOpenStatic, adLockReadOnly Dim grid Set grid ScreenItems(GridAlarm) grid.Rows rs.RecordCount 1 grid.Cols 4 表头 grid.TextMatrix(0, 0) 到达时间 grid.TextMatrix(0, 1) 报警文本 grid.TextMatrix(0, 2) 设备编号 grid.TextMatrix(0, 3) 确认时间 数据行 i 1 Do While Not rs.EOF grid.TextMatrix(i, 0) rs.Fields(0).Value grid.TextMatrix(i, 1) rs.Fields(1).Value grid.TextMatrix(i, 2) rs.Fields(2).Value grid.TextMatrix(i, 3) rs.Fields(3).Value i i 1 rs.MoveNext Loop rs.Close cn.Close Set rs Nothing Set cn Nothing End Sub这里有个性能经验rs.CursorLocation设置为adUseClient先把查询结果取到客户端内存再循环填充。服务器端的游标不适合这种大批量数据遍历响应会慢很多。数据量大的时候建议在网格下方加一行提示显示“共查询到XXX条记录”。我是在FillGrid子过程里统计rs.RecordCount赋给一个文本对象显示的。3.4 一键导出Excel导出Excel是操作员用着最爽、也是踩坑最多的功能。核心思路是用VBS创建Excel的COM对象把网格里的数据写进去再保存文件。Dim xlApp, xlBook, xlSheet, i, j Set xlApp CreateObject(Excel.Application) xlApp.Visible False Set xlBook xlApp.Workbooks.Add Set xlSheet xlBook.Worksheets(1) Dim grid Set grid ScreenItems(GridAlarm) For i 0 To grid.Rows - 1 For j 0 To grid.Cols - 1 xlSheet.Cells(i 1, j 1) grid.TextMatrix(i, j) Next Next Dim strFileName strFileName D:\AlarmReport\报警记录_ Year(Now) Month(Now) Day(Now) Hour(Now) Minute(Now) .xlsx xlBook.SaveAs strFileName xlBook.Close xlApp.Quit Set xlSheet Nothing Set xlBook Nothing Set xlApp Nothing三个实战中容易踩的坑目标文件夹必须提前建好Excel不会自动创建目录。报错“文件不存在”十有八九是这个原因。文件名加时间戳避免每次导出覆盖之前的文件。操作员导完发现忘存还得重新导有版本号方便追溯。xlApp.Quit之后一定记得Set xlApp Nothing释放COM对象否则内存里会残留很多Excel进程WinCC长时间运行会越来越卡。要是系统装的是64位Office而WinCC运行库是32位进程直接创建Excel COM对象可能报“远程过程调用失败”。我遇到这个情况后给操作员电脑统一装了32位Office就解决了。如果实在不想折腾Office版本可以改成导出CSV文件Excel也能打开用文本流写文件完全不依赖COM。3.5 定时自动刷新实现动态查询不仅要手动查还要支持定时自动刷新。我在WinCC全局脚本里建了一个周期执行的脚本每60秒跑一次读取当前画面上的筛选条件自动重新查询。关键点是刷新前判断当前查询条件是否有变化。我的做法是把最近一次查询的条件存到全局变量里刷新时先比对条件没变就不重复查询避免数据库白白扛压力。 全局脚本周期执行 If ScreenItems(GridAlarm).Visible False Then Exit Sub End If Dim strNowFrom, strNowTo strNowFrom ScreenItems(IOTimeFrom).Text strNowTo ScreenItems(IOTimeTo).Text 上次查询的时间范围 Dim strLastFrom, strLastTo strLastFrom HMIRuntime.Tags(LastQueryFrom).Read strLastTo HMIRuntime.Tags(LastQueryTo).Read If strNowFrom strLastFrom Or strNowTo strLastTo Then 条件变了触发查询 调用FillGrid和更新LastQuery标记 End If这个设计很实用操作员上班打开画面页面上自动刷新最新的报警不用每次手动点查询。条件变更后立刻用新条件查查完更新标记下次条件不变就不动作。4. 常见问题与排查实录4.1 查不到数据十有八九是这几个原因我在调试时遇到过三次查不到数据的情况每次原因都不一样。第一次是表名搞错了。网上教程写的表名和我实际项目里的对不上用Profiler一看才找到真实的表。这个不多说了前面已经强调过。第二次是时间字段格式不匹配。我开始直接拿界面上的datetime字符串去和数据库里的整数时间字段比较结果当然查不到。后来把时间先转成和目标字段一致的格式再拼接问题解决。第三次是权限不够。连接字符串用集成认证但WinCC运行服务的系统账号在SQL Server里只有public权限没有读报警表的权限。给账号加上db_datareader角色后解决。排查顺序建议先跑一条最简单的SQL用Management Studio执行看能不能查到数据再用VBS的MsgBox把最终拼接出来的SQL打印出来直接复制到Management Studio里跑。这一步能定位80%的问题。4.2 中文乱码与Excel导出异常报警消息文本查出来中文乱码这个常见于历史项目数据库字符集不一致的情况。解决办法是在连接字符串里加上Auto TranslateFalse避免ODBC自动做字符集转换。connStr ProviderSQLOLEDB;Data Source.\WinCC;Initial CatalogMyPlant;Integrated SecuritySSPI;Auto TranslateFalse导出Excel后中文乱码如果是用CSV方式需要带UTF-8 BOMExcel才会正确识别编码。如果用Excel COM写入单元格只要系统区域设置正确一般不会乱。还有一个奇怪的坑Excel导出时提示“文件正被占用”其实是操作员之前手动打开了一个同名的Excel文件没关SaveAs到同一个路径就会冲突。文件加时间戳后这个问题基本绝迹。4.3 性能压不住给查询设置安全边界报警记录表数据量增长极快一个中型车间跑一年报警表几百万条很正常。没有边界条件的查询就是灾难。我做了两个硬性限制时间跨度限制在31天内。操作员想查半年数据分几次查不让他一次拉全量。查询结果超过5000条时表格控件只显示前5000条并在界面上提示“记录超过5000条请缩小时间范围”。实现上很简单SQL里用TOP 5000限制行数查询前在VBS里比较开始和结束时间超过31天直接弹提示。If DateDiff(d, CDate(strTimeFrom), CDate(strTimeTo)) 31 Then MsgBox 查询范围不能超过31天请缩短时间范围 Exit Sub End If这样设计既保护了数据库也保护了MSFlexGrid控件。我实测过网格控件塞上万条数据刷新就卡了操作员体验很差。与其让他等半天不如引导他做精确查询。4.4 问题排查速查表现象可能原因排查方法查不到数据表名/字段名错误Profiler抓真实SQL对照查不到数据时间字段格式不匹配打印SQL在Management Studio执行查不到数据数据库账号无读取权限检查运行账号的SQL角色中文乱码字符集转换问题连接串加Auto TranslateFalseExcel导出失败64/32位Office版本冲突换32位Office或改导CSV导出报文件占用同名Excel已打开文件名加时间戳查询卡顿数据量过大限制最大查询天数自动刷新不触发全局脚本周期未启动检查全局脚本运行状态4.5 关于博途RT Advanced的特别提醒前面说过博途里的WinCC RT Advanced环境比较特殊报警记录不一定在SQL Server库里。我帮朋友排查过一个TIA Portal V19的项目RT Advanced的报警数据默认存储在运行系统的内部数据集里直接连项目库查询这条路走不通。这种情况下我不会硬套SQL直查方案而是改用WinCC自带的报警控件配合画面脚本在运行时动态设置筛选条件。虽然导出的灵活性差一些但至少功能稳定可用。如果你确定要用SQL直查前提是这个项目能把报警归档配置到外部数据库或者用的是WinCC Professional这个要看具体组态方式。5. 案例复盘与可扩展的方向做完这个案例我自己复盘了几点体会。第一动态选择报警记录这个功能真正提升的是操作员的工作效率不只是做个查询界面。以前他们想筛一个设备、某个时间段、只看未确认的报警得找工程师临时写SQL导出现在自己动鼠标就行省掉大量沟通成本。第二脚本里面最花时间的不是写SQL而是处理各种异常场景比如时间不选、条件冲突、数据量过大、Excel文件占用。这些边界情况处理好了运行阶段才不会被现场频繁喊去改脚本。第三这套“SQL直查表格控件导出Excel”的思路不只是报警记录能用WinCC的变量归档、用户归档、操作记录逻辑都是通的。我做第二个项目时把查询对象换成了操作记录表代码复用了大半开发周期压缩了至少一半。最后分享一个我自己常用的小技巧动态查询界面上加一个“重置”按钮一键把所有下拉框和输入框恢复默认值。听起来很简单但操作员用久了以后界面上会残留各种筛选条件查出来的数据怎么都不对重置一下立马恢复正常。这个按钮在验收时往往比查询按钮还受欢迎。如果这个项目后续还要扩展可以考虑把报警记录定时汇总到独立报表库对接车间的MES系统或者用WinCC OPC UA服务器把报警数据开放给上层管理系统让报表系统直接走OPC UA的报警接口取数。这些都是在查询功能稳定之后顺理成章的延伸方向看项目实际需求来定就行。