多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

WinCC归档数据读取与数据库同步:OLE DB Provider实战与增量同步方案

WinCC归档数据读取与数据库同步:OLE DB Provider实战与增量同步方案 简介这份资源面向工业自动化工程师与WinCC初学者聚焦西门子WinCC监控系统的报警、变量数据读取与归档操作帮助解决从WinCC工程数据库中提取历史数据、分析报警事件的实际问题。压缩包共41个文件约84KB以C#源码17个cs文件为核心配合5个resx与4个resources资源文件、3个可执行程序及config配置、csproj工程文件构成一套可直接编译运行的WinCC数据读取示例工程。目前已有382人学习下载。工程围绕报警日志、变量记录与用户归档三大模块展开涵盖数据库连接、SQL查询构建、结果集处理与数据导出等环节读者可据此理解WinCC归档数据的访问机制掌握通过编程接口读取报警与变量历史值的方法并借鉴其窗体与工具类的组织方式快速搭建自己的数据采集与分析程序。1. 从一份 ReadWinCCData_1.rar 说起WinCC 归档数据到底怎么落到数据库里现场调试过 WinCC 的人多半遇到过这个场景画面上的趋势曲线跑得好好的可一旦要拿历史数据做报表、做能耗分析、或者对接 MES就发现归档数据像锁在黑匣子里——看得见导不出。这份 ReadWinCCData_1.rar 就是冲着这个痛点来的它是一套读取 WinCC 归档数据并写入数据库的工程源码核心解决的是「WinCC 归档数据怎么被外部程序稳定读出来、再落到关系型数据库」这件事。适合两类人一是做 SCADA 上位机、需要把 WinCC 历史数据二次利用的自动化工程师二是接手了别人 WinCC 工程、要补数据接口的运维和集成人员。它不解决画面组态只解决数据出口。2. 先搞懂 WinCC 归档数据的存储结构为什么不能直接读数据库2.1 归档不是一张表而是分段压缩的时序块很多人第一反应是WinCC 归档数据不就在 SQL Server 里吗直接连库查表不就行了。真去翻过的人都知道WinCC 的归档Tag Logging在 SQL Server 里落成的是一堆以Archive开头的分段表加上TLG_F之类的元数据表字段是二进制压缩过的时间戳和值不是一一对应的行。你直接SELECT * FROM出来的东西人眼根本读不懂。这就是为什么必须走 WinCC 提供的接口而不是硬啃数据库。常见做法是两条路一条是 WinCC 自带的连通性包Connectivity Pack通过 OLE DB 或 WinCC OLE DB Provider 去查归档另一条是走 WinCC 的脚本或 ODKOpen Development Kit。这份工程走的是前者用 OLE DB 把归档当数据源查查出来的结果再写进目标数据库。选它的理由是不用停 WinCC 运行、不用改归档组态、对历史数据是只读的风险最低。2.2 连通性包和 OLE DB Provider 的对应关系要跑通这套东西机器上得先有 WinCC Connectivity Pack它装完会注册一个WinCCOLEDBProvider。连接字符串里指定归档所在的服务器和归档名查询用 WinCC 自己的 SQL 方言基于 ANSI SQL 扩展。这里有个容易翻车的点Provider 的版本必须和 WinCC 版本对齐WinCC 7.4 配的 Provider 拿去连 7.5 的归档十有八九报「找不到归档」。所以第一步不是写代码是确认版本。组件作用版本对齐要求WinCC Connectivity Pack提供归档访问接口与 WinCC 主版本一致WinCC OLE DB Provider实际执行归档查询随 Connectivity Pack 安装目标数据库存读取结果SQL Server / MySQL 均可归档服务器数据来源需开放对应访问权限2.3 读取流程拆成四步整个链路我一般拆成四步走这样出问题好定位第一步确认归档名和变量名在 WinCC 里用变量记录编辑器能看到第二步拼连接字符串连上 Provider第三步写查询语句按时间范围拉数据第四步把结果集映射到目标库的表结构里写入。这四步任何一步错现象都不一样后面避坑章节会细说。3. 把归档读出来连接字符串、查询语句与结果映射3.1 连接字符串怎么拼连接字符串是这套工程的第一道门槛拼错了连报错都看不懂。典型写法如下注意Catalog指向的是归档所在的数据库实例Data Source是 WinCC 服务器名// WinCC OLE DB Provider 连接字符串示例 string connStr ProviderWinCCOLEDBProvider.1; // Provider 名称版本随 WinCC 变 CatalogCC_MyProject_20240101_000000R; // 归档运行库名在 WinCC 里查 Data SourceWINCC-SERVER; // WinCC 服务器计算机名 Initial CatalogCC_MyProject; // 项目归档库 Integrated SecuritySSPI;; // 用 Windows 集成认证别用明文账号逻辑说明Provider决定用哪个驱动写错直接抛「未注册的提供程序」Catalog是归档运行库名格式是CC_项目名_日期_序号R这个值每个项目都不一样必须去 WinCC 的归档组态里核对Integrated SecuritySSPI表示用当前 Windows 账号认证比在字符串里写账号密码安全也少一个出错点。参数上唯一能改的是Data Source换成你实际的服务器名或 IP。3.2 查询语句的写法与时间范围WinCC 的查询语法和标准 SQL 有差异时间字段要用它自己的函数处理。下面这条是拉某个变量在指定时间段内的归档值-- 查询指定变量在时间范围内的归档值 SELECT ValueID, -- 变量在归档中的内部编号 TimeStamp, -- 归档时间戳 RealValue, -- 实际工程值 QualityCode -- 质量码判断数据是否有效 FROM Archive WHERE TimeStamp 2024-01-01 00:00:00 AND TimeStamp 2024-01-01 08:00:00 AND ValueID 12 -- 对应具体变量需先在变量表里查到 ORDER BY TimeStamp ASC;逻辑说明ValueID不是变量名是归档内部编号得先在 WinCC 变量记录里查到变量对应的 ID写错就查不到数据还不报错这是最阴的坑QualityCode一定要带上质量码非 0 的数据在后续分析里要过滤掉否则报表会出现莫名其妙的跳变时间范围建议按班次或小时切一次拉太大会让 Provider 超时。3.3 结果集写入目标数据库读出来之后要落到目标库常见做法是建一张结构对齐的表然后批量插入。下面用参数化插入避免拼接 SQL// 把归档结果批量写入目标数据库 using (var target new SqlConnection(targetConnStr)) { target.Open(); using (var cmd new SqlCommand( INSERT INTO TagHistory (TagId, SampleTime, Value, Quality) VALUES (id, time, val, qc), target)) { cmd.Parameters.Add(id, SqlDbType.Int); cmd.Parameters.Add(time, SqlDbType.DateTime); cmd.Parameters.Add(val, SqlDbType.Float); cmd.Parameters.Add(qc, SqlDbType.Int); foreach (var row in archiveRows) // archiveRows 是上一步读出的集合 { cmd.Parameters[id].Value row.ValueID; cmd.Parameters[time].Value row.TimeStamp; cmd.Parameters[val].Value row.RealValue; cmd.Parameters[qc].Value row.QualityCode; cmd.ExecuteNonQuery(); } } }逻辑说明用参数化而不是字符串拼接一是防注入二是时间字段的格式转换交给驱动处理省得自己格式化踩时区的坑批量插入时如果数据量大建议改成SqlBulkCopy逐条ExecuteNonQuery在几万条以上会明显变慢。参数上qc存质量码后续查询时用WHERE Quality 0过滤无效点。4. 避坑与排查归档读取最常见的五类翻车4.1 现象连接报「未找到提供程序」原因机器上没装 Connectivity Pack或者装了但版本和 WinCC 对不上Provider 没注册成功。解决先在「ODBC 数据源管理器」的「提供程序」页里确认WinCCOLEDBProvider.1在不在不在就重装对应版本的 Connectivity Pack装完重启一次服务。4.2 现象查询返回空结果但归档里明明有数据原因ValueID写错了或者Catalog指向了错误的归档运行库。解决去 WinCC 变量记录里逐个核对变量对应的 ValueID别凭记忆写Catalog用归档组态里显示的那个完整名字注意结尾的R不能漏。4.3 现象数据能读出来但时间戳整体偏移几小时原因WinCC 服务器和读取程序的时区设置不一致或者归档本身存的是 UTC。解决统一两边时区读取后在程序里做一次显式转换别指望驱动自动处理转换逻辑写死在一个函数里别散落在各处。4.4 现象大批量读取时程序卡死或超时原因一次查询的时间跨度太大Provider 在服务端做压缩解压数据量一上来就顶不住。解决按小时或按班次分片查询每片读完就写库释放内存如果还慢把查询放到独立线程别阻塞主界面。4.5 现象写入目标库时偶发主键冲突原因重复读取了同一时间段或者程序重启后没记录上次读到的位置。解决在目标库上对(TagId, SampleTime)建唯一索引插入用MERGE或先查后插更稳的做法是维护一张断点表记录每个变量最后读取的时间戳重启后从断点续读。提示调试阶段先把时间范围缩到 10 分钟确认链路通了再放大别一上来就拉一个月的数据出问题根本没法定位。5. 进阶把归档读取做成可复用的增量同步5.1 用断点表实现增量读取一次性全量读取只适合首次初始化日常运行必须做增量。思路很简单为每个变量维护一条断点记录每次只读「上次时间戳到现在」这一段。下面这张表就是断点表的结构字段类型说明TagIdint变量内部编号LastTimedatetime上次成功读取到的时间戳UpdateTimedatetime断点更新时间Statusint0 正常1 异常待重读每次读取前先查LastTime查询语句的起始时间就用它读完写库成功后再更新断点。这样即使程序中途挂了重启后也能从断点继续不会漏数据也不会重复。5.2 异常重读与质量码过滤归档读取偶尔会因为网络或服务重启失败这时候别直接跳过把Status置 1下一轮优先重读这段。质量码过滤放在写库前做QualityCode ! 0的点标记出来但不丢弃单独存一张异常表方便后面排查是采集问题还是通信问题。我一般会在断点表上加一个重试次数字段超过三次就告警避免死循环。5.3 一个我踩过的坑别在 WinCC 服务器上跑读取程序早期图省事把读取程序直接部署在 WinCC 服务器上结果程序一占资源画面刷新都变卡被现场投诉。后来改成在独立的采集机上跑通过局域网连 ProviderWinCC 服务器只负责归档压力小了很多。从那以后我每次部署这类程序都强制先确认它和 WinCC 服务器是不是同一台机器是的话一律拆开。读取频率也别设太密归档本身有采集周期读得比采集还快纯属浪费。5.4 验证读取是否正确的笨办法别信程序日志信数据。拿一个你知道变化规律的变量比如每小时整点跳一次的产量计数读出来跟画面对一遍时间戳和值都对得上才算链路通了。再挑一段有停机的时间看质量码是不是按预期变化。这套笨办法我每次都走一遍比看任何日志都靠谱。希望帮到你。本文还有配套的精品资源点击获取
返回列表