WinCC日报自动生成Excel:全局脚本定时触发,三行核心代码解放双手
做WinCC报表开发这几年我摸到一条规律报表需求几乎不会在白天冒出来它总是在晚上交班后、在第二天一早要开晨会前突然砸过来。今天想分享的这个骚操作就是我把每日报表从“人工打开画面、手动导出CSV、再加工成Excel”改成了“运行系统到点自动生成Excel”核心逻辑压缩成三行代码往后再也不用半夜爬起来看报表文件。如果你正在维护一套WinCC监控系统每天需要产线数据、巡检数据或者交接班报表又或者领导总在早会上问昨天到底干了多少活那这篇内容值得你花十分钟看完。它不教你做漂亮画面只解决“数据一直在、报表一直难”的最后一公里。下面直接说方案。1. 报表需求为什么总在半夜来我的方案是被现场逼出来的先说背景。我之前接手的项目是一个食品车间的生产监控WinCC管两条生产线加公用能源画面里变量几百个PLC里累加值、运行时长、报警信息都有。但问题在于数据都存在就是没法直接变成一张领导看得懂的报表。当时的日报流程是这样的每天早班班长打电话来要昨天的产量我得远程登上WinCC打开变量记录表格选好时间范围手动导出CSV再打开Excel粘贴、排格式、算汇总最后另存成当天日期的文件发到工作群里。整个过程顺的话二十分钟遇到报警记录多的日子还得对着报警清单逐条核对时间直接翻倍。这个流程有三个痛点最容易让人崩溃。第一手动选时间范围早班、中班、夜班交界的时间点很容易选错抽出来的数据差几分钟产量就对不上。第二CSV文件的中文经常乱码尤其是用老版本Excel直接双击打开的时候表头全变“锟斤拷”。第三每次手工排的格式都不一样今天加一列、明天合并两个单元格后天上头问为什么周报和日报数据对不上只能自己背锅。我一开始也想过用WinCC自带的报表编辑器。说实话做固定格式的打印报表它够用但问题在于定制模板有学习成本而且生成的报表格式偏“系统化”现场领导要看的往往是Excel格式因为Excel他们可以直接改、直接算、直接转发。WinCC自带方案最大的短板还是“定时自动化”能力弱它更适合“我点一下你出一张”而不是“每天早上六点你自己出一张”。所以我的结论是问题不在报表工具不够多而是缺一个“到点自己跑”的机制。方向定了就是“WinCC全局脚本定时动作Excel COM对象”把人的手工动作变成固定代码。这里有一个关键认知WinCC的全局脚本动作不依赖某个画面是否打开只要运行系统在跑定时触发到了就会执行。也就是说只要电脑不关机、运行系统不断报表这件事就可以完全没人管。2. “三行代码”不是标题党是把报表拆成了三个关键动作很多朋友看到“三行代码”的第一反应是吹吧用代码访问Excel导数据少说也要几十行。这里先解释一下所谓三行不是整个脚本只有三行而是把报表生成这条链路压缩成三个关键动作读变量、建Excel、存文件。所有复杂的逻辑都可以塞在这三个动作的缝隙里而真正不可省的核心只有这三步。核心代码可以简化成下面这样数据 HMIRuntime.Tags(Prod_Total).Read 第1行从WinCC读变量 Set xlApp CreateObject(Excel.Application) 第2行创建Excel对象 xlBook.SaveAs D:\WinCCReports\日报.xlsx 第3行保存文件当然实际脚本中还要写标题、填内容、关闭释放但这些都属于“结构代码”不是“业务逻辑”。你先把这三个动作想明白后面的脚本就是往缝隙里塞循环和判断而已。为什么选VBS不选CWinCC全局脚本同时支持C和VBScript但操作Excel这件事VBS有天然优势。C脚本要调用Excel的COM接口需要声明一堆变量和指针代码量会翻好几倍VBS本身是为脚本场景设计的CreateObject(Excel.Application)一句就把Excel进程拉起来了后面的单元格读写也直接。现场维护的非计算机专业同事看VBS也能猜个大概。为什么非要Excel而不是CSV因为报表最终是给人看的Excel能预置模板、能写公式、能设置边框CSV就是个纯文本表。工厂里几乎所有业务人员都会用Excel但没几个人愿意操作CSV。你用系统导出来的东西越贴近使用者的习惯后面扯皮就越少。为什么不用WinCC自带的报表编辑器我做个一个对比大家根据自己的场景选方案定时自动触发输出格式灵活度学习成本适合场景WinCC报表编辑器一般多靠手动/画面触发中等调整模板要学报表语法高固定格式的打印报表、交班报表全局脚本Excel COM强支持每日/每周定时高Excel模板随便改中需要发到业务部门、需要二次统计的日报/周报人工导出CSV再加工无高低临时性、一次性报表我这里不是说WinCC报表编辑器没用而是如果你要做的是“自动生成、自动发送、别人拿去直接能用”的Excel日报脚本方案明显更短路径。3. 从三行到完整脚本定时日报的落地全过程3.1 先规划报表内容再写代码我见过不少初次写报表脚本的人上来就打开代码编辑器开始写结果报表字段改了三次脚本重写三遍。正确顺序是先手工做一张Excel样例把字段、数据来源、统计口径定死再动手写脚本。以我那个食品车间为例日报字段是这些报表字段数据来源说明报表日期系统时间取当日零点到当前总产量WinCC变量 Prod_TotalPLC累加值A线产量WinCC变量 Prod_LineAPLC累加值B线产量WinCC变量 Prod_LineBPLC累加值运行时长WinCC变量 RunTime_Min设备运行分钟数停机次数WinCC变量 Stop_Count累计停机次数报警数量WinCC变量 Alarm_Count当日报警总数这里有一个重要的统计口径问题日报里的“总产量”到底要的是当日增量还是历史累计值如果PLC里存的是“开机以来总产量”脚本就必须先读昨天的留档值再用今天的累计值减昨天。为了省掉这段补偿逻辑我在WinCC里提前加了一个当日产量计算变量每天0点由PLC或WinCC脚本自动清零报表脚本只管直接读取。这算是报表自动化里面最容易被低估的一个步骤宁可多加几个计算变量也不要在脚本里反复做数据补偿。3.2 在WinCC全局脚本里新建动作操作路径很简单打开WinCC资源管理器找到“全局脚本”下的“动作”右键新建一个动作命名Daily_Report语言选VBScript然后在编辑区粘贴脚本。这里要特别说一下“动作”和“画面脚本”的区别。画面里的按钮脚本是“人点了才跑”而全局脚本的“动作”是由系统调度器触发的只要运行系统在运行触发器一到时间它就会自己执行。这正是整个方案能“解放双手”的基础。触发器设置也是在这个动作的属性里做。我用的“日期/时间触发器”选“每天”填06:00:00。WinCC的日期/时间触发器支持设置具体的执行日期和时间规律你甚至可以定义只在工作日触发周六日不生成周报送领导的统计数据灵活度比我想的还要好。3.3 完整VBS脚本示例下面是我整理后的一个可复用的完整脚本里面保留了注释方便大家改成自己的变量名Option Explicit Dim xlApp, xlBook, xlSheet Dim sDate, sReportPath, sLogPath Dim i, tagArray, titleArray 要读取的变量名可自行扩充 tagArray Array(Prod_Total, Prod_LineA, Prod_LineB, RunTime_Min, Stop_Count, Alarm_Count) 变量对应的中文标题顺序必须一致 titleArray Array(总产量, A线产量, B线产量, 运行时长(Min), 停机次数, 报警数量) 报表归档路径建议纯英文路径避免编码问题 sReportPath D:\WinCCReports sLogPath sReportPath \script.log 创建Excel COM对象 Set xlApp CreateObject(Excel.Application) xlApp.Visible False xlApp.DisplayAlerts False Set xlBook xlApp.Workbooks.Add() Set xlSheet xlBook.Worksheets(1) 生成日期字符串 sDate Year(Now) - Right(0 Month(Now), 2) - Right(0 Day(Now), 2) 写入表头 xlSheet.Cells(1, 1).Value 生产日报表 xlSheet.Cells(2, 1).Value 报表日期: sDate xlSheet.Cells(4, 1).Value 项目 xlSheet.Cells(4, 2).Value 数值 循环读取变量并写入Excel For i 0 To UBound(tagArray) xlSheet.Cells(i 5, 1).Value titleArray(i) xlSheet.Cells(i 5, 2).Value HMIRuntime.Tags(tagArray(i)).Read Next 保存文件文件名带日期 Dim sFile sFile sReportPath \Daily_ Year(Now) _ Right(0 Month(Now), 2) _ Right(0 Day(Now), 2) .xlsx xlBook.SaveAs sFile 日志方便排查问题 Dim fso, fLog Set fso CreateObject(Scripting.FileSystemObject) Set fLog fso.OpenTextFile(sLogPath, 8, True) fLog.WriteLine Now 报表生成成功: sFile fLog.Close 释放对象顺序不能乱 xlBook.Close False xlApp.Quit Set xlSheet Nothing Set xlBook Nothing Set xlApp Nothing这段脚本里有几个细节值得说明。Right(0 Month(Now), 2)是为了让月份、日期都显示成两位数字保证生成的报表文件名按日期排序时不会出现“1、10、11、2”这种混乱顺序。DisplayAlerts False是关闭Excel的弹窗确认不然脚本跑到一半弹一个“是否保存”半个小时后过来它还在等着你点按钮。日志写到文件里是为了排错后面踩坑章节我会专门讲日志的价值。3.4 触发器设置与运行系统验证新建动作后在动作的属性窗口里添加“触发器”类型选“日期/时间触发器”。如果你的需求是每天早上固定时间就选每天06:00:00。如果还要在周一早上额外发一份上周汇总再加一个触发器设成“每周”即可。测试时不要干等。我一般会把触发时间临时改成“当前时间后两分钟”比如现在是14:23就设14:25:00然后切回运行系统等待。两分钟后去看D:\WinCCReports目录有没有新文件有就打开检查内容没有就去看日志文件。确认一切正常后再把触发器改回正式时间。有一点必须提醒全局脚本动作依赖运行系统的调度器所以WinCC项目必须始终处于运行状态。很多工厂为了省事会把WinCC运行系统做成开机自启动但如果是“用户未登录系统”的自启动方式脚本执行时可能无法正常访问Excel这个下面踩坑部分细说。4. 脚本写好了却出不来报表我踩过的坑和完整排查链路方案再漂亮落地时总会遇到几个“脚本没问题但报表就是出不来”的时刻。这一节我把这几年实际踩过的坑按现象列出来每个都给出排查链路和解决思路。4.1 现象一任务管理器里Excel进程越来越多第一次上线自动日报后我到现场发现服务器越来越卡打开任务管理器一看十几个EXCEL.EXE进程堆在那。原因很简单WinCC动作每次触发都会创建一个新的Excel COM实例如果脚本中途报错或者运行系统在Excel对象释放前被强制关闭xlApp.Quit这行代码根本没执行到Excel进程就永远留在内存里。解决办法分两步。第一步脚本保证执行后释放xlBook.Close、xlApp.Quit再把对象置空。第二步在服务器上做一个维护计划任务每周固定时间执行一次清理命令把残留的Excel进程清掉taskkill /f /im EXCEL.EXE注意这个命令会把那台服务器上所有正在打开的Excel全部强制关闭。如果维护工程师正好在服务器上手工编辑表格那就会白干一场。所以清理任务最好安排在凌晨或者干脆只在月底维护窗口手动执行。4.2 现象二CreateObject(Excel.Application)直接报错有段时间动作触发器到了日志一个“报表生成成功”都没有打开动作信息一看错误提示是“ActiveX component cant create object”。这属于典型的COM对象创建失败。排查链路我先说大家照着走一遍。第一步确认脚本里CreateObject(Excel.Application)这个对象名没写错。第二步单独用系统自带的cscript跑一个小测试脚本确认这台电脑的Excel COM能不能正常调用Set xl CreateObject(Excel.Application) WScript.Echo OK如果这步就报错问题基本出在Office安装上。我遇到过的原因主要有三个一是Excel是精简版/绿色版COM组件注册不完整二是Excel版本是64位而WinCC运行系统是32位COM注册路径对不上三是WinCC运行系统在“未登录桌面”的会话里以自动启动方式运行Excel无法在非交互式会话里创建对象。解决办法也挺直接在服务器上安装完整的32位OfficeWinCC运行系统要保证是正常用户登录会话下启动的不要用“用户未登录时开机自启”的服务化方式。这个点特别阴现场看起来WinCC画面正常在跑但报表脚本就是创建不了Excel对象查了半天才发现是会话隔离问题。4.3 现象三报表文件生成了但数据全是0或者空这个坑我在做自动报表的第三个月踩到。文件每天都有Excel也打得开但“总产量”那一栏有时候是0有时候干脆是空单元格。问题出在变量读的时机上。WinCC变量管理里的内部变量在运行系统刚启动或者变量值尚未从PLC刷新回来时用HMIRuntime.Tags(...).Read读出来可能是Empty。把这个Empty直接写入Excel表现出来就是0或者空白。另一个可能原因是PLC的当日产量累加值刚好在触发瞬间被清零脚本读到的是清零后的值。我的解法是三层保险第一触发时间避开变量更新和清零的临界点安排在交班后数据稳定的时段第二在脚本里读两次第一次读到的值如果为空或明显偏小隔两秒再读一次第三把关键变量先传给WinCC的中间变量让PLC确认这个值已经稳定跟上脚本只读中间变量。层层加码之后空数据的情况基本消除了。4.4 现象四文件名乱码、日期排序不对还有一类小问题不出现在Excel内容里而是出现在文件名上。直接用Month(Now)生成的月份不带前导零8月会变成Daily_2024_8_5.xlsx和10月混在一起排序时会排在9月前面看起来非常乱。中文路径在某些精简版Excel和WinCC版本组合下打不开文件也是一个常见问题。解决方式就是我上面脚本里写的日期字符串用Right(0 Month(Now), 2)补零报表路径一律用纯英文不放在“文档”或者“报表”这种中文目录下。这些看起来是小事但现场维护的人只看结果目录里一排文件名整整齐齐他们才会觉得这套东西靠谱。4.5 排查一定要留日志前面反复提到日志这是我权衡之后最后悔没早点加的功能。第一次脚本不生成文件我只能盯着屏幕猜后来才意识到WinCC动作本身有执行状态但不够细看不出是卡在哪一行。加了日志函数之后脚本跑到哪一步、读出了什么值、保存的文件路径是什么全都能看到。推荐大家在脚本里加这样一个通用日志函数Sub WriteLog(msg) Dim fso, fLog Set fso CreateObject(Scripting.FileSystemObject) Set fLog fso.OpenTextFile(D:\WinCCReports\script.log, 8, True) fLog.WriteLine Now msg fLog.Close End Sub调用方式就是Call WriteLog(读取变量成功)。排查时打开日志文件按时间一看就知道脚本有没有执行到关键步骤。下面把最常见的五种情况汇总一下方便大家对照现象可能原因排查方法没有文件生成触发器没生效/动作被停用/COM创建失败看WinCC动作状态看脚本日志文件生成但全为0读取时机不对/变量为空读两次中间变量Excel进程堆积对象未释放/异常中断检查释放代码定时清理中文乱码文件编码/路径中文路径用英文文件名补零内容重复触发器秒级并发执行触发器设“触发一次”加防重入5. 日报只是一个起点周报、月报、邮件和模板化日报跑通之后方案差不多就到“解放双手”的阶段了。但同一个三段式逻辑稍微改一改就能延伸出更多报表形态。这里分享几个我实际扩展过的方向。5.1 周报、月报的自动汇总日报每天生成一个文件到周五想把五天的数据汇总到一张Excel里不需要让脚本从头去读WinCC变量。更省事的办法是让VBS脚本去遍历这一周生成的日报文件用Workbooks.Open打开读取指定单元格再写入汇总表。For d 1 To 5 Dim dayFile dayFile D:\WinCCReports\Daily_ 日期 .xlsx Set wbTemp xlApp.Workbooks.Open(dayFile) xlSheet.Cells(d 1, 2).Value wbTemp.Worksheets(1).Cells(5, 2).Value wbTemp.Close False Next这种做法的好处是日报和周报完全不共享业务逻辑。日报出错了改日报脚本周报出错了改周报脚本不会因为一处改动影响另一处。5.2 统计逻辑交给Excel公式很多人忽略了WinCC脚本后面有个Excel替你在自动算数。平均值、最大最小值、累计求和这些统计完全可以在脚本里直接写Excel公式xlSheet.Cells(10, 2).Formula SUM(B5:B9) xlSheet.Cells(11, 2).Formula MAX(B5:B9)这样日常维护统计口径时不需要懂WinCC只要会改Excel公式就行。我把项目交接给现场工程师之后他们自己改了两次统计规则完全没动VBS脚本只改了模板里对应的公式。这就是所谓的“把变化留在Excel层把稳定留在WinCC层”。5.3 自动邮件发送产线领导并不一定每天登录WinCC看报表更常见的是希望每天早晨邮箱里躺着一份日报。这个需求可以用CDO邮件组件实现脚本把Excel生成后直接发出去Set msg CreateObject(CDO.Message) msg.Subject 生产日报 sDate msg.From scadacompany.com msg.To managercompany.com msg.AddAttachment sFile msg.Configuration.Fields.Item(http://schemas.microsoft.com/cdo/configuration/sendusing) 2 msg.Configuration.Fields.Item(http://schemas.microsoft.com/cdo/configuration/smtpserver) smtp.company.com msg.Configuration.Fields.Update msg.Send如果公司内部用的是Outlook也可以改用CreateObject(Outlook.Application)发邮件。邮件收件人、抄送人最好做成一个Excel配置表脚本读配置而不是写死在代码里这样领导变动时你自己改Excel就行不用上服务器改代码。5.4 用模板保住报表颜值报表自动化做到最后大家追求的不是“能生成”而是“生成出来就能直接用”。保底做法是运维一张“报表模板.xlsx”里面预置好公司Logo、表头样式、边框颜色、打印区域、公式脚本打开模板写入数据再另存为当天文件。这样每天的报表长一个样不会因为脚本版本的差异导致格式漂移。维护方式也很简单模板文件用Excel打开改样式保存后下一次脚本自动生效。写死的脚本永远不做格式调整所有的视觉调整全部在Excel模板里完成。这对于一家公司里“会Excel的人比会WinCC的人多得多”的现实来说是最聪明的分工。5.5 版本兼容提醒如果你用的是新版本的WinCC Unified要注意它已经转向基于JavaScript的脚本体系不再支持VBScript。但上面这个三段式思路完全不变读变量、创建Excel文件、保存。差别只是换了语言和API调用方式。新项目上手统一方案时不要照抄VBS代码先理解“读数据—写文件—定时跑”这套架构再用目标语言重新实现反而比硬翻译更快。最后再分享一个小技巧吧。我在每次动作执行完都固定写一行日志格式很死板“生成成功 文件名”。一开始觉得多余后来很多次排查全靠这一行日志文件没生成看日志有文件但内容错看Excel连日志都没有说明动作压根没触发去看WinCC运行状态。就靠这一行字省下了你好几次爬服务器查看的功夫。这套报表方案能从两三行代码越滚越完整靠的其实不是代码本身而是把每一步都变成可观测的习惯。