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

文章详情

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

博图WinCC V16实战:用ADODB将SQL Server数据绑定到DataGrid

博图WinCC V16实战:用ADODB将SQL Server数据绑定到DataGrid 简介这份文档面向工业自动化领域的博图WinCC V16使用者尤其是需要在HMI画面上实时展示SQL Server数据的工程师与调试人员。内容围绕ADODB组件与DataGrid控件的配合展开给出可直接参考的VB脚本示例解决WinCC与数据库交互时数据无法呈现的常见困扰。资源包为单个docx文件约32KB结构紧凑便于快速查阅与移植代码。目前已有3693人学习下载说明该方案在同类场景中具有较高参考价值。读者可从中获取完整的连接字符串配置思路、Recordset游标设置方法、DataGrid数据源绑定与刷新逻辑以及连接关闭与对象释放的注意事项同时能了解表名、字段名和DSN等参数需按实际数据库调整的排错方向适合作为WinCC数据库显示功能的入门与调试参考。1. 博图WinCC V16里用ADODB把SQL Server数据拉进DataGrid一条被低估的产线数据链路产线上位机跑着博图WinCC V16画面里要实时显示一批来自SQL Server的工艺参数、批次记录或报警归档很多人第一反应是走WinCC自带的归档控件或者连通性包。但真到现场你会发现归档控件对自定义SQL查询的支持很别扭连通性包又要额外授权和配置。这时候一条更直接的路子就浮出来了在WinCC的VBS脚本里用ADODB组件连SQL Server把结果集塞进DataGrid控件显示。这套组合不依赖额外选件WinCC V16自带的VBS引擎就能跑适合做批次报表、参数看板、历史追溯这类“查询结果展示”场景。它不适合高频刷新的实时曲线但对秒级、分钟级刷新的表格数据稳定性和开发效率都够用。下面把我实际落地这套方案的过程拆开讲包括连接串怎么写、DataGrid怎么绑、哪些参数一改就翻车。2. ADODB与DataGrid在WinCC V16里的角色分工谁连库、谁渲染2.1 为什么选ADODB而不是WinCC归档控件WinCC V16的画面脚本支持VBS而VBS天然能调用Windows的ADO组件。ADODBActiveX Data Objects Database是Windows平台上一套成熟的数据库访问接口通过它可以用统一的方式连SQL Server、Access甚至Oracle。在WinCC里用它最大的好处是查询逻辑完全由你控制——想查哪张表、想怎么拼WHERE条件、想按什么排序都是标准SQL说了算不受归档控件那套固定数据模型的约束。相比之下WinCC自带的归档控件面向的是WinCC自己的归档系统数据先要进WinCC归档再通过控件展示。这条链路对“已经在SQL Server里存好的业务数据”来说多了一层搬运。而ADODB是直接读SQL Server省掉中间环节。代价是你要自己管连接、自己处理异常、自己控制刷新节奏没有归档控件那种开箱即用的缓冲和断线重连机制。所以选型判断很简单数据源在SQL Server且查询逻辑自定义程度高用ADODB数据源在WinCC归档且要跟趋势控件联动用归档控件。2.2 DataGrid控件在WinCC画面里的注册与放置DataGrid不是WinCC默认工具箱里的控件需要手动注册。WinCC画面编辑器里右键控件面板选“添加/删除控件”在ActiveX控件列表里找到“Microsoft DataGrid Control, Version 6.0 (SP6)”或更高版本勾选后它才会出现在工具箱里。拖到画面上之后给它起一个明确的控件名比如dgSQLData这个名称后面在VBS里要用到。放置时有个细节DataGrid默认高度只显示两三行现场看板至少要拉到能显示10到15行。另外把AllowUpdate属性设为False防止操作工误点单元格改数据AllowAddNew也设False。这些属性既可以在控件属性面板里改也可以在脚本初始化时统一设置后者更利于版本管理和批量部署。2.3 连接字符串的构成与WinCC V16下的Provider选择连SQL Server的Provider在WinCC V16这种偏工控的Windows环境里我一般用SQLOLEDB而不是MSOLEDBSQL。原因不是性能而是兼容性——SQLOLEDB是Windows自带的老组件几乎不会因为目标机没装新版驱动而翻车MSOLEDBSQL需要额外安装驱动包现场部署多一台机器就多一个变量。连接字符串的典型结构如下 WinCC V16 VBS脚本中构造SQL Server连接字符串 Dim connStr connStr ProviderSQLOLEDB.1; _ Data Source192.168.10.50; _ SQL Server的IP或主机名 Initial CatalogLineData; _ 目标数据库名 User IDwincc_reader; _ 只读账号别用sa PasswordReadOnly2024; _ 密码按现场策略管理 Connect Timeout5; 连接超时5秒避免画面卡死这里几个参数值得说清楚。Data Source写IP比写主机名稳因为工控网络里DNS不一定可靠。Initial Catalog是数据库名不是表名。User ID强烈建议单独建一个只读账号只给目标表的SELECT权限这样即使脚本被误改也删不了数据。Connect Timeout设5秒是经验值——设太长SQL Server一挂画面就假死设太短网络抖动时又频繁报错。5秒在产线局域网里是个平衡点。3. 从零跑通VBS脚本连库、查询、绑定DataGrid的完整步骤3.1 画面加载时初始化连接与DataGrid列头第一步是在画面打开时建立连接并配置DataGrid的列结构。我习惯把初始化逻辑放在画面的“打开画面”事件里而不是“显示”事件因为“显示”事件在某些切换场景下会重复触发导致连接对象被反复创建。下面这段脚本做了三件事创建Connection对象、打开连接、设置DataGrid的列头和基本属性。 画面打开事件初始化数据库连接和DataGrid外观 Dim conn, rs Dim connStr connStr ProviderSQLOLEDB.1;Data Source192.168.10.50; _ Initial CatalogLineData;User IDwincc_reader; _ PasswordReadOnly2024;Connect Timeout5; Set conn CreateObject(ADODB.Connection) conn.Open connStr 把连接对象存到画面级变量供后续查询复用 Set HMIRuntime.Tags(g_conn).Value conn 注意实际项目中连接对象不宜直接存Tag 配置DataGrid列头 Dim grid Set grid ScreenItems(dgSQLData) grid.Clear grid.Columns.Add 0, BatchNo, 批次号, 1200 grid.Columns.Add 1, ProductName, 产品名称, 2000 grid.Columns.Add 2, TempValue, 温度(℃), 1000 grid.Columns.Add 3, RecordTime, 记录时间, 1800 grid.AllowUpdate False grid.AllowAddNew False逻辑说明CreateObject(ADODB.Connection)创建连接对象conn.Open执行实际连接。列头通过grid.Columns.Add逐个添加参数依次是列索引、绑定字段名、显示标题、列宽单位是缇1缇约等于1/20磅。这里有个坑列索引必须从0开始连续跳号会导致后续绑定错位。另外把Connection对象存进WinCC Tag这种做法在简单场景能用但更稳妥的是存到画面脚本的全局变量或使用脚本模块级变量因为Tag系统对COM对象的序列化支持并不理想跨画面传递容易出玄学问题。3.2 用Recordset填充DataGrid并处理NULL值查询和绑定是核心步骤。下面这段脚本在按钮点击或定时器触发时执行从SQL Server取数并填入DataGrid。 查询按钮点击事件执行SQL并把结果集绑定到DataGrid Dim conn, rs, sql Dim grid Set conn CreateObject(ADODB.Connection) conn.Open ProviderSQLOLEDB.1;Data Source192.168.10.50; _ Initial CatalogLineData;User IDwincc_reader; _ PasswordReadOnly2024;Connect Timeout5; sql SELECT TOP 50 BatchNo, ProductName, TempValue, RecordTime _ FROM ProductionLog _ WHERE RecordTime DATEADD(hour, -8, GETDATE()) _ ORDER BY RecordTime DESC Set rs CreateObject(ADODB.Recordset) rs.Open sql, conn, 1, 1 1adOpenKeyset, 1adLockReadOnly Set grid ScreenItems(dgSQLData) grid.Clear 逐行填充处理NULL值 Dim i i 0 Do While Not rs.EOF grid.AddItem grid.TextMatrix(i, 0) SafeStr(rs(BatchNo)) grid.TextMatrix(i, 1) SafeStr(rs(ProductName)) grid.TextMatrix(i, 2) SafeStr(rs(TempValue)) grid.TextMatrix(i, 3) SafeStr(rs(RecordTime)) i i 1 rs.MoveNext Loop rs.Close conn.Close Set rs Nothing Set conn Nothing NULL值转换辅助函数 Function SafeStr(v) If IsNull(v) Then SafeStr Else SafeStr CStr(v) End If End Function逻辑说明rs.Open sql, conn, 1, 1里的两个1分别是游标类型和锁类型adOpenKeyset允许看到其他用户的修改adLockReadOnly表示只读这对查询展示场景足够且开销小。grid.AddItem增加一行grid.TextMatrix(row, col)按行列赋值。NULL处理是必须的——SQL Server里的NULL传到VBS会变成Null直接赋给TextMatrix会报类型错误所以用SafeStr包一层。参数方面TOP 50限制返回行数防止一次拉太多把画面拖慢DATEADD(hour, -8, GETDATE())取最近8小时数据这个时间窗口按你的产线节奏调批次间隔短就缩到2小时间隔长就放到24小时。3.3 定时刷新与连接释放的节奏控制如果画面需要自动刷新不要在画面循环事件里直接查库。WinCC的循环事件触发频率可能很高每次都开连接会把SQL Server的连接池打满。我的做法是用一个内部变量做计数器每N次循环触发一次查询或者直接用WinCC的定时器对象。 画面循环事件每10秒触发一次查询 Dim lastQueryTime lastQueryTime 存在画面级变量中初始为0 If (HMIRuntime.Tags(g_lastQuery).Value 0) Or _ (DateDiff(s, HMIRuntime.Tags(g_lastQuery).Value, Now) 10) Then HMIRuntime.Tags(g_lastQuery).Value Now 调用查询子过程 Call RefreshGrid() End If逻辑说明用DateDiff判断距上次查询是否超过10秒是则更新标记并执行查询。g_lastQuery是一个WinCC内部变量类型为日期时间。刷新间隔的设定要看数据变化频率和SQL Server负载——产线参数一般5到10秒够用批次记录可以30秒甚至手动刷新。注意每次查询后必须rs.Close和conn.Close否则连接会泄漏跑几个小时之后SQL Server就会拒绝新连接这是最常见的翻车点之一。4. 避坑与排查ADODBDataGrid在WinCC V16里的5个血泪教训4.1 现象画面打开报“未找到提供程序”脚本直接中断原因目标机没注册SQLOLEDB提供程序或者WinCC运行在64位模式下但脚本试图调用32位Provider。WinCC V16的画面脚本引擎位数取决于安装配置而SQLOLEDB在部分精简版Windows上默认不注册。解决先在目标机用regedit查HKEY_CLASSES_ROOT\SQLOLEDB是否存在。不存在就装MDAC或SQL Server Native Client。更稳的做法是统一用MSOLEDBSQL并确保每台机器都装了对应驱动虽然多一步部署但避免了现场找不到Provider的尴尬。4.2 现象DataGrid显示中文列头变成乱码原因VBS脚本文件保存编码和WinCC运行时的区域设置不一致。脚本里写的中文列头如果文件存成UTF-8而WinCC按ANSI解析就会乱码。解决把脚本文件另存为ANSI编码或者在脚本里用ChrW函数拼中文。更彻底的办法是列头不写死在脚本里而是从SQL Server的一张配置表里读这样编码问题转移到数据库层由SQL Server的排序规则统一处理。4.3 现象查询返回几千行时画面卡死十几秒原因逐行AddItemTextMatrix赋值在行数多时性能急剧下降每次赋值都触发控件重绘。解决查询端加TOP限制展示端在填充前设grid.Redraw False填充完再设grid.Redraw True。另外把grid.Visible临时设False也能减少重绘。如果确实要展示大量数据考虑分页查询每页50到100行用“上一页/下一页”按钮控制。4.4 现象脚本运行一段时间后报“连接对象已关闭”原因Connection对象被多个过程共享某个过程提前conn.Close了其他过程再用就报错。或者SQL Server端设置了空闲连接超时连接被服务端断开。解决每个查询过程独立创建和关闭连接不要跨过程共享Connection对象。如果一定要复用加一个状态判断If conn.State 0 Then conn.Open connStr。另外在连接字符串里加Connect Timeout5只是连接超时空闲超时要在SQL Server端配置或者靠每次查询重新建立连接来规避。4.5 现象DataGrid列宽在运行时自动变回默认值原因Columns.Add设置的列宽在grid.Clear之后被重置或者画面分辨率变化导致控件重新布局。解决把列宽设置放在每次Clear之后、填充数据之前而不是只在画面打开时设一次。如果画面支持多分辨率用相对宽度而不是绝对缇值或者监听画面尺寸变化事件重新设列宽。5. 进阶技巧把查询参数化和结果导出做成可复用模块5.1 用参数化查询替代字符串拼接前面示例里的SQL是写死的实际项目里经常要按批次号、时间段筛选。直接拼字符串有SQL注入风险虽然工控内网风险低但操作工误输入引号就会导致语法错误。参数化查询是更稳的做法。 参数化查询按批次号筛选 Dim cmd, rs, sql Set cmd CreateObject(ADODB.Command) Set cmd.ActiveConnection conn cmd.CommandText SELECT BatchNo, ProductName, TempValue, RecordTime _ FROM ProductionLog WHERE BatchNo ? ORDER BY RecordTime DESC cmd.Parameters.Append cmd.CreateParameter(BatchNo, 200, 1, 50, batchInput) Set rs cmd.Execute逻辑说明?是占位符CreateParameter的参数依次是参数名、数据类型200表示adVarChar、方向1表示adParamInput、长度、值。这样即使batchInput里带引号也不会破坏SQL结构。参数化查询在ADODB里对SQL Server走的是sp_executesql执行计划可以复用性能也比拼字符串好。5.2 把DataGrid内容导出为CSV供追溯现场经常需要把当前显示的表格导出来存档。DataGrid本身没有导出方法但可以遍历它的内容写文件。 导出DataGrid内容到CSV文件 Dim fso, f, i, j, line Set fso CreateObject(Scripting.FileSystemObject) Set f fso.CreateTextFile(D:\Export\BatchExport_ _ Replace(Replace(Now, :, ), , _) .csv, True) 写列头 line For j 0 To grid.Cols - 1 line line grid.TextMatrix(0, j) , Next f.WriteLine Left(line, Len(line) - 1) 写数据行 For i 1 To grid.Rows - 1 line For j 0 To grid.Cols - 1 line line grid.TextMatrix(i, j) , Next f.WriteLine Left(line, Len(line) - 1) Next f.Close逻辑说明grid.Rows和grid.Cols返回当前行数和列数TextMatrix按行列读值。文件名用时间戳避免覆盖。注意CSV里的逗号如果出现在数据内容中会破坏格式严格做法是对每个字段做引号包裹和内部引号转义这里为了简洁省略了实际项目建议补上。5.3 一个可复用的查询模块结构把连接、查询、绑定、导出拆成独立子过程放在WinCC的脚本模块里画面事件只负责调用。这样换一个画面、换一张表只需要改SQL和列头定义不用重写整套逻辑。我一般会建一个modDB模块里面放OpenConn、QueryToGrid、ExportGrid三个函数画面脚本里Call QueryToGrid(SELECT ..., dgSQLData)一行搞定。这套结构在多个产线看板上复用之后维护成本比每个画面各写一套低得多。最后说个习惯每次改完SQL或连接串先在WinCC的脚本调试器里单步跑一遍确认rs.EOF和rs.Fields.Count符合预期再挂到画面上。我吃过太多次“脚本没错但数据不对”的亏后来养成先看Recordset再绑控件的习惯省下不少现场排查时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表