几个月前接了一个产线改造项目甲方提了一个很实际的需求设备每天的关键参数要在WinCC画面上点个按钮就能出成Excel报表还要能回查历史归档数据。当时项目里用的报表方案挺乱的有手抄的、有从趋势图截图再粘贴到Word的效率很低。我最后落地的方案就是标题里说的这种“WinCC嵌入式Excel报表”后台用VBS脚本读取WinCC历史归档数据库前台在画面上挂按钮触发数据自动写入事先设计好的Excel模板生成按日期命名的报表文件。这篇文章不打算讲太虚的东西只讲三件事WinCC历史归档数据到底存在哪、怎么读出来、怎么塞进Excel。中间会把连接字符串、查询语句、VBS脚本、常见坑一个个拆开写最后附上我在现场踩过的问题清单。适合正在做WinCC项目、或者想优化报表流程的组态工程师参考项目里如果有类似的报表需求基本可以直接照着复现。1. 项目背景一条报表需求背后的真实场景1.1 为什么选Excel而不是WinCC自带报表WinCC本身不是不能出报表。自带的报警控件、趋势控件都能显示历史数据也能通过“运行系统打印”把趋势图打出来。但现场真正要的报表往往是多台设备、多个变量、按班次或按天汇总成一张表表头要固定格式数据要能二次加工最好还能带统计计算。这些需求靠WinCC自带的控件来做效果很一般。趋势控件适合看过程曲线不适合当数据明细表报警控件也是以事件为主没法按变量逐行排列。WinCC的报表组件不是没有但配置复杂而且打印格式一旦要改你得重新做布局灵活性远不如Excel。所以很多工控项目的最终报表都落到了Excel上。这里说的“嵌入式Excel报表”本质上是两块内容拼在一起数据读取端通过WinCC自带的历史归档库把指定时间段的变量值取出来。数据呈现端利用Excel作为承载工具通过脚本自动写入、自动生成图表、自动保存。WinCC负责触发和调度Excel负责呈现和排版两边各干各最擅长的事。1.2 方案对比原生报表、CSV导出、嵌入式Excel我见过不少项目用的报表方案大致是下面这几种方案优点缺点适用场景WinCC自带趋势打印不用写代码当天就能出图格式固定没法做数据明细二次加工困难临时看看趋势不作为正式报表WinCC变量记录导出CSV实现简单数据全格式乱CSV用Excel打开中文容易乱码还得手动整理偶尔导出一次能做后续加工自己写脚本生成Excel报表格式自由、一键生成、自动化程度高需要写VBS/脚本前期要调通连接和模板需要长期、定期、多变量出报表的正式项目做下来我的感受是如果报表只是“偶尔导一次”那CSV够了。但像甲方这种“每天要出报表、月底还要汇总”的用法最终还是得靠脚本方案。这也是我把整个方案定为“WinCC嵌入式Excel报表”的原因它的目标不是给工程师偶尔用而是给操作工、车间主任每天固定用。2. 读懂WinCC历史归档数据是报表的第一步2.1 归档数据库在哪里、长什么样要读WinCC的历史归档数据首先得知道这些数据放在哪里。WinCC从7.x开始底层数据库用的是SQL Server。项目里你打开SQL Server管理工具能看到以“计算机名_Archive_年份_序号”命名的数据库比如我的机器叫“HMI01”那归档库一般就是“HMI01_Archive_2024_1”。如果项目里配置了多个归档周期也可能出现多个数据库比如快速归档、慢速归档各一个。每个归档库里真正存过程值数据的表名字通常像“RPD#1”“RPD#2”这样。RPD是WinCC的“过程值归档数据表”Raw Process Data缩写。表名后面的数字代表的是第几组归档段。以我接触的项目为例打开SQL Server里的归档库你一般会看到几类关键表RPD#开头过程值表存的是变量的实时历史值。TAG#开头变量定义表记录了变量名在归档库里的索引信息。TV#开头变量文本值表如果归档了文本型变量会用到这张表。ALG#开头报警归档表存的是报警记录。做报表主要关注RPD#和TAG#这两类就够了。2.2 窄表与宽表两种常见的存储结构WinCC归档表有窄表和宽表两种结构理解这个很重要因为它直接影响你的查询语句怎么写。窄表的结构类似于“每行一个变量、一个时间戳、一个值”表里有“ValueName”“Value”“TimeStamp”这样的字段。好处是查询单个变量方便WHERE条件直接按变量名过滤就行。宽表的结构则更像“每行一个时间点、所有变量排成一行”字段是“ID”“TimeStamp”“Value1”“Value2”“Value3”这样依次排开。每新增一个归档变量就多占一列。报表查询时你直接取某几个Value列就行。实际工程里WinCC大多默认用宽表结构因为写入效率高同一时刻多个变量的值可以一次落盘。但宽表有个麻烦变量多了以后列特别多你查之前得先搞清楚“我要查的变量到底落在第几列”。怎么确认列对应关系最靠谱的办法是查TAG#表。TAG表里每条记录对应一个归档变量里面会有变量名和它对应的值列号。比如你想查“压力”这个变量在TAG表里查到它对应的是Value3那查询时就直接查RPD#表的Value3字段。2.3 时间戳和质量码这两个坑必须先说清楚读WinCC归档数据时有两个地方最容易被坑一个是时间戳空值一个是质量码。先说时间戳。宽表结构和窄表不同它的时间戳不是每一列都重复写一遍。真实存储时一行里经常只有前面某一列带时间戳后面的字段都是空的。你如果直接“SELECT TimeStamp, Value1, Value2”去查拿回来的TimeStamp有可能是一串NULL特别是在某些版本的WinCC里多变量一起归档时非常明显。处理方式一般有两种一是查询时不做特殊处理程序里判断一下如果当前行的时间戳是空的就把上一行的时间戳补过来二是直接用SQL把“下一条有效时间戳”关联出来这个后文实操部分会写。再说质量码。WinCC归档过程值表里每个Value列旁边往往还有一个质量码列比如“QC1”“QC2”表示这个值的好坏状态。0通常表示质量正常、数值可用非0值可能代表坏值、不确定值或者手动输入值。做报表时如果不过滤质量码你辛辛苦苦导出来的Excel里可能混着一堆断线、停机时的无效数据。特别是设备通信中断时WinCC会按“保持上一值”或“写坏值”的方式处理不筛QC的话报表数据根本没法看。查询时加上“QC1 0”这类过滤条件能挡掉大部分脏数据。具体的质量码定义每个版本略有差异但工程上一般按这个思路处理是没问题的。3. 嵌入式Excel报表的总体思路3.1 技术选型VBS脚本 ADO WinCC OLEDB Provider读WinCC历史归档数据有很多条路可以走。比如用WinCC自带的C脚本直接调内部API或者用DLL封装后供外部程序调用再或者用ODBC连SQL Server的归档库。我最后选的是WinCC全局脚本里的VBS脚本配合ADO访问WinCC官方的OLEDB Provider。这么选的原因有三个。第一写起来快。VBS脚本在WinCC里内置支持图形编辑器里的按钮可以直接挂VBS动作不需要额外编译改起来也方便。C脚本虽然性能好但COM对象操作和字符串处理比VBS繁琐。第二OLEDB Provider是西门子官方提供的访问接口。相比直接用ODBC或SQL Server客户端去连归档库OLEDB Provider对归档数据库的兼容性更好不用管数据库用户密码只要WinCC运行组态正常基本都能连通。第三ADO是Windows上操作数据库的通用方案。WinCC里跑VBS脚本调用ADO和平时写VBA操作Access、SQL Server是同一套思路资料多、好排查问题。注意不推荐直接在归档库上用SQL Server客户端强行做写入操作。归档库是WinCC运行系统实时在用的任何人都不要去改它的表结构或者写数据只做只读查询才是最安全的。3.2 Excel模板与工程结构设计项目里做报表最忌讳的就是把所有表头、样式全写在脚本里。代码里用“Cells(1,1)...”、“Cells(1,2)...”这种写法填表头不仅代码冗长后面客户要改个字体、加个列你还得改程序。我这边统一用“Excel模板脚本填数”的结构模板文件放在一个固定的工程路径下比如“D:\Report\Template\DailyReport.xlsx”。模板里提前做好表头、列宽、边框、打印区域、统计行。脚本只负责把查询结果按列顺序填进模板的空数据区域。这样做有两个好处一是报表样式改动时只需要改模板不用动WinCC脚本二是哪怕换了操作工只要他们不碰模板报表格式就不会乱。另外建议在模板里预留一个“参数区”比如A1单元格写“报表生成时间”B1单元格留着让脚本填日期。这样报表一打开就能看出这份是哪天生成的、哪条产线用的避免数据串批次。3.3 为什么采用“生成文件自动打开”而不是画面上放一个Excel对象做“嵌入式”报表很多人第一反应是在WinCC画面上拖一个Excel OLE对象进去。实际这个思路我试过效果不太好。WinCC画面里的OLE对象要么是嵌入式激活要么是链接打开数据刷新和视图联动都很别扭而且画面本身会卡。我最后采用的是另一种“嵌入式”画面里放按钮按钮触发后台脚本脚本在服务器上生成Excel文件然后用CreateObject把Excel打开或者直接弹出文件路径让用户去取。这种方式从用户视角看就是在WinCC画面上点了一下“日报表”然后Excel自己弹出来了体验上完全够用。如果项目要求比较高还可以在WinCC画面上放一个“文件列表”控件把服务器指定目录下生成的报表列出点哪份看哪份。这比在画面上嵌Excel对象要稳定得多推荐大家优先考虑。4. 详细实操从查询归档数据到生成Excel报表4.1 准备工作归档配置与连接测试动手写脚本前先做三件事第一步确认需要出报表的变量已经在WinCC变量管理里勾选了“归档”。这个是最容易漏的。变量如果不归档历史库里根本不会有数据脚本写得再好也是白搭。第二步确认归档周期。WinCC的归档分“快速归档”和“慢速归档”慢速归档一般存的是分钟级平均值或瞬时值适合做长期趋势快速归档存的是秒级原始值数据量大查询时要控制好时间范围。报表如果需要的是分钟级数据直接用慢速归档里的平均变量就行不要硬查快速归档。第三步测试数据库连接。在WinCC运行系统的全局脚本里临时写几行VBS用ADODB.Connection去连归档库能连上再继续。测试时用这个最小连接串就够了Dim objConn Set objConn CreateObject(ADODB.Connection) objConn.ConnectionString ProviderWinCCOLEDBProvider.1;CatalogHMI01_Archive_2024_1;Data Source.\WinCC objConn.Open If objConn.State 1 Then MsgBox 连接成功 End If objConn.Close Set objConn Nothing连串里的Catalog要改成你机器的实际归档库名。不确定名字的话到SQL Server的管理工具里看一眼或者检查WinCC项目文件夹里的“归档数据库路径”配置。4.2 核心VBS脚本逐段讲解下面这段脚本是这个项目的核心功能是读取某个变量在指定时间段的历史归档数据写入Excel模板另存为带日期的报表文件。我拆开讲一下。首先是连接数据库和查询数据Dim objConn, objRS, strSQL, strConn strConn ProviderWinCCOLEDBProvider.1;CatalogHMI01_Archive_2024_1;Data Source.\WinCC Set objConn CreateObject(ADODB.Connection) objConn.ConnectionString strConn objConn.Open 查询指定变量在指定时间段的数据 strSQL SELECT Value1, TimeStamp FROM RPD#1 _ WHERE TimeStamp 2024-11-01 00:00:00 _ AND TimeStamp 2024-11-01 23:59:59 _ AND ID 0 ORDER BY TimeStamp ASC Set objRS objConn.Execute(strSQL)这段有两个细节要说。一是Value1到底代表哪个变量建议先查TAG#表确认不要靠猜。二是我加了一个“ID 0”的条件这是为了避开归档表里一些特殊行比如初始化行或内部标记行能让结果干净一点。实际项目里查询时间一般不会硬编码而是由操作工在WinCC画面上输入起始时间和结束时间。做法就是在画面上放两个输入框或一个时间选择控件脚本里取这两个输入框的值拼到SQL语句里。接下来是写入ExcelDim objExcel, objWB, objSheet Set objExcel CreateObject(Excel.Application) objExcel.Visible False objExcel.DisplayAlerts False Set objWB objExcel.Workbooks.Open(D:\Report\Template\DailyReport.xlsx) Set objSheet objWB.Worksheets(Sheet1) 从第二行开始写数据第一行是模板表头 Dim intRow, intCol intRow 2 Do While Not objRS.EOF objSheet.Cells(intRow, 1).Value objRS.Fields(TimeStamp).Value objSheet.Cells(intRow, 2).Value objRS.Fields(Value1).Value intRow intRow 1 objRS.MoveNext Loop 保存并关闭 Dim strFileName strFileName D:\Report\Output\DailyReport_ Year(Now) Right(0 Month(Now), 2) Right(0 Day(Now), 2) .xlsx objWB.SaveAs strFileName objWB.Close False objExcel.Quit MsgBox 报表已生成 strFileName这段的“逐单元格写入”方式数据量小的时候没问题。但如果查询结果是几万行那逐行写会非常慢。优化办法是把数据先放进一个二维数组再用一句Range赋值一次性写入Excel。比如ReDim arrData(objRS.RecordCount, 2) intRow 0 Do While Not objRS.EOF arrData(intRow, 0) objRS.Fields(TimeStamp).Value arrData(intRow, 1) objRS.Fields(Value1).Value intRow intRow 1 objRS.MoveNext Loop objSheet.Range(A2).Resize(intRow, 2).Value arrData数组写法的速度提升非常明显实测几万行数据逐单元格写要几十秒数组批量写基本是瞬间完成。做报表这块能批量就别一行行来。4.3 时间戳与变量匹配的难点处理前面说宽表时间戳可能出现NULL这里给一个工程上实用的处理方式。比如你查了RPD#表取回了ID、Value1、Value2、TimeStamp四列。其中Value1是某个温度变量Value2是某个压力变量但TimeStamp可能只在部分行有值。最简单的方案就是在VBS循环里判断一下If IsNull(objRS.Fields(TimeStamp).Value) Then 当前行为空沿用上一行时间戳 objSheet.Cells(intRow, 1).Value dtPrevTime Else dtPrevTime objRS.Fields(TimeStamp).Value objSheet.Cells(intRow, 1).Value dtPrevTime End If这个“沿用上一行时间戳”的办法做日报表、班报表完全够用因为归档查询一般是按时间顺序返回的。还有一种更SQL的做法用假设的“下一行有效时间戳”补齐空值。思路是先给每行一个行号然后用行号去关联“小于等于当前行号、且时间戳非空的最大行号”把那条的时间戳取过来。这个SQL写起来复杂性能也一般工程上我更推荐直接用VBS判断。4.4 在WinCC画面中挂载报表按钮数据读取和报表生成脚本调通以后剩下的就是把功能挂到WinCC画面上。在WinCC图形编辑器里操作步骤很简单打开需要放按钮的画面从对象选项板拖一个“按钮”到画面上。右键按钮选择“属性”。在属性窗口里找到“事件”页签选择“鼠标动作”或“按左键”。事件类型选择“VBS动作”打开VBS编辑器。把写好的脚本粘贴进去编译保存。如果脚本是写在WinCC全局脚本里的函数那按钮动作里只需要写一行调用语句比如“Call GenerateDailyReport()”。这样代码好维护按钮只是入口逻辑全在全局脚本里。一个建议测试阶段不要直接弹MsgBox因为如果数据量很大MsgBox会弹很多很多次。建议先输出到一个文本文件比如在脚本里加几行写日志的逻辑把“开始执行”“查询到X行”“写入完成”这些节点记录到“D:\Report\Log\ReportLog.txt”。定位问题会方便很多。5. 常见问题排查与实用避坑5.1 快速排查表下面这个表是我在这个项目里和平时维护时总结出来的高频问题按“现象—原因—解决办法”三列整理建议直接收藏。现象可能原因解决办法连接串一执行就报错Catalog数据库名不对或WinCC运行系统没启动确认归档库真实名称先启动WinCC运行系统再测试查询能连上但返回0行变量没有归档或查询时间范围不对去变量管理确认归档勾选把时间范围扩大再试返回很多NULL值宽表中时间戳分布在部分行或变量本身有坏值VBS里做空值回填查询时加质量码过滤Value值和变量对不上RPD#表的列号和变量对应关系搞错了查TAG#表确认列对应关系再改SQL报表生成特别慢逐单元格写入数据量又大改成数组批量写入一次赋值给RangeExcel文件生成了但打不开脚本运行过程中Excel异常退出文件被占用检查脚本结尾是否正常Quit先结束残留Excel进程再重试WinCC里脚本没有权限操作Excel操作系统或杀毒软件拦截了COM调用检查运行账号是否有本地权限给WinCC运行程序放行5.2 几条实战经验小结第一归档变量命名要规范。历史归档数据读出来以后别人能不能看懂报表完全取决于归档变量名。项目初期就统一命名规则比如“产量_1号线_今日累计”“温度_反应釜_A区”报表自动生成后才能让人一眼看懂。第二报表文件定期清理。WinCC报表脚本每天生成一个文件时间长了文件夹会堆很多。建议在脚本里加一段清理逻辑超过30天的报表自动删除或者按月归档到子目录。别小看这个事很多项目运行一年后报表目录乱到根本没法找数据。第三模板文件不要放在C盘系统目录。之前有客户把模板放在“C:\Program Files”下WinCC脚本每次打开都提示权限问题。放在D盘独立目录并且把读写权限放开能省掉很多麻烦。第四别忽视时间控件。WinCC投影画面上让操作工手动输入时间格式很容易出错。建议在画面上用WinCC自带的“日历控件”或“时间选择控件”让用户点选而不是手动敲日期能大幅提高报表数据的准确性。这套方案做完之后后面还可以继续扩展比如把多个变量组合成一张综合报表、给Excel加上趋势图、或者把数据导入到数据仓库做更深度的分析。核心思路是一样的WinCC历史归档数据读出来Excel负责呈现。只要连接和查询这块明白了报表形式可以按需无限变化。
