做WinCC项目这些年,被业主塞过来最多的一句话就是:“给我做张报表,每天24小时的数据,注意我这个值是累计值,你帮我算成每小时的差值。”这句话听着不难,但真落地的时候,里面全是细节:累计值怎么取、差值在哪个环节算、归档配置不对数据为什么对不上、跨天清零又怎么处理。这篇文章就把我多次做wincc报表的完整思路和踩坑过程捋一遍,尤其是“累计值差值日报表”这个需求,从需求拆解讲到SQL实现,再到归档配置和排错,希望能帮大家少走点弯路。适合正在用WinCC做生产报表、能耗报表、产量统计的朋友参考,也适合刚接手工控项目报表开发的新手。
1. 先读懂需求:日报表不是“抄24个数”那么简单
1.1 区分累计值和瞬时值,报表逻辑完全不同
做报表之前,先把变量分类理清楚。瞬时值看的是当前状态,比如压力、温度、流量速度,日报表显示整点快照或者小时平均值就可以;累计值看的是从某个起点累积起来的总量,比如电度、水流量积算、产量计数,它只增不减(除非复位)。
你想想家里水表:月末看表底数减去月初表底数,才知道这个月用了多少吨水,把表底数直接报成“这个月用水量”是没有意义的。工业现场的道理完全一样。所以凡是从电表、流量积算仪、计数器进WinCC的变量,做日报表时必须先算差值,再谈显示。
很多项目里甲方自己都没说清楚,只丢一句“给我出日报表”。这时你要主动问三个问题:这个变量是累计值还是瞬时值?报表是按每小时一行出24行,还是按整点快照出24个点?累计值如果清零了怎么处理?这三个问题不搞清楚,后面返工概率极高。
1.2 “每日24点数据”有两种理解,别一开始就做反
标题里“每日24点数据”这句话,我见过至少两种理解。第一种是抄24个整点瞬时值,0点、1点……23点各一行;第二种是把一天切成24个小时段,每个小时段显示该时段内的用量。
结合后面那句“如果设置的是累计值,计算每小时的差值”,基本可以断定需求是第二种:每天出24行,每行是某个小时内的累计值增量。如果你只按第一种做了,甲方会追着你改;如果你两种都做了,反而显得专业。
实际项目里,累计值日报表最常见的是下面这种布局:
| 时间区间 | 电度差值(kWh) | 用水差值(m³) | 班次 | 备注 |
|---|---|---|---|---|
| 00:00-01:00 | 12.5 | 3.6 | 夜班 | 正常 |
| 01:00-02:00 | 11.8 | 3.4 | 夜班 | 正常 |
| 02:00-03:00 | 13.2 | 3.8 | 夜班 | 正常 |
| ... | ... | ... | ... | ... |
| 23:00-24:00 | 10.2 | 3.1 | 中班 | 正常 |
| 日合计 | 287.4 | 89.2 | - | - |
注意,这里每一行的“差值”指的是该小时段内的增量,而不是“这个小时结束时表上显示的数字”。这是整套报表的核心语义,一定先跟甲方对齐。
1.3 被截断的“和最”:到底还需要哪些统计项
标题最后“和最”两个字被截断了,但根据经验,后面大概率是“和最大值、最小值、平均值”或者“和日合计”。做报表需求调研时,要把甲方这句话问全,否则做出去不是缺列就是多列。
一份完整的累计值日报表,除了每小时差值,通常还包括:日合计、当天0点/24点的累计值底数。如果工厂是两班制或三班制,还要有班次小计。瞬时量变量则经常需要小时平均、小时最大、小时最小。
我给一个典型列结构做参考:
| 列类型 | 说明 |
|---|---|
| 时间区间 | 每小时的起止时间,跨班次时标注班次 |
| 差值 | 本小时末的累计值减去上一小时末的累计值 |
| 累计值底数 | 本小时末表上显示的实际累计值,供核对 |
| 日合计 | 当天所有小时差值的总和 |
| 班次小计 | 按班次分段汇总的小计数 |
| 数据状态 | 标记该小时数据是否稀疏、是否发生复位 |
把这些带齐了再写代码,报表一版过的概率高很多。
2. 为什么不能直接读变量:累计值差值离不开归档机制
2.1 WinCC里没有“累计值类型”,有的是累计型过程量
很多刚入门的人会找WinCC里有没有“累计值”这个属性,结论是:WinCC变量类型只有二进制、8/16/32位整数、浮点、文本这些,并没有一个“累计值类型”的勾选项。
我们常说的累计值,本质是外部设备(电表、流量计、PLC累加器)算好后送进来的过程量。因此差值计算必须自己做,可以在三个位置做:PLC里算、WinCC脚本里算、报表查询里算。
我的习惯是放到报表查询里算,因为PLC里算要改下位机程序,脚本里算会长期占用WinCC运行资源,而SQL一条语句就能解决,报表生成时现算现出,平时不消耗任何性能。
2.2 归档方式决定你能拿到多细的数据
WinCC的变量归档有三种常见方式,对报表取数的影响差别很大:
| 归档方式 | 记录条件 | 对差值报表的影响 |
|---|---|---|
| 周期归档 | 每固定时间记一条 | 数据最完整,适合差值计算 |
| 变化归档 | 数值变化才记录 | 累计量长时间不变时点数太少,小时末值可能缺失 |
| 压缩归档 | 在周期归档基础上按时间段压缩 | 存AVG/MIN/MAX/LAST,直接算差值容易失真 |
对累计值做差值,最理想的数据源是周期归档的原始数据。每个小时段内至少要能取到最后一条有效记录,如果归档周期太长或者点数太少,差值计算会缺脚。
这里有个容易忽略的点:归档周期和画面采集周期是两回事。你在画面上看到的数值刷新频率由画面采集周期决定,而数据库里写不写历史记录由归档周期决定。有些工程师只改了画面采集周期,忘了改归档周期,结果报表里拉出来的数据颗粒度完全不对。
2.3 差值计算的标准数学定义
差值计算的本质很简单:设T0时刻读取累计值C0,T1时刻读取累计值C1,那么T0到T1时段内的用量 = C1 - C0。
日报表每小时差值,严格来说是“该小时最后一条累计值”减去“上一小时最后一条累计值”。这里用“最后一条”而不是“平均值”,更不是“整点值”,原因有两个:一是采样时刻不一定恰好落在整点,整点那条记录可能不存在;二是累计值在小时段内可能一直在涨,取该小时段最后一条最接近真实小时末状态。
如果用SQL实现,就是先按小时分组,再在每个小时内按时间倒序取第一条,之后用LAG窗口函数和上一小时的末值做差。具体的SQL写法在第4节展开。
3. 报表实现路线对比:在线表格控件、SQL直查、脚本导出,我各试过一遍
3.1 在线表格控件适合“看”历史,不适合“算”差值
WinCC自带的在线表格控件(Online Table Control)可以在画面里拖一个控件,配置归档变量和时间区间,自动生成历史数据表。优点是零开发,配置一下就能看历史值。
缺点是:它查出来的是原始采样序列,不能自动按小时分组,不能自动算差值,行和列的格式也不能完全自定义。如果硬要用,得先在脚本里把差值算好再塞进表格,那不如直接写SQL来得痛快。
所以我的结论是:这个控件适合操作员在线查历史值,比如“昨天下午三点那台设备压力是多少”;但不适合给生产部出日报表,日报表要的是格式化、可打印、带统计的产出物。
3.2 直接查归档库 + VBS脚本导出,是单项目交付最通用的组合
WinCC所有归档数据都存在自带的后台数据库里,具体来说是同机安装的SQL Server实例。用VBS脚本创建ADO连接,写SQL按小时分组算出差值,最后用Excel.Application导出文件,这套流程是工控行业最通用的做法。
这个组合的好处是:不需要额外购买报表授权,凡是装了WinCC的机器基本都能跑;代码量适中,客户要调整格式直接改脚本;交付时把脚本挂到按钮上,操作员点一下自动生成Excel,或者用定时器每天凌晨自动跑。
我曾经在一个水厂项目里用这套方案做了12张报表,从电耗到流量到药剂用量,全部是VBS+SQL+Excel。项目运行三年,报表功能基本没动过。
3.3 SSRS和C#小工具,适合沉淀成标准品
如果公司不是做一个项目,而是要做一套“WinCC报表包”重复卖给甲方,那建议用SQL Server Reporting Services(SSRS)或者C#写定时服务,周期抓取归档库数据出PDF/Excel。
SQL Server Reporting Services的好处是格式规范、权限管理成熟、支持定时邮件推送,集团型项目很喜欢。C#写报表服务的灵活性更高,可以嵌入你们自己的业务逻辑,比如自动计算班次达成率、自动发企业消息。
坏处也很明显:前期开发成本高,而且WinCC版本升级后归档库表结构可能有变化,要跟着改。所以这条路线只适合产品化,不适合单项目快速交付。
3.4 我的选型建议和适用场景
| 项目情况 | 推荐路线 | 理由 |
|---|---|---|
| 单项目、快速交付 | 全局脚本VBS+SQL导出Excel | 零授权成本,改格式方便 |
| 客户会自己维护报表 | 脚本+界面按钮,附使用说明 | 操作员一键导出,减少售后 |
| 多项目、产品化 | SSRS或C#报表服务 | 格式统一,支持批量部署 |
| 集团数采平台 | 数据库视图+标准报表平台 | 与上层平台对接方便 |
需要说明的是,WinCC版本从v7.x到v8.x,上面这些路线都成立。版本变化主要影响归档库内部结构,不影响“查归档库算差值”这个思路本身。
4. 核心实现:SQL窗口函数把“每小时末累计值”取出来,再用LAG算差值
4.1 归档数据怎么取:连接字符串和视图
WinCC归档数据查询一般通过ADO/ODBC走后台数据库。常用的连接字符串长这样:
Provider=SQLOLEDB;Data Source=.\WinCC;Initial Catalog=WINCC;Integrated Security=SSPI注意第四部分是重点:Data Source里的实例名要按实际安装情况修改,默认是.\WinCC,但有的项目装的是命名实例。Initial Catalog是数据库名,WinCC默认是WINCC。如果连接失败,先在数据库管理工具里确认实例名和库名。
WinCC归档数据在后台库里不是一张简单的平表,而是按时间段切分的归档段。查询时一般会接触到类似TAG:R,_xxx的视图或表。不同版本的表名有差异,我不建议读者把某个具体表名死记硬背,正确做法是:在数据库客户端里找到你们项目对应的归档视图,确认时间字段和值字段的名称,再往下写SQL。
下面所有SQL里的ArchiveView都是示意表名,落地时换成你们项目实际的归档视图名。
4.2 把时间戳切成小时桶,取每小时最后一条累计值
先写最核心的一步:把原始时间序列按小时分组,然后在每个小时内取最后一条累计值。
WITH raw_data AS ( SELECT SampleTime, Value FROM ArchiveView WHERE TagName = '电度' AND SampleTime >= '2024-12-01 00:00:00' AND SampleTime < '2024-12-02 00:00:00' ), hourly_last AS ( SELECT DATEADD(hour, DATEDIFF(hour, 0, SampleTime), 0) AS hour_start, SampleTime, Value, ROW_NUMBER() OVER ( PARTITION BY DATEADD(hour, DATEDIFF(hour, 0, SampleTime), 0) ORDER BY SampleTime DESC ) AS rn FROM raw_data ) SELECT hour_start, Value AS hour_end_value FROM hourly_last WHERE rn = 1 ORDER BY hour_start这段SQL做了三件事:第一,用DATEADD和DATEDIFF把任意时间戳归到它所在小时的起点,相当于把数据“切”成小时桶;第二,用ROW_NUMBER()在每个小时桶内按时间倒序编号,最新一条标号为1;第三,取rn=1的记录,就是每个小时最后一条累计值。
为什么用ROW_NUMBER()而不是直接MAX(Value)?因为累计值在小时段内可能发生过清零再上涨,MAX(Value)取到的可能不是时间上最后一条记录,而是清零前的高值,这样差值就错了。按时间倒序取最后一条才是严格正确的做法。
4.3 用LAG窗口函数做相邻小时差值
拿到每小时末值之后,下一步就是和上一小时末值做差。SQL里用LAG窗口函数最方便,它可以直接取前面一行的值:
WITH raw_data AS ( SELECT SampleTime, Value FROM ArchiveView WHERE TagName = '电度' AND SampleTime >= '2024-12-01 00:00:00' AND SampleTime < '2024-12-02 00:00:00' ), hourly_last AS ( SELECT DATEADD(hour, DATEDIFF(hour, 0, SampleTime), 0) AS hour_start, SampleTime, Value, ROW_NUMBER() OVER ( PARTITION BY DATEADD(hour, DATEDIFF(hour, 0, SampleTime), 0) ORDER BY SampleTime DESC ) AS rn FROM raw_data ), hourly_end AS ( SELECT hour_start, Value AS hour_end_value FROM hourly_last WHERE rn = 1 ), hourly_diff AS ( SELECT hour_start, hour_end_value, LAG(hour_end_value) OVER (ORDER BY hour_start) AS prev_hour_value FROM hourly_end ) SELECT hour_start, hour_end_value, ISNULL(hour_end_value - prev_hour_value, 0) AS hour_diff FROM hourly_diff ORDER BY hour_start这里的关键点是:LAG(hour_end_value) OVER (ORDER BY hour_start)会按小时顺序取出上一行的累计末值,然后当前小时末值减去上一小时末值,就是该小时的用量。
第一小时因为没有“上一小时末值”,LAG返回NULL,所以用ISNULL(..., 0)兜底,或者根据业务填成空值。如果第一小时从0点开始时累计量不是从0起,那第一小时的真实用量本来就是“1点末值减去0点前最后一个值”,需要额外补一个0点之前的历史点,这个后面排错部分专门讲。
4.4 日合计和扩展统计
一天的总用量,最简单的是把上面结果里的hour_diff加起来:
SELECT SUM(hour_diff) AS day_total FROM ( -- 上面那段 hour_diff 的完整子查询 ) AS t也可以直接用当天最后一个累计值减昨天最后一个累计值。两种算法理论上结果一致,实际以哪个稳定用哪个。我的经验是:如果归档数据完整,小时差值累加的结果更可靠,因为出现小时级别的复位时能被发现。
如果你还需要瞬时量的小时平均、最大、最小,可以在hourly桶上继续加聚合:
SELECT hour_start, AVG(Value) AS hour_avg, MAX(Value) AS hour_max, MIN(Value) AS hour_min, COUNT(Value) AS sample_count FROM raw_data GROUP BY DATEADD(hour, DATEDIFF(hour, 0, SampleTime), 0)这里的sample_count就是后面要说的“数据完整性”标记的数据来源——如果某个小时只有一条记录,那这个小时的差值可信度就要打问号。
4.5 VBS脚本落地:连库、取数、写Excel
SQL想好了,接下来就是把查询嵌进VBS脚本,跑出结果写到Excel。核心骨架如下:
Dim conn, rs, sql Set conn = CreateObject("ADODB.Connection") conn.ConnectionString = "Provider=SQLOLEDB;Data Source=.\WinCC;Initial Catalog=WINCC;Integrated Security=SSPI" conn.Open sql = "WITH raw_data AS (...)" ' 这里放前面完整SQL Set rs = conn.Execute(sql) Dim xlApp, xlBook, xlSheet Set xlApp = CreateObject("Excel.Application") xlApp.Visible = False Set xlBook = xlApp.Workbooks.Add() Set xlSheet = xlBook.Worksheets(1) ' 表头 xlSheet.Cells(1, 1).Value = "时间区间" xlSheet.Cells(1, 2).Value = "累计值差值" xlSheet.Cells(1, 3).Value = "小时末累计值" Dim rowIdx rowIdx = 2 Do While Not rs.EOF xlSheet.Cells(rowIdx, 1).Value = rs("hour_start") xlSheet.Cells(rowIdx, 2).Value = rs("hour_diff") xlSheet.Cells(rowIdx, 3).Value = rs("hour_end_value") rowIdx = rowIdx + 1 rs.MoveNext Loop rs.Close conn.Close xlBook.SaveAs "D:\Report\daily_report.xlsx" xlBook.Close xlApp.Quit Set xlSheet = Nothing Set xlBook = Nothing Set xlApp = Nothing这里提醒一句:Excel对象用完一定要释放,否则脚本跑几次之后,服务器上会残留一堆EXCEL.EXE进程,把内存吃光。我习惯在脚本里加一个错误处理:On Error Resume Next配合Err判断,在异常时先杀掉残留进程再退出。如果项目对稳定性要求高,建议用定时触发器在凌晨执行,生成前一天报表。
5. 归档配置的坑:归档周期、压缩归档和变量类型,是数据准不准的前提
5.1 归档周期决定差值可取的最小颗粒
归档周期是差值报表的第一前提。假设累计值从仪表过来,WinCC变量设成1小时归档一次,你再怎么写SQL也拿不到小时内的“最后一条”,因为整小时内可能只有一条记录,前一小时的最后一条和当前小时的唯一一条之间隔了整整一小时,差值算出来就是错的。
我建议:累计量归档周期设5到20秒,瞬时量可以更短。每小时记录数可以用3600除以周期估算,比如10秒周期每小时360条,做报表绰绰有余。如果再短到1秒,数据库膨胀速度会很快,对差值报表的精度提升却几乎没有,没必要。
归档周期和采集周期是两个独立设置,都在变量属性里。改完归档周期后,要重新激活一次项目,让归档配置生效。有些工程师改了参数发现没变化,就是忘了重新激活。
5.2 压缩归档会“吃掉”累计值的真实增量
这里重点说压缩归档。WinCC的压缩归档会把一段时间(比如1小时)内的原始数据合成一条记录,字段有平均值、最小值、最大值、最后值等。很多人图省事直接读压缩归档做报表,结果累计值差值完全对不上。
原因很简单:累计值是个积分量,它的“平均值”没有物理意义。假设这个小时前半段从1000涨到1100,后半段从1100涨到1200,平均值算出来可能是某个不存在的中间值,拿平均值相减当然不对。
如果实在要用压缩归档,一定要用“最后值”字段做差值,不要用平均值。但我的建议是把原始归档保留至少30天,报表全走原始数据,压缩归档只给WinCC趋势显示用。归档压缩的配置位置在WinCC归档管理里,压缩周期可以设成1小时,压缩字段选“最后值”给趋势用,平均值给瞬时量报表备用。
5.3 变量类型、溢出和数据复位
外部仪表的累计值类型要特别小心。很多电表走Modbus RTU,寄存器是32位DWORD,4个字节的顺序还有ABCD、CDAB之分。WinCC变量如果按有符号16位接收,数值超过32767就变成负数,报表里会出现莫名其妙的跳变。
正确做法是在通讯层把寄存器拼成32位无符号或浮点,在WinCC侧统一用浮点变量接收。这样还能避免累计值超过32位上限后溢出。溢出或清零都会导致差值变成负值,这个在第6节展开怎么判断。
另外,如果累计值来自PLC内部累加器,要注意PLC程序是32位还是64位累加,是否有自动清零逻辑。一些老项目的PLC累加器会在数值到达上限后自动滚动归零,如果没有提前沟通,报表里就会出现规律性的负差值。
5.4 跨天、班次和服务器时区
日报表按自然日切分是最常见需求,但工厂往往有班次概念,比如0到8点、8到16点、16到24点。SQL里小时分组默认按服务器本地时间,如果WinCC服务器时区设置不对,报表的起始小时会整体错位,跨天那行对不上交接班。
部署时把服务器时区固定为项目所在地时间,并且让报表时间范围和交接班表对齐。如果项目里有多个子公司跨时区,建议在报表脚本里把“报表本地时间”作为参数传入SQL,而不是直接依赖服务器默认时间。
有夏令时的地区,一年里有两天小时数会多一个或少一个,报表逻辑要提前考虑,否则会出现25点或23点这种行。国内项目基本没有这个问题,但如果做海外项目,这块一定要提前问清楚。
6. 排错实录:报表空白、负差值、查询卡顿的完整排查链路
6.1 报表全是空白,先分清“归档问题”还是“查询问题”
遇到报表空白,不要一头扎进SQL。我一般按这套顺序排查:
第一步,先用WinCC的历史趋势曲线控件拉同一个变量、同一个时间段的曲线。如果曲线有数,说明归档有数据,问题出在查询层;如果曲线也是空的,直接去变量归档属性里看是否勾选了“归档”,再去检查WinCC归档进程是否启动、磁盘空间是否足够。
第二步,如果曲线有数而报表空,检查SQL时间参数。很常见的坑是:SQL查询的时间范围用字符串拼接,中文系统日期格式是yyyy/m/d,而SQL Server往往按yyyy-mm-dd或yyyymmdd解析,一旦格式不匹配就查不到数据。建议在脚本里统一用FormatDateTime或直接拼成ISO格式。
第三步,检查TagName匹配。WinCC归档的变量名大小写、前后缀可能和画面变量不完全一致,有的是内部名称、有的是外部名称,查询语句里写错一个字符就查不到。在数据库客户端里先SELECT DISTINCT TagName看看实际存的名称。
6.2 负差值和跳变:识别“清零复位”而不是“数据异常”
我碰到过一个真实案例:某车间电度表每天凌晨会通讯闪断,恢复后仪表清零重启,结果那个小时的差值出现负几百,紧接着下一个小时又突然正几百,日合计凭空少了一段。
处理思路是:当相邻差值小于0时,不要简单把这个负数写进报表,先判断是不是一次复位。如果复位点对应仪表重启,那么复位后那一小时的用量应该是“本小时末值减去0”,也就是说,该小时用量直接等于本小时末值,因为仪表是从0重新开始累计的。
如果负值很小,可能是计量单位切换、乘法因子变了或者现场仪表重新拨码。这些情况要拿到仪表说明书和通讯配置确认,不要自己在报表里瞎改。SQL里可以加一列标记:
CASE WHEN hour_diff < 0 THEN '复位/异常' ELSE '正常' END AS data_status然后把“复位/异常”的标记行保留在报表里,让甲方去现场确认。这个标记在项目验收时可以省掉大量扯皮。
6.3 查询越来越慢:跨归档段和索引失效
WinCC把归档数据按时间切段存放,一个查询跨多个归档段时,SQL如果写得不好就会特别慢。常见错误是在WHERE条件里对时间字段做函数运算,比如WHERE DATEPART(hour, SampleTime) = 8,这会让索引直接失效。
正确做法是把时间范围写成区间比较:
WHERE SampleTime >= '2024-12-01 00:00:00' AND SampleTime < '2024-12-02 00:00:00'并且单次查询跨度控制在31天以内,更长的数据按月拆分。如果VBS执行SQL时超时,可以在连接字符串里加Connect Timeout=30;如果还是很慢,优先检查是不是把归档视图全表扫了。
另外,报表并发也会拖慢查询。多台操作站同时点导出按钮时,数据库压力会很大。我的做法是在脚本入口加一个互斥标记:用一个网络文件夹或数据库表记录“正在生成中”,第二个请求直接弹窗提示稍后再试。
6.4 KepServerEX转发数据的质量戳和数据类型
很多累计值不是PLC直接给WinCC,而是通过KepServerEX从电表、流量计转发过来。OPC通讯里有个质量戳,Bad质量时KepServerEX通常会把上一次的好值保持不变继续往WinCC推,报表里就出现一段平线,差值变成0,通讯恢复后差值突然变大。
所以做报表前先确认OPC链路的质量处理策略。最好在转发侧把Bad质量置为无效,或者让WinCC侧对连续多次不变的时间戳做标记。SQL里可以查每个小时段的最后一条和第一条是否完全相等,如果相等且持续整个小时,就加一个“疑似通讯保持值”的标记,提醒人工复核。
另一个点:Modbus仪表里的DWORD如果按INT解析会出负值。比如一块电表累计到30000 kWh时就超过16位有符号整数上限,如果通讯层配置错误,WinCC收到的值会突然变成负数。转发配置里一定要把数据类型配成Long/ULong,WinCC侧用浮点接收,中间再用脚本做一次范围检查。
7. 把整套东西落地后,我有几点实际操作心得
7.1 差值运算尽量下沉到SQL,别在VBS里逐行循环算
我最开始做差值计算时是在VBS里逐行循环:先取所有原始点,再按小时遍历、相减。变量少还行,几十个变量查一个月数据就卡爆了,报表生成一次要半小时。
后来把逻辑全部下沉到SQL窗口函数,一条SQL完成分组、取末值、差值、合计,报表从半小时变成几秒。这个经验直接决定了后面所有报表的写法:凡是能在SQL里算的,绝不在脚本里循环算。VBS只负责连数据库、执行SQL、填Excel,越“笨”越好。
7.2 报表交付前,用历史趋势曲线抽查三天数据
报表做出来先别急着交付,拿历史趋势曲线抽查前三天数据。我一般会挑几个典型时段:用电尖峰、交接班附近、跨零点那一小时。把趋势控件的曲线和日报表并排放,比较对应时段差值是否一致。
这个动作能发现很多“逻辑对但数据不对”的隐藏问题,比如某个变量根本没有归档、仪表通讯一直断、时间分组边界错位等。等甲方自己在报表里发现数据不对,再被叫去现场排查,成本完全不一样。
7.3 在报表里加“数据完整性”标记,能帮你省掉大量答疑
每张报表我都建议加一列“数据完整性”。SQL查询每个小时段的记录数,如果记录数明显少于预期,比如每小时少于60个采样点,就在报表里标注“数据稀疏”。 这个标记的好处是,甲方看到异常数据时,你可以快速判断是生产原因还是采集链路问题,不用每次都被拉去现场。很多情况下,“那小时产量对不上”其实是当小时通讯中断导致的采集缺失,加一个标记就解释清楚了。
7.4 WinCC版本迁移时,报表脚本怎么减少改动
WinCC从v7.x到v8.x,归档库结构有变化。我迁过一个v7.3SE项目到v8.1,报表脚本里的SQL表名、视图名基本都要重配,但窗口函数、Excel导出框架、定时触发逻辑全部可以复用。
所以新项目如果预见到以后要升级,尽量把“归档查询”封装成一个独立的函数或存储过程,表结构变化时只改这一层,报表界面和导出逻辑不用动。我自己在v7.x项目里就是先把所有查询语句集中到一个VBS函数库文件里,后来升级时只改了一个文件,画面和导出逻辑几乎没碰。这个习惯在项目维护阶段省下的时间,远比当初多写几行代码多。