ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WinCC历史数据写入SQL Server:脚本方案与ODBC配置实战指南

WinCC历史数据写入SQL Server:脚本方案与ODBC配置实战指南 这段时间一直在用西门子博图和WinCC做上位机被同行问得最多的不是画面怎么做而是历史数据存储方案怎么落地。好多人折腾到一半卡就卡在“实时变量怎么怼进SQL数据库”这一步。借着这个机会我把整个流程从头到尾盘一遍包括建库、配ODBC、写脚本、设触发器以及那些常规文档里不会写、实操里特别容易踩的坑。先说明一下这篇文章的适用人群目前在用博图WinCC做项目现场有历史曲线、报表或者MES对接需求手上攒了一堆变量想把它们按自己的规则存到数据库里。不管你是刚入行的自动化工程师还是接了运维改造项目的老师傅这套思路都适用。下面讲的方案不依赖WinCC自带归档的界面直接用脚本把变量写进SQL Server灵活度和可控性都会高很多。1. 先把需求捋清楚为什么要往SQL里怼数据1.1 WinCC自带归档不是不能用是难用很多人一上来就问“WinCC不是自带日志归档吗为什么还要自己写SQL”。确实WinCC自带的数据归档能存曲线、报警查询也能做但真到项目交付的时候你会发现它有几个磨人的地方。第一查询和分析的面板都在WinCC工程里甲方想看报表你得给人家做画面、做按钮第二MES、ERP或者第三方系统想直接取数WinCC这套归档库结构不开放、表也复杂IT部门看着就头大第三WinCC的归档数据一般跟着项目走设备或者工位多了以后维护起来很麻烦。所以在中小型项目里自己建一张清清楚楚的历史数据表用标准SQL查询所有数据都在自己的掌控之下后续接报表、接大数据平台都非常直接。这就是为什么我建议走“自定义SQL库”这条路。1.2 文件、时序库、关系库到底怎么选除了SQL Server市面上可选的存储方案还有CSV文件、MySQL、达梦、时序数据库之类的。我把几个主流方案的取舍列一下方案优点缺点适合场景CSV/Excel文件简单、零成本数据一多就卡断点续写困难检索靠翻文件变量少、临时记录SQL Server/MySQL标准化SQLIT运维成熟问题好查需要数据库基础高频写入要调优绝大多数工控项目达梦/国产数据库满足自主可控要求SQL语法大体兼容驱动、案例相对少有国产化要求的项目时序数据库InfluxDB等写入性能强、压缩好工控现场部署门槛高团队学习成本大海量点位的长期趋势分析如果你只是给一台设备做趋势记录CSV凑合能用。但要对接业务系统、做多变量历史查询我的建议就是直接上SQL Server。WinCC项目最常见的就是SQL Server一方面WinCC自身也用它做存档库另一方面IT部门普遍熟悉出问题不愁没人接盘。1.3 整体架构一句话说清整个方案不神秘WinCC运行系统启动后通过全局脚本按固定周期读取实时变量把变量名、时间戳、数值、质量码组装成INSERT语句依靠ODBC或ADO连接写入指定的SQL Server数据库表。外加一个看门狗逻辑保证脚本自己死了能在画面上报警。这套架构里WinCC只负责“采集和推送”SQL库负责“存储和查询”两边解耦。比WinCC自带归档强的地方在于表结构是自定义的脚本还能做死区过滤、断线缓存、批量写入这些都是自带归档给不了的。2. 建库、建表、配ODBC动手前先把地基打牢2.1 数据表设计是第一个分水岭表结构设计得好不好直接决定后面查询顺不顺。我见过有人把所有变量一股脑塞进一个文本字段也能用但做大以后查询性能和类型判断都是灾难。我的建议是至少包含以下字段CREATE DATABASE HistoryDB; GO USE HistoryDB; GO CREATE TABLE dbo.TagHistory ( ID INT IDENTITY(1,1) PRIMARY KEY, TagName NVARCHAR(50) NOT NULL, TimeStamp DATETIME NOT NULL, Value NVARCHAR(50) NOT NULL, Quality INT NULL ); GO CREATE INDEX IX_TagHistory_TagName_TimeStamp ON dbo.TagHistory(TagName, TimeStamp); GO这个表里TagName存变量名TimeStamp存触发时间Value存数值Quality存质量码用于区分正常值、坏值、手动值。这样设计的好处是无论开关量、模拟量还是字符串都能统一塞进去查询时用TagName过滤、按TimeStamp排序。如果全项目都是模拟量也可以把Value字段设为FLOAT甚至把布尔量单独拆一列。但混合类型的项目统一用NVARCHAR是最稳的。要注意的是用NVARCHAR存数字做排序和聚合时要显式转换SQL语句里写CAST(Value AS FLOAT)别偷懒。一张大表还是按变量分表我的经验是变量少于500个一张大表加索引就够。变量上千、数据量一天数百万条建议按变量分组做表分区或者按月建表。初期千万别过度设计后续再拆表也不迟。2.2 SQL Server账号、认证、协议先配齐数据库装好以后别直接用sa上去怼。生产环境要给WinCC创建一个专用账号比如USE master; GO CREATE LOGIN HisUser WITH PASSWORD 你的强密码; GO USE HistoryDB; GO CREATE USER HisUser FOR LOGIN HisUser; GO ALTER ROLE db_datareader ADD MEMBER HisUser; GO ALTER ROLE db_datawriter ADD MEMBER HisUser; GO然后检查几件事。一是SQL Server的认证模式。右键实例选择“属性”里的“安全性”确认启用“SQL Server和Windows身份验证模式”。如果只开Windows认证后续从WinCC脚本用SQL账号连会出现登录失败。二是TCP/IP协议。在SQL Server配置管理器里把实例的TCP/IP协议启用并重启SQL服务。客户端连接时走1433端口如果SQL Server是远程服务器Windows防火墙必须放行1433。三是用账号连通性测试。在客户端电脑上用sqlcmd或者Navicat连一次确保账号、密码、端口、网络都没问题。测试通了再写WinCC脚本否则后面排查范围会非常广。2.3 ODBC数据源的32位/64位坑新手必看WinCC的脚本进程绝大多数是32位的所以配置ODBC数据源时要用32位管理工具。很多人直接在控制面板里打开“ODBC数据源(64位)”配完之后脚本报“找不到数据源”反过来怀疑代码写错。正确做法是打开C:\Windows\SysWOW64\odbcad32.exe在“系统DSN”里添加一个指向HistoryDB的数据源驱动选择“ODBC Driver 17 for SQL Server”或“SQL Server Native Client 11.0”。驱动名称要看SQL Server版本没装对应驱动就去装一个常见问题大多是驱动缺失或者位数不对。配置DSN的时候服务器地址如果是本机写.\SQLEXPRESS或localhost都行如果是远程写IP地址。选好数据库勾选“使用SQL Server身份验证”填入刚才建好的账号测试连通性。到这里数据库侧的地基就算打完了。3. WinCC脚本把变量写进SQL手把手代码实现3.1 脚本放在哪、怎么触发在WinCC项目树里找到“全局脚本”新建一个函数。这个函数可以用C脚本也可以用VBScript。我个人推荐优先用VBScript因为ADO操作数据库代码更短后面的维护人员也更容易接手。函数写完后在全局脚本下面建一个触发器指定周期执行。触发器的时间周期从500毫秒到几秒都能设但实际项目中我建议从头就按5秒来稳妥、数据量可控。你说要按秒级实时性那先算算点位数和数据库负载别把SQL库压垮后面第5章会细讲。需要特别提醒的是博图WinCC的Runtime Advanced对脚本支持有限这套方法最好在WinCC Professional或者经典WinCC里用。如果工程里只有Runtime Advanced脚本能力可能会受到版本限制做之前先查一下项目类型。3.2 VBScript快速上手的写法下面这段代码就是最经典的“读变量插数据库”流程Dim conn Dim sql Dim fValue Dim timestamp 组装标准时间字符串 yyyy-mm-dd hh:nn:ss避免本地化格式问题 timestamp Year(Now) - Right(0 Month(Now), 2) - Right(0 Day(Now), 2) Right(0 Hour(Now), 2) : Right(0 Minute(Now), 2) : Right(0 Second(Now), 2) 读取WinCC变量 fValue HMIRuntime.Tags(AI_Pressure).Read 连接SQL Server Set conn CreateObject(ADODB.Connection) conn.ConnectionString ProviderSQLNCLI11;Data Source.\SQLEXPRESS;Initial CatalogHistoryDB;User IDHisUser;Password你的密码; conn.Open 执行插入 sql INSERT INTO dbo.TagHistory (TagName, TimeStamp, Value, Quality) VALUES (AI_Pressure, timestamp , fValue , 0) conn.Execute sql conn.Close Set conn Nothing这段代码有几个点需要注意。第一时间戳我用了手写拼接而不是FormatDateTime(Now, 0)。因为WinCC运行系统的区域设置五花八门可能出现“上午/下午”这种本地化字符串SQL Server解释不了直接一条坏数据。自己拼接yyyy-mm-dd hh:nn:ss最保险。第二布尔量在WinCC里读取出来可能是TRUE/FALSE往SQL里插会报错。要先用If fValue True Then fValue 1 Else fValue 0转换一下。整数和实数一般没事但建议都转成字符串再拼SQL。第三这段代码每次触发都会新建连接、关闭连接测试期没问题长期跑不太合适。后面我会讲怎么优化成保持连接、批量写入。3.3 C脚本用ODBC的老派稳定打法如果你所在的项目之前一直用C脚本网上流传的ODBC API写法也可以照搬。核心思路是通过#pragma code引入odbc32然后申请环境句柄、连接句柄、语句句柄最后用SQLExecDirect执行INSERT。思路虽然繁琐但是非常稳定我在几个跑了好多年的老项目里见过这种代码几乎没出过问题。#pragma code(kernel32.dll) #include windows.h #pragma code() #pragma code(odbc32.dll) #include sql.h #include sqlext.h #pragma code() static SQLHENV hEnv; static SQLHDBC hDbc; void InitDB(void) { SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, hEnv); SQLSetEnvAttr(hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); SQLAllocHandle(SQL_HANDLE_DBC, hEnv, hDbc); SQLDriverConnect(hDbc, NULL, DSNHistoryDSN;UidHisUser;Pwd你的密码;, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE); }这段只是示意真要落地还要处理错误和释放句柄。我的建议是除非项目强制或者你特别熟C否则VBScript已经够用了。新项目维护的人不一定懂CVBS至少看起来更直观。3.4 触发器绑定和死区过滤触发器绑定之后函数会按周期执行。如果每个周期都无条件插一条一天下来数据量很吓人一个变量1秒插一条一天就是86400条10个变量80万条。更合理的方式是“变化率死区”。思路是这样脚本内部用静态变量记住上次的值本次读取后做差超过设定死区才插库。Static lastValue Dim fValue fValue HMIRuntime.Tags(AI_Pressure).Read If Abs(fValue - lastValue) 0.5 Then 执行插入 ... lastValue fValue End If如果开关量只记录状态变化就行没必要每个周期都插。这样数据量能降一个数量级SQL库的负载也轻很多。要小心的是第一次运行时lastValue为空要做好初始化否则第一次插入可能被误跳。4. 现场最容易踩的坑我帮你一个个排过雷4.1 “脚本语句未结束”和各种语法误报搜索热点里总有人问“WinCC脚本语句未结束”这个问题十有八九是语法细节。C脚本漏了分号、括号不匹配、中英文标点混用都会报这种错。VBS脚本最常见的是字符串没闭合少写一个双引号行尾就显示“语句未结束”。我的排查套路是把脚本从WinCC里复制到Notepad或者VS Code先统一用英文标点刷新一遍。VBS字符串引用用双引号SQL语句里字符串两端也是双引号转义关系很容易乱。先用独立编辑器看语法高亮再粘回去编译基本能解决八成问题。4.2 数据库连接握手失败与登录失败“握手错误”这个词在现场出现频率很高。我遇到过最常见的一种场景SQL Server实例只启用了Named Pipes没启用TCP/IP而ODBC连接默认走TCP两边协议对不上报握手失败。解决方法是打开SQL Server配置管理器把TCP/IP启用并重启服务然后客户端重连。另一种是登录失败。如果你用的SQL账号报“Login failed for user”先确认三件事认证模式是不是混合认证账号密码是否真的正确账号是否有权访问HistoryDB。生产环境账号密码建议单独建别拿sa到处试。还有一类隐藏问题WinCC运行系统如果是作为Windows服务运行的服务账户可能没有权限访问SQL Server。这种情况建议让WinCC运行系统以“本地系统”账户运行或者给服务账户也赋予数据库登录权限。4.3 SQL Server版本不兼容与“正在装载”问题网上最热门的问题之一是“SQL Server 2012的数据库备份2008能用吗”答案是不能用。高版本备份无法直接附加到低版本实例反过来倒是没问题。现场经常有人把2012的备份文件拷到2008机器上附加结果得到“无法打开备份文件”或者数据库一直显示“正在装载”。应对这类问题一是从源头统一SQL Server版本二是必须跨版本迁移时用“生成脚本数据导出”的方式迁移表结构再通过导入导出工具把数据搬过去。千万不要硬附加。“正在装载”状态本身通常是数据库文件路径冲突或者实例重启未完成。先用RESTORE DATABASE HistoryDB WITH RECOVERY尝试恢复还不行就把库脱机、检查日志文件路径实在不行就删了重建再重新恢复。4.4 授权问题WinCC 8.1、TIA Step7 Pro V15.1整条链路都正常脚本却不执行先别急着怀疑代码看看授权。WinCC运行系统授权缺失或过期项目能不能启动都是问题更别提跑脚本了。博图WinCC V15.1、WinCC 8.1这些版本授权管理都靠Automation License Manager一定要在实际运行环境中检查授权状态。授权这事还要提一句项目现场必须有合规授权这也是工程交付里的基本盘别等调试到最后一步才发现授权不完整那是真耽误事。4.5 数据写不进去但也没报错这类问题最磨人。脚本明明没报错数据库就是没数据或者有数据但全是旧值。我的排查顺序是这样的先打开SQL Server Profiler抓一下有没有来自WinCC的T-SQL语句到达数据库。如果Profiler里根本看不到INSERT说明脚本环节没执行去看触发器周期和运行系统状态。如果Profiler能看到INSERT但表里没数据说明语句自身有问题比如插到了别的库、别的表或者表字段约束把数据挡掉了。再一个隐藏原因变量读取质量不好读出来是坏值。有些时候WinCC变量与PLC通信中断读取结果是一个空字符串或者“BAD”标识脚本还是把它插进库了这就是为什么表里要留Quality字段后期查数的时候能一眼筛掉坏值。4.6 数据进库后的展示问题数据进了SQLWinCC画面里怎么看两个方向如果只是想看趋势WinCC自身的趋势控件加归档变量更方便但如果想在画面上展示SQL库里的历史数据就得用OLEDB查询再绑定表格工作量不小。这也是很多项目选择“SQL负责报表WinCC负责实时画面”的原因。顺带提一句组显示控件在WinCC里主要用来分组显示设备状态、趋势图和报警直接绑定SQL数据源不是它的强项。别指望SQL库能和组显示控件无缝对接各干各的最省心。5. 从脚本级到工程级性能、清理和运维建议5.1 别每秒都插学会批量写很多工程都是几十上百个变量一起入库存如果每个变量、每个周期各开一个连接SQL Server会率先崩溃。标准做法是把多次INSERT合并到一个连接、一次提交。最简单的办法是在脚本里做字符串累积每次触发时把一两百条INSERT语句拼成一个变量攒到一定量或者固定时间再conn.Execute一次。比如每5分钟批量写入一次数据实时性会差一点但数据库压力大幅下降。连接资源也不要每次开闭。可以做一个全局连接对象WinCC运行系统启动时初始化每次周期只重用这个连接。唯一要注意的是SQL Server空闲回收有时会断开老化连接脚本里要有重连逻辑捕获到连接错误后重试一次。5.2 数据清理与分区归档历史表只增不减是运维大忌。我建议用SQL Agent建立定时作业把超过设定保留周期的数据删除或迁移。保留周期怎么定一般经验是秒级数据保留3个月分钟级数据保留1年超出的可以离线归档到数据库备份文件或者同步到数据仓库做长期存留。如果数据量巨大还可以按月份做分区表。这样删除老数据时直接切分区即可不用每秒执行大范围DELETE。索引维护也很关键每周或每月重建一次IX_TagHistory_TagName_TimeStamp索引避免表越跑越慢。5.3 怎么知道脚本还“活着”在SQL库里建一张系统心跳表脚本每次执行都更新一条最新时间戳CREATE TABLE dbo.SystemHeartbeat ( SystemName NVARCHAR(50) PRIMARY KEY, LastTime DATETIME );每隔一段时间插一条或更新一条上位机画面或者云平台通过查询这张表一旦LastTime超过设定阈值立刻报警“历史存储脚本停止”。这类监控逻辑成本低、作用大千万别省。如果SQL Server宕机导致一段时间数据没写进去最简单的缓存方案是在脚本里用内存变量保存最近一批数据等服务恢复后补插。更稳妥的是先落本地文件再回补但是工程复杂度会上去中小项目我建议先做心跳监控回补策略按业务重要性再慢慢加。最后说一个我自己的习惯每到一个新项目我不会直接开写WinCC脚本而是先用数据库工具建好表、配好ODBC再单独写一个最小化的VBS脚本测试插入。等这一条路跑通再往大脚本里加变量读取、触发器、批量提交。分段验证的好处是出问题能立刻定位数据库的归数据库脚本的归脚本。这个流程帮我省了不少半夜调试的时间你也可以试试。
返回列表