WinCC报表实战:从变量归档到VBS+Excel一键生成 很多做工控的朋友第一次接到“做个报表”的需求时都会下意识地打开WinCC的画面编辑器往界面上拖一个表格控件然后盯着属性对话框发呆——因为WinCC自带的报表功能和你想象的不太一样它不是Excel也不是水晶报表它是一套需要先理解“数据从哪来、怎么存、怎么取”才能玩转的体系。我这几年用WinCC 7.x和Unified做过的报表项目不下十个从最初只会手动导CSV到后来做成带查询条件、一键导出、定时打印的完整报表系统中间踩过不少坑也总结出了一套“无门槛”的上手路径。这篇就把整套思路和可复用的脚本、配置、排查方法一次讲透。1. 先搞清楚WinCC报表的“数据地基”变量归档与消息归档开始动手做报表之前必须先建立一个大前提WinCC本身不是数据库它只是一个数据采集和监控平台。报表需要的数据全部来自WinCC的“归档系统”。归档系统分两大类理解清楚这两类你后面所有报表方案的设计都会顺理成章。变量归档Tag Logging负责定时采集和存储过程变量例如温度、压力、流量、产量计数、设备状态等。它的存储方式是“压缩归档”默认经过压缩算法因此历史曲线、日报表、趋势分析都依赖它。消息归档Message Logging负责存储报警、事件、操作记录例如“电机过载报警”“操作员A登录”“泵启动”等存储的是离散事件带时间戳和确认状态。报表的本质就是从这两类归档数据中查数据整理成规定格式后呈现出来。如果你连变量归档都没有正确配置那么无论用什么报表方案都等于无源之水。举个最常见的反面教材有人把变量的“采集周期”设成了1秒过程值归档也开了结果做日报表时发现数据全是乱的一天86400条记录Excel根本打不开。这背后的问题不是报表工具不行而是归档配置本身就是错的。归档周期和采集周期应该是“采集周期决定数据新鲜度归档周期决定存储频率”两个概念不能混。一般过程值的归档周期设为采集周期的整数倍即可不要盲目追求每秒归档。做报表的数据精度到秒级足够但如果做能源分时统计甚至归档周期设为1分钟也合理。1.1 变量归档的两种类型与关键配置在WinCC的“变量记录”编辑器中归档变量分为两种类型类型适用场景典型周期占用空间过程值归档连续变化的模拟量温度、压力、液位等1s~1min较大二进制归档开关量、状态量启动、停止、故障按事件/周期较小配置归档时有几个参数直接决定报表能不能做出来采集周期该变量从PLC读取数据的间隔由“变量管理”中的采集周期决定例如100ms、1s。归档周期变量记录中该变量存数据库的间隔可以设为“每个采集周期”或某个固定周期如1分钟。存储时长例如“7天”“30天”“365天”超出会被覆盖。这个决定了报表能回看多远的历史。最怕的情况是项目刚上线时默认是一周到月底要出月报时发现数据早没了。我建议在项目交付时将存储时长这种参数主动写入运维文档并且在程序里做一个“归档数据完整性检查”的提示——这不是报表本身的功能但没有它再好的报表也会在关键时刻露怯。1.2 消息归档的配置要点消息归档用于存储报警和事件。WinCC默认将消息归档和变量归档存储在同一个SQL Server实例中消息归档数据在运行状态下自动写入。需要设置的是“单个消息归档的时间段”通常是7天或30天。但这里有一个关键点消息归档的查询用WinCC自带的“报警控件”看是没问题但如果你想在报表里按“班次”统计报警次数、报警时长直接用SQL查询消息归档表你会发现表结构非常复杂字段名都是内部的。所以我的建议是如果报表需要详细的报警统计不要直接捞数据库表而是在消息触发时用C脚本或VBS将关键内容写入自己的业务表这样查询会轻松一百倍。这个技巧后面会详细展开。2. 报表方案的岔路口三种主流路线怎么选归档数据准备好了接下来的核心问题是用什么方式来呈现和输出报表WinCC世界里的报表方案市面上流传的乱七八糟但归纳起来无非三条主要路线。不要把精力浪费在研究什么“专业报表控件”上先把这三条路线看明白按需选取就行。2.1 路线AWinCC自带报表编辑器Old School但仍有价值很多老工程师刚接触WinCC时学的是“报表编辑器”——在那个界面里拖一个表格对象连上归档变量然后设计打印布局。这是最传统的方案它的优势是集成度高、不依赖外部软件缺点是布局样式古板、导出格式有限一般是打印导出Excel需要额外配置。如果你是在WinCC 7.x的老项目上叠加一个简单报表这条路线快捷有效但不要指望它做出现代化的可视化报表。2.2 路线BVBS脚本Excel模板最实用、最推荐的主干方案这是我个人最推崇的方案也是下述实战方案的主角。核心思路是WinCC运行画面中放置一个按钮点击后触发VBS脚本脚本打开Excel模板把WinCC归档数据按规则写入Excel生成一份格式完全受控的报表文件。这套方案灵活度极高因为Excel的格式你可以自由定制——标题、边框、页眉页脚、单位、小数位、合并单元格、图表Excel能做的一切都能体现在报表上。它不限制导出时间范围支持按班次、按天、按周、按月统计。只要不涉及实时高频写入性能也足够了。2.3 路线C直接SQL查询归档数据库面向高级需求当报表需求不仅仅是“简单日报表”而是涉及跨系统数据合并、数据仓库分析时就需要直接查询WinCC的SQL Server数据库。WinCC的归档库中有TAG_ARCHIVE、MESSAGE_ARCHIVE等内部表但这些表的结构跨版本差异很大且字段名不友好直接写SQL的维护成本比较高。我通常只在做数据二次开发例如发布到Web服务器、对接MES时才使用这条路线且会封装成一个独立的查询接口避免业务报表逻辑耦合到库表结构上。2.4 三条路线的选型建议需求特征推荐路线说明极简打印不做文件存档A减少开发量日报/班报/月报要发邮件或存档B最灵活实用跨系统查询、大数据量分析、Web展示C灵活性最高但成本最高一旦你定了路线B接下来的内容就是给你准备的一个可以直接复用的、带查询条件、支持任意时间范围导出的WinCC报表系统。3. VBA/VBS读写WinCC归档的核心在WinCC中调用脚本查询归档数据报表系统的第一道技术门槛不是如何在Excel里画表格而是如何在WinCC脚本里查询归档数据。很多人卡在这一步是因为WinCC自带的API和普通的数据库查询不太一样。WinCC的脚本环境支持C脚本和VBS脚本VBS可以访问WinCC的自动化接口其中最关键的是HMIRuntime.Tags和HMIRuntime.DataSet但在报表场景中我强烈建议直接使用SQL方式查询归档表配合WinCC的“数据连接”机制实现。3.1 通过WinCC的ODBC/JDBC方式访问归档WinCC提供一个名为WinCC OLEDB Provider的数据库连接方式你可以把它理解成一个桥梁让外部程序包括Excel、VBS脚本、甚至Python能够以类似SQL的方式读取WinCC归档数据。这个Provider的名称在不同版本中略有差别WinCC 7.x中通常是WinCC OLEDB Provider或WinCC/Plus。使用WinCC OLEDB Provider的一个最大优势它会把压缩的归档数据“解压”成你熟悉的关系表查询时可以使用Archive、Value、Time、Quality等字段而不是面对原始的内部存储格式。一个典型的SQL查询示例SELECT Value, Time FROM TAG_ARCHIVE WHERE TagName ProcessValue_1 AND Time BETWEEN 2024-01-01 00:00:00 AND 2024-01-01 23:59:59但实际用WinCC OLEDB Provider时表名不是TAG_ARCHIVE这么简单而是一个以符号开头的内部名称格式类似TAG_ARCHIVE_XXXX。千万不要去硬记这个表名正确的做法是使用ProviderWinCCOLEDBProvider;Catalog你的项目名;Data Source你的机器名\WinCC这样的连接字符串然后在查询时使用TAG_ARCHIVE, 变量名你的变量名这种类SQL语法。准确连接字符串因版本而异我记得在7.2版本里的标准写法是ProviderWinCCOLEDBProvider.1;CatalogYourProject;Data SourceYourComputer\WinCC如果连接不上绝大部分原因是“WinCC运行状态”或“数据源名称不正确”。建议使用WinCC自带的一个工具来测试连接在开始菜单中找到“WinCC - Tools - WinCC OLEDB Test”先确认连接能够成功再写VBS脚本可以省下大量排错时间。3.2 VBS脚本中查询归档数据的“最小可用模板”下面给出一段在WinCC全局脚本或画面按钮事件中可用的VBS代码它的功能是“查询某个变量在指定时间范围内的全部记录”并写入一个数组返回。这段脚本是我所有报表功能的基础赛过任何花哨的包装你直接复制到项目里改变量名和时间即可使用。 查询WinCC变量归档历史数据返回二维数组 参数strTagName 变量名dateTimeFrom 开始时间dateTimeTo 结束时间 返回arrResult(0, i) 时间arrResult(1, i) 值 Function QueryArchiveData(strTagName, dateTimeFrom, dateTimeTo) Dim conn, rs, strSQL Dim arrResult() Dim i Set conn CreateObject(ADODB.Connection) conn.ConnectionString ProviderWinCCOLEDBProvider.1;CatalogYourProject;Data SourceYourComputer\WinCC conn.Open 使用WinCC OLEDB Provider的语法查询 strSQL SELECT * FROM TAG_ARCHIVE WHERE TagName strTagName _ AND Time FormatDateTime(dateTimeFrom, 0) _ AND Time FormatDateTime(dateTimeTo, 0) Set rs conn.Execute(strSQL) If Not rs.EOF Then ReDim arrResult(1, rs.RecordCount - 1) i 0 Do While Not rs.EOF arrResult(0, i) rs.Fields(Time).Value arrResult(1, i) rs.Fields(Value).Value rs.MoveNext i i 1 Loop End If rs.Close conn.Close Set rs Nothing Set conn Nothing QueryArchiveData arrResult End Function你可能会问为什么要先查询到数组再写入Excel为什么不直接让Excel通过ADO读取WinCC归档这是因为在工业现场WinCC报表功能的数据查询经常涉及多个变量、多个时间段如果每一步都让Excel直连数据库会频繁建立和断开连接速度极慢还容易导致Excel崩溃。合理的做法是先一次性查完存到内存数组然后统一写入Excel。这也是一个性能优化的基本素养。有一点要提醒这个函数里我用了FormatDateTime生成时间字符串这个格式在不同系统区域设置下可能不同如果查询结果为空优先查看是不是时间格式导致SQL语法错误。最稳妥的做法是用YYYY-MM-DD HH:MM:SS格式并且明确指定不要依赖系统区域。3.3 归档数据质量码的妙用你查出来的数据可能“脏”WinCC归档的每个值都带一个质量码Quality Code。国际上通用的质量码表示“好”Good、“坏”Bad、“不确定”Uncertain三种基本状态。默认查询时坏值和不确定值也会被返回。如果你的报表统计的是产量、能耗、温度平均值这些坏值会完全拉偏统计结果。我在一次做能耗月报时就因为这个吃了大亏一台流量计在某几天故障PLC侧输出0WinCC归档也记了0结果月报的平均流量被拉低了20%差点被客户质疑系统有问题。后来我在查询语句中加入了质量码过滤条件只取质量码为“Good”的记录问题才解决。WinCC OLEDB返回的字段中有一个Quality字段你可以通过判断质量码是否为192或0x00C0来过滤。在标准脚本中应该这样写strSQL SELECT * FROM TAG_ARCHIVE WHERE TagName strTagName _ AND Time fmtStart AND Time fmtEnd _ AND Quality 1923.4 数据采样与插值怎样把5万条数据变成500条且不丢失特征做日报表时2小时产量、流量平均值、峰值温度等统计量如果不做任何聚合直接查原始归档Excel行数会非常恐怖。一小时秒级归档就是3600条一查就是一天合计86400条。Excel其实能容纳上百万行但你的报表排版肯定不能这么干——没人愿意看到一张十万行的“日报表”。因此必须在查询阶段就做数据聚合Aggregation。WinCC OLEDB Provider本身支持SQL聚合函数例如AVG、MIN、MAX、SUM还能按时间段分组。例如要统计每小时的平均温度strSQL SELECT DATEPART(hour, Time) AS HourOfDay, AVG(Value) AS AvgValue _ FROM TAG_ARCHIVE WHERE TagName Temperature _ AND Time fmtStart AND Time fmtEnd _ GROUP BY DATEPART(hour, Time) ORDER BY HourOfDay如果希望报表中的每个数据点都更有代表性例如贴近日志现场的真实工况还有一种方法是在脚本中做插值按固定间隔比如1分钟取出最近邻历史值。此时需要查询语句中做“排序取最近值”不过复杂度会上升。通常而言做生产报表用SQL聚合就够了做趋势分析才会用到插值方案。4. 实战一个带查询条件、一键导出Excel日报表的完整实现好了理论铺垫到此为止。现在开始做一个完整的、可直接复用的报表系统。这套系统的基本需求是操作员在WinCC画面中选择开始时间和结束时间点击“生成日报表”按钮系统自动生成一张包含指定变量平均值、最大值、最小值、累计量等统计信息的Excel报表。这套方式是WinCC报表的中坚用法。4.1 准备Excel报表模板先在电脑上创建一个Excel模板例如命名为DailyReportTemplate.xlsx格式尽量做成最终交付的样子不要指望代码里再去调格式。为什么因为VBS里操作Excel单元格的格式很繁琐每次执行还会耗时间而模板只需要做一次每次只是填数据速度和稳定性都大幅提升。模板设计建议包含第一行报表标题例如“某生产线日报表”字体加粗字号16合并居中。第二行报表时间范围、生成时间、操作员等元信息字号10。第三行起表头行包含序号、变量名称、平均值、最大值、最小值、累计量、单位。预留数据区域从第4行起先填入变量名列表最好预先设置好边框、小数位数、底色这样最终报表颜值会很高这也是“看起来专业”的关键。模板存放路径建议在WinCC项目的GraCS目录下并固定命名。不要放在中文带空格的路径下VBS的字符串处理对中文路径偶尔会抽风。4.2 在WinCC画面中创建按钮和输入控件在WinCC画面编辑器中从“对象选项板”中拖入一个“输入/输出域”用于接收开始日期时间。不建议直接在控件里输入日期时间容易格式错。更好的方式是在画面中放两个“日期时间选择器”Siemens自带的ActiveX控件Siemens HMI DatePicker能显著提高操作员体验。再拖入两个“输入/输出域”作为开始和结束时间的补充时、分或干脆用一个标准的“日历控件”。放置一个“按钮”文本设为“生成日报表”。双击按钮在“事件”页中绑定VBS脚本。如果你用的是WinCC UnifiedUnified系列的画面基于JavaScript脚本语言就要换成JS。那条路线以后单独写一篇这里先按WinCC Classic展开。Classic的VBS仍然大量存在于存量项目中具备很强的现实价值。4.3 按钮的VBS脚本完整代码下面这段代码就是核心中的核心。它实现了三件事获取开始/结束时间、查询多个归档变量的统计量、写入Excel模板并另存为带日期的报表文件。由于VBS在WinCC中的调试体验不算好错误提示不够直观建议先在本地Excel里用VBA调通逻辑再移植到WinCC的VBS中。这个开发顺序可以节省大量时间。 生成日报表主脚本 放置在WinCC按钮的“单击”事件中 Option Explicit Dim objExcel, objWorkbook, objSheet Dim strTemplatePath, strSavePath Dim strStart, strEnd, strTagName Dim dStart, dEnd Dim arrTags, arrVals Dim i, rowCount 1. 读取画面上的输入域值 假设画面上有两个输入域对象名称分别是StartTimeInput和EndTimeInput dStart CDATE( ScreenItems(StartTimeInput).Text ) dEnd CDATE( ScreenItems(EndTimeInput).Text ) 时间有效性检查 If dStart dEnd Then MsgBox 开始时间必须早于结束时间, vbExclamation, 报表提示 Exit Sub End If strStart FormatDateTime(dStart, 0) strEnd FormatDateTime(dEnd, 0) 2. 定义要统计的变量列表此处根据实际项目修改 格式数组元素为“变量名|显示名称|单位” arrTags Array(Tag1_Temperature|反应釜温度|℃, _ Tag2_Pressure|反应釜压力|MPa, _ Tag3_Flow|进料流量|m3/h, _ Tag4_Count|当日产量|kg) 3. 创建Excel对象 Set objExcel CreateObject(Excel.Application) objExcel.Visible False objExcel.DisplayAlerts False strTemplatePath C:\WinCCProject\ReportTemplates\DailyReportTemplate.xlsx Set objWorkbook objExcel.Workbooks.Open(strTemplatePath) Set objSheet objWorkbook.Worksheets(1) 4. 填充报表元信息 objSheet.Cells(2, 1).Value 报表区间 strStart 至 strEnd objSheet.Cells(2, 3).Value 生成时间 FormatDateTime(Now, 0) 5. 遍历变量列表查询统计值并写入Excel rowCount 4 For i LBound(arrTags) To UBound(arrTags) Dim tagInfo, tagName, displayName, unit Dim avgVal, maxVal, minVal, sumVal tagInfo Split(arrTags(i), |) tagName Trim(tagInfo(0)) displayName Trim(tagInfo(1)) unit Trim(tagInfo(2)) 调用统计查询函数返回一个字典对象这里简化为数组 在实际应用中请使用第3节中所述方式构造查询 avgVal GetArchiveStat(tagName, AVG, dStart, dEnd) maxVal GetArchiveStat(tagName, MAX, dStart, dEnd) minVal GetArchiveStat(tagName, MIN, dStart, dEnd) sumVal GetArchiveStat(tagName, SUM, dStart, dEnd) 写入行 objSheet.Cells(rowCount, 1).Value rowCount - 3 objSheet.Cells(rowCount, 2).Value displayName objSheet.Cells(rowCount, 3).Value FormatNumber(avgVal, 2, -1, 0, 0) objSheet.Cells(rowCount, 4).Value FormatNumber(maxVal, 2, -1, 0, 0) objSheet.Cells(rowCount, 5).Value FormatNumber(minVal, 2, -1, 0, 0) objSheet.Cells(rowCount, 6).Value FormatNumber(sumVal, 2, -1, 0, 0) objSheet.Cells(rowCount, 7).Value unit rowCount rowCount 1 Next 6. 保存文件 strSavePath C:\WinCCProject\DailyReports\DailyReport_ FormatDateTime(Now, 1) .xlsx objWorkbook.SaveAs strSavePath, 51 51代表xlsx格式 objWorkbook.Close objExcel.Quit Set objSheet Nothing Set objWorkbook Nothing Set objExcel Nothing MsgBox 报表已生成 strSavePath, vbInformation, 报表完成4.4 统计查询函数GetArchiveStat的实现上面的代码用到了一个统计查询函数GetArchiveStat这个函数是整个查询链的基石。它的实现可以使用连接WinCC OLEDB Provider的方式或者如果你在测试环境中更方便也可以先通过HMIRuntime.Tags读取少量数据。但为了支持“任意时间范围聚合”我还是推荐OLEDB方式。 查询变量在指定时间范围内的统计值 strTagName: 变量名 strStat: AVG/MIN/MAX/SUM dStart, dEnd: 起始/结束时间 Function GetArchiveStat(strTagName, strStat, dStart, dEnd) Dim conn, rs, strSQL Dim dtStart, dtEnd dtStart FormatDateTime(dStart, 0) dtEnd FormatDateTime(dEnd, 0) Set conn CreateObject(ADODB.Connection) conn.ConnectionString ProviderWinCCOLEDBProvider.1;CatalogYourProject;Data SourceYourComputer\WinCC conn.Open strSQL SELECT strStat (Value) AS StatValue FROM TAG_ARCHIVE _ WHERE TagName strTagName _ AND Time dtStart AND Time dtEnd _ AND Quality 192 Set rs conn.Execute(strSQL) If Not rs.EOF Then If IsNull(rs.Fields(StatValue).Value) Then GetArchiveStat 0 Else GetArchiveStat rs.Fields(StatValue).Value End If Else GetArchiveStat 0 End If rs.Close conn.Close Set rs Nothing Set conn Nothing End Function4.5 为什么用实数“192”过滤质量码Windows和WinCC的质量码遵循OPC DA规范“Quality192”表示“Good: Non-specific”。很多网上资料只告诉你加这一句“防止坏值”却不说为什么是192。质量码是一个字节高6位是状态低2位是限制和质量子状态。十进制192的二进制是11000000正好等于0xC0属于“Good”类质量。如果是193则是“Good/Local Override”如果你们工艺上不介意覆盖值也可以留到Quality 192 and Quality 224范围都算Good。如果你的现场有大量设备瞬时离线导致的“Bad”值加192过滤的效果立竿见影。但也别以为只有坏值才影响报表——还有一种情况是“数据不更新”但质量码一直是Good例如PLC某通道卡死这种情况质量码查不出来只能靠数据合理性判断。我建议在报表的最后附一个“数据完整率”指标期望记录条数与实际记录条数之比小于90%就标黄提示逼着维护人员去查现场。4.6 报表生成耗时优化与运行性能工业现场最不能忍的就是“点击按钮后界面卡死一分钟”。VBS在WinCC中运行于用户界面线程一旦脚本执行时间过长画面会无响应操作员会一直点“生成报表”造成多个Excel进程堆积。这个问题的标准解法是优化SQL查询。缩小查询范围明确开始结束时间尽量不查全表。预聚合日报表不需要查询原始秒级数据可以通过SQLGROUP BY做小时或分钟聚合后再统计能显著减少IO和计算量。异步执行使用WinCC的“异步脚本”或在线程池中执行复杂报表用HMIRuntime.ScriptFunctionTrigger触发后台脚本完成后用标志位通知界面。考虑到脚本复杂度大多数情况下“优化SQL预聚合”就够了不必非得上异步。记得用objExcel.DisplayAlerts False否则Excel弹出任何对话框都会挂住脚本。实测下来查询7天数据、30个变量的日报表在优化前需要80秒优化后按小时聚合再统计约度只需要8秒。这不只是报表体验问题还影响操作员对系统的信任度——没有人愿意每天等一分半钟看一张纸。4.7 日期时间输入控件常见坑操作员不会输、输错格式日期时间输入是WinCC报表最容易出交互问题的地方。直接让操作员在输入输出域里敲2024-07-21 08:00:00几乎一定会出现两种错误一是时区格式乱二是有人用中文输入法输入全角数字。我的做法是做一个使用按钮的“快捷时间选择”区域放置几个预先设定好时间跨度的按钮“今日”“昨日”“本周”“上周”“本月”。点击后自动设置输入域的文本值。操作员只需要在极少数特殊需求下手动改时间而且手动改的时候限定输入掩码——在输入输出域的属性页可以设置格式掩码例如####-##-## ##:##:##这样可以避免大部分格式错误。如果你觉得这样还不可靠可以加入一步“时间解析后自动修正”在按钮的VBS中先用CDATE做格式转换如果转换失败弹窗提示并退出如果转换成功再检查是不是超出归档数据库的起始时间例如项目上线日期避免查询过久远的数据导致数据库压力过大。5. 报表的灵魂从“查数据”到“讲故事”的统计口径设计上面这些步骤准确说只教会了你“把数据从库里捞出来放到Excel”。但实际工作中用户对报表的期望远远不止“数据正确”他们还要“可解释”。同样是“产量日报表”不同用户对“产量”的理解可能就不同是当天生产件数还是当天合格品数是物理计数器值还是PLC内部累计值的差值这些歧义如果不提前定好你做出来的报表再漂亮也没人敢用。5.1 累计量报表的正确打开方式差值而不是绝对值WinCC归档中存储的累计值例如产量计数器是一种“不断增长”的变量的值。如果你直接查“某一天0点时刻的值”并且把那个数字填进日报表那你得到的其实是“从项目启动开始累计到当天的总产量”而不是“当天的产量”。正确做法是查询当天结束时刻的累计值减去当天开始时刻的累计值。所以对于累计型变量日报表应该这样设计查询деньStart时刻的累计值取最接近且不晚于该时刻的有效值。查询деньEnd时刻的累计值取最接近且不早于该时刻的有效值。当日产量 结束值 - 开始值。这个逻辑看似简单但在WinCC OLEDB中取“某个时刻的最近值”并不那么直观需要用到子查询SELECT Value FROM TAG_ARCHIVE WHERE TagName TotalCounter AND Time 2024-07-21 00:00:00 AND Quality 192 ORDER BY Time DESC LIMIT 1注意不同的WinCC版本对LIMIT的支持不一定相同有些环境可能要用TOP 1。我在不同版本上都遇到过这个坑建议在开发前先做一个小查询测试。5.2 平均值报表怎么避免“停机时间污染”温度、压力、流量这类过程值统计平均值时有一个隐蔽陷阱设备停机时段很多变量的值会变为0如果传感器跟着停了或者保持停机前的值如果传感器还在供电。如果直接求算术平均会把停机时段计进去均值会严重偏离“正常生产时段”的真实水平。处理方案有两种方案一推荐只统计质量码Good且变量值在工艺允许范围内的记录范围外的值视为无效。例如流量正常范围是0到100 m³/h如果某段时间值恒为9999很多仪表超量程会输出这个值必须去除。方案二先统计“运行时长”通过电机运行状态变量或流量阈值的时长再用“总累计量/运行时长”反推平均流量这种“按开启时间平均”的统计更符合生产实际。这些“统计口径”的设计是报表从“能用”走向“好用”的分水岭。拿到需求时我通常先问用户“平均值是24小时平均还是只是运行时间的平均”这一个问题就能避免报表交付后的无数争议。5.3 报表中的“小时级明细”与“汇总行”怎么设计除了最终统计值很多班组长希望日报表中除了汇总行还要有“每小时明细”方便他们定位具体几点出了问题。此时报表结构可以设计成“顶部汇总区下方明细区”。汇总区放当日均值/最大/最小/累计明细区按小时列出每小时的平均值和最大值一旦发现异常能快速定位。之所以强调这个分层设计是因为WinCC自带的表格控件很难做出“汇总区明细区”在同一页面的布局而Excel模板轻松胜任。这再次印证了“模板脚本”路线的优越性。5.4 变量数量多时的报表排版策略如果报表涉及的变量不是4个而是40个甚至更多Excel模板的设计也需要动点脑筋。直接竖向罗列40个变量每页可能放不下打印也不方便。我的策略是按工艺段分Sheet每个Sheet放一个工段的报表一个Excel文件里包含多个SheetSheet命名如“反应工段”“精馏工段”“公用工程”。或按“模拟量报表”“开关量报表”“报警统计报表”分Sheet。每个Sheet的变量数量控制在10~15个以内保证打印页宽度合适。这样做还有个额外好处如果某个工段的数据查询出错不会让整个Excel文件生成失败代码中可以在每个Sheet生成时用On Error Resume Next加一个独立的错误捕获把错误信息写到单独的“错误记录”Sheet而不是直接中断所有生成这对现场排故可太关键了。6. 必踩的坑与高频故障排查从查询无数据到Excel进程卡死最后一个板块我会把做WinCC报表时最常遇到的故障和排查手段完整列出来。这些内容在官方文档里往往是分散的我集中整理了出来每一条都是用血泪换来的教训。6.1 查询不到数据的七个原因与排查顺序如果脚本运行不报错但Excel里没有数据排查顺序强烈建议这样来由简到繁顺序检查点操作建议1时间格式是否正确直接在SQL查询工具中执行看时间字段的格式区域设置2变量名字是否精确注意WinCC变量名是否包含空格、中文、点号这些字符在SQL中是否需要转义或加引号3归档是否真的开启在WinCC变量记录的运行状态中查看该变量是否有曲线4质量码过滤是否过严先用不加入质量条件的SQL查询测试确认是否是质量码问题5归档是否被清理检查“今天”的数据是否存在以及存储时长是否过短6OLEDB连接字符串中的Catalog是否准确Catalog大小写要核对当项目较多时写错名称会导致“连接成功但无数据”7查询的表名是否匹配版本不同WinCC版本对TAG_ARCHIVE的别名解析不同换版本后务必重新测试其中最常见的是第2和第6。变量名如果叫PLC1.Tag1在SQL中直接写TagName PLC1.Tag1通常没问题但如果变量名中出现单引号、中文引号SQL语句可能被截断尤其是通过VBS拼接SQL时最容易出问题。我惯用的方法是先把strSQL用MsgBox弹出来完整检查一遍再执行。6.2 为什么查询速度越来越慢归档数量膨胀与索引失效有一类报表问题不是“没有数据”而是“数据越来越慢”。当WinCC项目运行了几个月甚至一年以上SQL Server中归档数据量可达到数百万行。此时直接用OLEDB Provider做查询如果没有走对索引查询时间会成倍增加。WinCC OLEDB Provider会自动处理一部分索引优化但如果你想查询的数据跨越较长时间范围且变量很多系统仍可能产生大量临时数据。优化手段尽量缩小Time范围。这不仅是SQL习惯更是对现场数据库的保护。将报表查询改为“分块查询”比如7天数据分成7次每次查询1天然后将结果拼接。实测下来分块查询总耗时通常比一次大查询更快原因在于每次查询的临时表更小SQL Server的排序和聚合的成本更低。定期归档数据库维护在项目停机检修期间可以执行DBCC SHRINKDATABASE或重建归档表的聚集索引。但这需要停机而且相关操作有一定风险建议由有经验的数据库管理员指导下操作。6.3 Excel进程卡死与残留进程的处理WinCC报表脚本最典型的故障就是脚本运行到一半出错Excel对象没有被正确释放后台残留了几个EXCEL.EXE进程导致下次执行时Excel对象调用失败甚至整个WinCC画面操作变卡。处理策略分两步第一步是预防。在脚本结尾要把所有对象置空objWorkbook.Close False objExcel.Quit Set objSheet Nothing Set objWorkbook Nothing Set objExcel Nothing但这还不够因为脚本中一旦发生异常退出Set objExcel Nothing可能根本执行不到。所以更稳妥的做法是在脚本开头检查并清理可能残留的Excel进程Function KillExcelProcess() Dim strComputer, objWMIService, colProcess, objProcess strComputer . Set objWMIService GetObject(winmgmts:\\ strComputer \root\cimv2) Set colProcess objWMIService.ExecQuery(SELECT * FROM Win32_Process WHERE Name EXCEL.EXE) For Each objProcess In colProcess objProcess.Terminate Next End Function注意在运行WinCC的服务器上清理Excel进程时要确保没有操作员正在用Excel打开别的文件。如果是单机站建议在执行“生成报表”前先强制清理然后在脚本执行完后再检查一次。6.4 按钮“单击事件”中的VBS没有反应的三种原因如果你在WinCC按钮的“单击”事件里写了VBS但是点击按钮没有任何反应最常见的原因是命名空间问题脚本里使用了ScreenItems(StartTimeInput)但画面对象的名字不叫这个导致运行时报错但WinCC在画面中不显示错误尤其是运行版画面。此时最好在脚本的第一行先写MsgBox 脚本开始做探针测试。权限问题WinCC运行状态下操作员权限不足按钮的“授权”属性限制了该按钮只能由特定级别操作。检查“按钮-属性-其他-授权”是否设置了“操作员授权”。脚本被全局脚本同名函数覆盖如果你在全局脚本中也定义了一个名为QueryArchiveData的函数可能会引发冲突。建议所有报表相关函数加一个前缀例如RPT_QueryArchiveData避免命名冲突。6.5 WinCC Unified与WinCC Classic的脚本差异你可能在网上看到不少WinCC Unified的报表教程注意Unified的报表能力和Classic差别很大。Unified基于HTML5脚本是JavaScript没有VBS。这意味着你在Classic中积累的VBS代码在Unified中不能平移使用。如果你正在做Unified项目报表方案通常有两种在Unified画面中写JavaScript调用WinCC Unified的开放API如HMIRuntime.DataEngine查询归档数据后导出Excel。但这需要你先启用“企业集成”等高级授权。独立开发一个报表服务例如C#的Windows服务连接Unified的HTTP/API接口获取数据生成报表文件通过Web页面下载。Unified本身的正确定位是“持续更新的可视化平台”WinCC报表这类重数据处理功能在Unified中承担的角色已经和Classic大不相同。建议项目选型时把报表需求作为一个独立的评审点而不是只盯着画面美不美来看。6.6 报表生成后的文件命名与存储策略最后一条经验关于报表文件的存储策略。很多项目里报表是一个带法律性质的记录不能随便覆盖。我的做法是按日期创建子目录如DailyReport\2024\2024-07\。文件名带日期和班次如DailyReport_20240721_BaiBan.xlsx。每天生成的文件不许覆盖使用FileSystemObject检查文件是否存在存在则在文件名后加序号。定期清理在项目服务器上挂一个Windows计划任务每周清理超过6个月的报表文件到冷存储或刻盘归档。不要图省事总是自动覆盖同一文件名。真等到某天审计需要调出三个月前的报表时你会发现当时覆盖掉的那份文件可能正是解决争议的关键证据。7. 从固定报表到可配置报表进一步拓展的思路当你把上面这套固定逻辑跑通之后下一步很自然地就会遇到新的需求——“领导说报表格式要改”“工艺员说我要再加两个变量”。这时候你如果还是靠改代码、改模板工作量和出错概率都会上升。合理的做法是做一个“可配置报表”把变量列表、统计方式、显示名称放在数据库表或文本配置文件中脚本启动时读取配置再生成报表。WinCC项目中可以用全局数据包文件或者SQL Server中的一张自定义表来存储配置。我惯用的做法是维护一个ReportConfig.csv文件格式如下TagName,DisplayName,Unit,StatType,SheetName Tag1_Temperature,反应釜温度,℃,AVGMAXMIN,反应工段 Tag2_Pressure,反应釜压力,MPa,AVGMAXMIN,反应工段 Tag3_Flow,进料流量,m3/h,SUM,反应工段脚本读取配置后动态生成SQL查询和Excel行。这样以后工艺员要加变量只需要在CSV里加一行无需动一行代码。客户端那边只需要提供配置文件更新服务端重启或定时检测配置变更就行。更进一步可以做“报表模板设计器”——这个工程量大适合产品化小项目没必要。但如果你的公司有统一的报表开发平台这绝对是降低交付成本的大杀器。还有一个方向是“定时自动生成”在WinCC全局脚本中挂一个定时触发器每天凌晨0点1分自动生成前一天的日报表并发送邮件给相关管理人员。邮件发送可以用CDO.Message在工控机器上只要配好SMTP就能发。这里有个小坑SMTP服务器的SSL和端口配置在不同现场差异大且很多厂区是内网邮件服务器收发限制严格。建议先跑通“自动生成文件到共享文件夹”再考虑邮件发送邮件只是增值项而不是必需项。如果你有MES、ERP系统也可以把生成的Excel放到一个特定共享目录由上层系统自动抓取解析。做进这层数据链路后WinCC报表就从“人肉的Excel操作”进化成了“生产数据自动化流转”的一环价值完全不一样。