首页 / 资讯中心 / 文章详情

WinCC与Excel数据交互全脚本方案:自动化报表提速实践

WinCC与Excel数据交互全脚本方案:自动化报表提速实践 ★ FEATURED ARTICLE
做WinCC项目的人迟早会遇到一个绕不开的需求把WinCC里的过程数据交到Excel手上。为了不让报表工作淹没在手工导出里我一直在琢磨自动化查询的方案最后用全脚本实现了WinCC与Excel数据交互——不依赖第三方报表组件查询速度还能明显提升。这篇内容适合正在做SCADA工程实施、需要频繁导出生产数据做分析的工程师也适合想把手动报表改成定时自动生成的自动化项目团队。1. 为什么非得自己写脚本SCADA到Excel的最后一公里1.1 工控现场的数据孤岛数据在系统里报表在表格里工厂现场的数据链路往往存在一个很尴尬的空档WinCC把过程值、报警、操作记录全部收进了归档数据库监视画面上想看哪个趋势都能调出来但一旦需要把这些数据整理成正式的Excel报表——交接班记录、月度产量统计、设备停机分析——立刻卡壳。很多现场根本没有MES甚至没人说得清WinCC的历史归档怎么导出来。操作工的做法是每小时抄一次屏工程师的做法是手动点趋势控件截图再照着数据手工录入表格。这不仅仅是效率问题数据准确性和可追溯性都打了折扣。做自动化的人本质上是给现场解决“数据搬家”的问题WinCC到Excel这最后一公里恰恰是投入最少却最容易忽略的一块。1.2 现成方案各有各的别扭脚本反而是最优解业内常见的做法有这么几种但每一套都有明显的局限性。用WinCC自带的报表控件适合在线看生成独立Excel文件的能力很弱而且报表模板一格式化就牵一发动全身。用ODK或C脚本开发的报表包功能强大但开发量摆在那里不是每个项目都值得投入人力。用OPC Server转发数据配合第三方报表工具先要配OPC通道还要买报表软件的授权很多项目就死在授权费用和IT部门的审批流程上。对比下来用WinCC的VBS全局脚本直接操作Excel COM对象是一个成本极低但很实用的方案。不需要额外的软件授权不需要装额外组件WinCC运行系统本身就内置VBScript脚本宿主。脚本按时自动触发查档、导出、归档、格式化一条龙全部自己跑这就是“全脚本实现”的核心价值。它并不是什么高深黑科技但把现场一个高频又繁琐的动作彻底自动化了。1.3 哪些项目适合全脚本哪些硬上会吃亏再好的方案也有适用边界这个话必须说在前面。全脚本方案适合中小型项目、数据量在几十万条以下的报表需求比如交接班报表、日报周报、设备报警统计这类场景脚本跑起来很顺手维护成本也低。但如果你的需求是跨多个WinCC服务器做海量数据中心查询或者要做实时性要求极高的数据推送脚本方案就不是最优解了该上数据接口平台还是得上平台。另外一个底线是脚本方案解决的是“自动查询和导出”的问题不是“大数据分析平台”的问题。如果你在项目里硬塞这类需求脚本会越写越复杂最后变成一个谁也维护不了的巨型文件。我见过有人试图用VBS脚本做实时历史数据库的全量迁移跑一次要几个小时中途一个网络抖动就前功尽弃这种场景就该换工具了。2. 动手前的三个关键决策脚本语言、COM引用和运行权限2.1 VBS和C脚本谁的活谁来干WinCC的脚本体系有两套C脚本和VBS脚本选型直接决定了后面开发顺不顺。C脚本底层能力强处理数学计算和系统调用效率高但写串Excel COM对象真心不友好指针、结构体、类型转换的坑一个接一个。VBScript虽然语法看起来“弱鸡”但对COM对象的支持非常直接CreateObject一下就能拿到Excel的实例操作表格就像在VBA里写宏一样顺畅。我个人的选择原则很简单凡是跟外部COM应用Excel、Outlook、Access打交道的逻辑全部走VBS凡是需要在WinCC运行时里做高频率数据运算的算法可以用C脚本写或者把计算放到SQL查询里交给数据库引擎做。这两个脚本体系可以靠内部变量和画面来做握手彼此不冲突。记住这个分工后面会少很多折腾。2.2 Excel COM对象是一串俄罗斯套娃用脚本操作Excel本质上是让WinCC这个程序去“遥控”另一个程序。Excel是独立进程脚本只是通过COM接口一层一层往下索引先拿到Excel.Application这个顶层对象再通过它打开Workbooks工作簿继续往下拿到Worksheets工作表最后才能定位到Range单元格。这个引用链条跟我们平时点外卖是一样的逻辑先打开外卖App再选店铺再点菜品每一步都必须落在上一层的句柄上。新手最容易犯的错是拿到Range之后直接把对象丢一边脚本结束时只释放了最底层的对象Excel进程还在后台偷偷运行。正确姿势是引用层级不要跨得太深用完一个就释放一个最后把顶层Application也置空。这不是洁癖问题COM对象释放不干净Excel进程就会残留在后台后面锁文件、占用CPU都是从这个点上引爆的。2.3 运行权限与桌面交互被忽略的隐形雷区脚本本身写得没问题跑到一半莫名其妙不动了这是项目中很常见的故障。排查到最后一半以上的原因出在运行权限和桌面交互上。WinCC运行系统如果被配置成Windows服务的方式跑脚本里的CreateObject(Excel.Application)很可能创建失败或者创建出来的Excel对象完全不可见、不可响应因为服务进程没有交互式桌面的权限。另外WinCC脚本宿主是跟在运行系统这个用户会话下的。这个登录账号需要具备Office软件的正常运行权限同时桌面要允许自动化界面可见你可以把Visible设成False但创建过程本身需要一个可交互的会话。以前我在调试时碰到过一个很诡异的现象脚本在开发机上跑得好好的部署到现场服务器上就卡死在创建Excel那一步查了半天才发现现场主机用了精简版操作系统缺了Excel的自动化组件。所以动手前先确认三件事脚本运行在普通交互会话里、Office完整安装、脚本宿主的位数和Office位数一致。第三点后面踩坑章节还会详细展开。3. 数据查询脚本的核心链路从WinCC读到Excel表3.1 读当前值HMIRuntime.Tags直接用如果只是查询当前时刻的过程值用WinCC的VBS接口非常直白。在全局脚本或画面脚本里通过HMIRuntime.Tags集合就能拿到变量对象再调用Read方法读取数值。Dim objTag Set objTag HMIRuntime.Tags(Motor_Speed) MsgBox objTag.Read Set objTag Nothing这里有个容易被忽略的细节Read方法返回的是Variant类型如果变量是浮点数、整数、二进制字符串直接用就行但如果你的变量是RawData类型的字节数组VBS里处理起来就很别扭需要自己解析字节串。所以做数据导出时我一般建议用浮点和整数类型的内部变量或过程变量没有必要背这个额外的复杂度。批量读取当前值时不建议写一个一大堆Read调用的超长脚本每条Read指令都是一次进程内的查找动作数量一多整体性能会被拖下来。更合理的做法是先在WinCC那边把需要导出的点汇总成一个变量列表脚本里循环读取的同时把每个点的质量状态Quality也一起带出来。质量状态是很多报表脚本容易漏掉的字段你不带这个数据报表里出现一个跳变的坏值下游分析的人根本不知道这数据在那一刻本来就不可信。3.2 读历史归档走OLE DB Provider别碰后台库读历史归档比读当前值要麻烦一个层级。WinCC的历史数据是存在后台数据库里的但我不推荐直接拿SQL账号连后台库去查表结构非常复杂而且不同版本差异巨大稍有不慎查出来的数据量巨大还会给现场的实时归档带来额外压力。推荐的做法是用WinCC自带的OLE DB Provider来查归档数据用类似SQL的查询语法把“变量名、归档名、时间范围”作为查询条件交给接口层处理。写过很多脚本后我发现这套接口在不同版本上的连接字符串略有差异但骨架基本一致Dim objConn, objRS Set objConn CreateObject(ADODB.Connection) objConn.ConnectionString ProviderWinCCOLEDB;Data Source.;Initial CatalogWinCC;Integrated SecuritySSPI objConn.Open Set objRS objConn.Execute( _ SELECT Value, DateTime _ FROM TAG:Motor_Speed;ARCHIVE:Archive1 _ WHERE DateTime 2024-01-01 00:00:00 _ AND DateTime 2024-01-02 00:00:00) objRS.Close Set objConn Nothing注意上面只是骨架代码实际使用时要先确认现场WinCC项目的数据库名称、归档名称和OLE DB Provider名称。我的经验是在开发阶段不要一次把整年数据查出来先查一天确认返回的字段和值是你想要的再逐步拉长查询区间。这个做法能帮你避开一大半因版本差异导致的语法错误。3.3 写入Excel循环写入是最慢的写法拿到查询结果之后下一个关键动作就是写Excel。绝大多数人第一次写Excel导出脚本时都会写成下面这种逐格循环For i 0 To iRowCount - 1 objSheet.Cells(i 1, 1).Value arrValue(i) objSheet.Cells(i 1, 2).Value arrTime(i) Next这个写法逻辑上没错跑起来却会让你怀疑人生。为什么慢因为每次访问Cells(i, j)都是一个跨进程的COM调用Excel应用收到请求要刷新一次界面状态写5000行数据意味着要来回通信至少5000次每一次都在交“过桥费”。把整批数据打包成二维数组一次性赋值给Range区域才是正解Dim arrData(5000, 2) 填充数组... objSheet.Range(A1).Resize(5001, 3).Value arrData等号右边是一个完整的二维数组Excel收到之后会在内部一次性完成所有单元格的填充通信次数从几千次骤降到一次。这个差异不是百分之几十而是数量级的差别。后面的提速章节我会拿实测数据来说明这一小节你只要记住一个结论永远不要循环写单元格永远用数组整体赋值。3.4 让查询自动跑全局脚本触发与文件命名手动运行脚本只是第一步全脚本方案的灵魂在于“自动”。WinCC的全局脚本Global Script支持设置触发器常见的触发方式有周期触发、变量触发、时间触发。比如每个整点触发一次或者每天凌晨一点把昨天的归档数据自动导出。设置好之后运行系统只要开着脚本就会自己按计划跑根本不需要人来点按钮。自动运行带来的另一个问题是文件管理。导出文件名如果不带时间戳第二天就会被覆盖。我用日期命名已经形成了标准习惯格式建议用“报表_20240102.xlsx”这样的可排序模式前缀一致、日期用yyyyMMdd在文件夹里按名称排序就是天然的时间轴。注意文件名里要保持年月日顺序用“01022024”这种美式日期排序会让你崩溃别问我是怎么知道的。4. 数据查询提速实战把查询速度真正提上去4.1 慢的根源是COM跨进程的“过桥费”很多工程师以为脚本慢是WinCC查询本身慢但实际项目里测下来查询往往只占了20%的时间剩下的80%全耗在数据搬运上。WinCC脚本和Excel是两个独立的进程进程之间通信走COM每一次读写单元格都相当于你去窗口排队办一次业务。办1000次业务自然比一次性递一沓材料要慢得多。理解这一点特别重要因为提速的思路全都从这里生发出来减少跨进程通信次数把能在本地处理的事情尽量在本地做把大批量数据打包成“整批业务”交出去。4.2 第一板斧让Excel不显示、不刷新、不弹框Excel对象创建之后默认是可见的这一下就会拖慢速度。脚本运行时没有必要让用户看到Excel的每一次刷新关掉显示是最便宜但收益最高的一步。objExcel.Visible False objExcel.ScreenUpdating False objExcel.DisplayAlerts FalseVisible设为False让Excel窗口不渲染ScreenUpdating设为False暂停界面刷新DisplayAlerts设为False禁止弹出“是否保存”之类的对话框。这三行代码一加同样的逻辑能快一大截而且省去了弹窗卡脚本的风险。提醒一句DisplayAlerts关闭后脚本结束时如果没主动保存Excel不会问你而是直接不保存这个你自己心里要有数。4.3 第二板斧SQL查询条件前移和列裁剪查询历史归档时很多人习惯先把整表数据SELECT *拉出来再在VBS里过滤。这是把不该干的活揽到自己身上。数据库引擎本身有索引和条件优化被查的数据越少网络传输和脚本侧的内存占用都越小。我的标准姿势是时间条件必须写进SQL的WHERE里字段列表只查需要的列如果只需要某几个归档点就分别按归档名查询不要设计一个把全部变量查出来再挑拣的SQL。比如查电机转速趋势只需要Value和DateTime两个字段SELECT *纯属浪费。4.4 第三板斧批量读取与数组整体赋值前面提到过Recordset拿到之后不建议在VBScript里用循环访问每一行。ADODB.Recordset自带一个GetRows方法可以直接把结果集一次性装进二维数组然后再把数组整体喂给Excel的Range两重加速叠加起来效果非常明显。Dim arrResult arrResult objRS.GetRows() 注意GetRows返回的数组第一维是字段编号第二维是行编号 通常需要先转置或重新构造再赋值给Range关于GetRows返回数组的维度顺序我踩过坑也刷了不少文档。不同工具里数组的下标含义很让人迷惑稳妥的做法是拿到数组后先用UBound确认维度边界再按照字段数、记录数做一次内存重排构造出Range需要的二维数组然后一次性赋值。这个过程本身发生在脚本进程内没有跨进程通信花的时间微不足道。4.5 实测对比脚本改完后到底快了多少纸面讲再多都不如直接看实测数据。我用一组5000行、3列的历史归档数据做了个简单对比机器是普通工控机WinCC和Excel同机安装。同一个查询任务不同写法耗时如下写法查询耗时写入耗时总耗时约全默认配置 逐格循环写入6秒72秒78秒关闭界面刷新 逐格循环写入6秒41秒47秒关闭界面刷新 数组整体赋值6秒2秒8秒SQL列裁剪 数组整体赋值3秒2秒5秒这个数据说明两件事写入方式从循环改成数组直接把最耗时的大头干掉了SQL条件前移则从源头上减少了跨进程搬运的数据量。把这几板斧叠加完整个流程从将近80秒压到5秒以内提速非常可观而且全程没加任何硬件、没换任何采购件纯粹是脚本层面的优化。5. 现场踩坑记录最值得提前避开的典型问题5.1 Excel进程残留报表日期明明是昨天文件却被锁住脚本跑完之后如果文件还是被占用打开资源管理器一看一堆EXCEL.EXE进程挂在后台大概率是COM对象没有释放干净。这事的根因通常有两种脚本中途报错跳出了流程后面的对象释放代码压根没执行或者脚本用VisibleFalseExcel进程退得不够彻底。解决方案分两层。第一层是写脚本时保证出错也能清理用On Error Resume Next跳过非关键错误在脚本末尾统一释放对象先把Workbook关闭再Quit掉Application最后逐个Set Nothing。第二层是防残留可以给脚本加一个启动前的检查逻辑如果发现目标文件被占用就提示或先强制清理进程。注意taskkill /f /im EXCEL.EXE会杀掉电脑上所有Excel实例如果现场有人正开着Excel做别的活千万别在脚本里直接调用它建议做成带确认的手动清理工具。5.2 Office位数不匹配CreateObject成功却啥都拿不到WinCC脚本宿主的位数和Office的位数如果不一致CreateObject可能能成功但拿到的对象状态会很奇怪比如属性读取不到、方法调用报错。这个坑很隐蔽因为报错信息往往不是一句明确的“位数不匹配”而是看起来毫无规律的“操作失败”或“类型不匹配”。排查方法是写一行最简测试脚本只创建Excel对象并输出版本号先验证这条链路通不通。我经历过一次现场排查WinCC装的是32位Office Office装了64位两者共存时一切看起来正常但脚本一操作Sheet就报错。最后的解法是把Office统一换回32位整条链路立刻通了。别觉得64位Office更先进就非它不可在WinCC这个场景里32位Office和WinCC脚本宿主的一致性比“先进”值钱得多。5.3 时间格式的本地化陷阱WinCC归档查询里时间条件的写法直接决定了能不能查到数据。有些现场机器的系统区域设置是中文有些是英文同样的时间字符串在一种语言下正常在另一种语言下就查不到。SQL里直接写2024-01-02 00:00:00这种ISO格式在绝大多数环境下比较稳但Windows的区域设置偶尔会插手日期解析导致查询结果为空。我的做法是脚本里统一生成日期字符串避免手写硬编码。用DateSerial、DateAdd这些VBS自带的日期函数算出起止时间再按照yyyy-mm-dd hh:mm:ss的格式拼出来。另外提醒一点查询的起止时间要算清楚边界查“昨天”的数据起点应该是昨天0点整终点是今天0点整很多人习惯写“昨天23:59:59”看起来对实则把最后一秒丢掉了而且容易在跨夜时出问题。5.4 一次查三个月的数据脚本差点把内存吃满把数据量想得太小是导出类脚本最危险的。某次我为了做一台设备的月度能耗分析写了个查全月归档的脚本预期数据量不大结果因为采集周期是秒级一个变量一天就是8万多条记录一个月下来有几百万条。整个Recordset一次性灌进内存脚本直接卡死Excel赋值阶段更是寸步难行。对付大结果集原则是分页或分段。可以先按天循环每天查一次数据写入一个临时分组再合并到最终文件也可以只取按小时聚合过的数据使用数据库端的聚合函数把数据量压下来。查询脚本不是数据仓库永远不要把“把所有原始数据全部搬出来”当成默认方案先在SQL层面按业务粒度做一次压缩性能问题大概率不治自愈。5.5 给脚本装一双眼睛自写日志定位问题定时脚本最怕什么呢怕半夜三点跑的时候报错了第二天早上没人知道报表文件还是旧的。脚本没有日志排查问题就全靠猜。无论脚本多简单我都习惯加一个写日志文件的小函数记录启动时间、每个关键阶段耗时、最后抛出的错误编号和描述。日志输出可以用最简单的脚本方式比如生成一个txt文件往里追加文本不依赖任何外部组件。有了日志脚本跑没跑、跑到哪一步挂的、SQL查出多少行全都有据可查。这个习惯平时看着繁琐真正出问题的时候能帮你省掉半天排查时间。我甚至可以这么说给脚本加日志和给设备加传感器是一个道理都是让不可见的东西变得可见。5.6 关于直接连后台数据库我的忠告最后一条踩坑记录也是我见过最多人栽跟头的地方WinCC的历史归档明明存在SQL Server里不少人觉得绕过OLE DB Provider直接用数据库账号连上去查会更直接。这个思路从技术上讲确实可行有些老项目也确实是这么干的但我强烈不建议把它当默认方案。WinCC后台数据库的表结构是跟版本绑定的版本一升级表结构就可能调整脚本里的SQL就全废了。更危险的是直接操作后台库还会带来并发读写风险一个不谨慎的查询可能加重数据库负担影响正在运行的数据归档。OLE DB Provider存在的意义就是把这些复杂度包住给上层提供一个稳定的查询接口。我的忠告是既然全脚本方案的初衷就是省事、可靠、可控那就把底层访问交给官方接口别给自己挖坑。6. 最后分享一个让脚本更耐用的习惯写WinCC与Excel交互脚本一开始我总想着把功能一次性写完后来发现最耐用的脚本都是从小骨架长起来的。先跑通一分钟的数据导出确认环境、对象、数组这些都顺畅再逐步加定时触发、文件命名、异常日志和提速优化。每一步都有日志可查每一步的改动都能明确知道到底快了多少、稳了多少。如果你准备在项目里试这套全脚本方案我建议你先在自己的开发环境搭一个最小测试脚本用自带样例变量跑通整条链路再拿到现场改变量名和归档名。这样既能避开大部分环境兼容问题也能在真正接数据之前把脚本的“脾气”摸熟。数据交互脚本这个东西只要跨进程通信稳了、批量数据处理用对了剩下的大头就是业务本身了——报表该怎么排、数据该按什么粒度出那又是在现场和工艺人员反复对出来的东西了。
阅读完成 · 觉得有帮助?
咨询建站