尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Access数据库写入WinCC变量:ODBC+DSN+VBS脚本实战指南

Access数据库写入WinCC变量:ODBC+DSN+VBS脚本实战指南 简介这份文档面向工业自动化与过程控制领域的工程师及WinCC初学者讲解如何借助VBS脚本将Access数据库中的指定数据写入WinCC变量实现监控画面的实时数据联动。资料以实际操作为主线完整演示了建立Wincc_Data数据库与数据表、配置名为Sample的ODBC数据源并编写脚本查询Tag1字段第50条记录写入U16Tag1变量的全过程。代码中包括了ADODB连接、SQL查询、记录集读取及资源释放等关键步骤同时涉及连接字符串、字段数量判断与异常输出等细节可直接参考改造用于其他字段或变量。该文档示例清晰不仅提供完整VBS代码还解释了数据库表结构、ODBC配置要点以及脚本运行后的资源清理逻辑能帮助读者理解数据库与WinCC交互的完整链路。资源为单个PDF文档大小仅18KB便于快速查阅目前已有342人学习适合在备考或项目开发中需要快速掌握WinCC与数据库通信方法的读者下载使用。1. Access数据库写入WinCC变量为什么这条数据通路值得打通车间里最常见的场景是MES 或实验室系统把批次号、配方参数、质检结果存在 Access 数据库里中控室的 WinCC 画面上要实时显示这些数据。手抄进画面既慢又容易出错直接用 WinCC 的 ODBC 能力把这层数据桥接起来是很多项目里最省事也最可靠的做法。这篇技术笔记围绕 Access 数据库数据写入 WinCC 变量这个方向把从 ODBC 驱动配置、DSN 建立、VBS 脚本编写到常见坑的完整过程说清楚。适合正在做 WinCC 画面集成、又不想为一个小功能引入整套数据库中间件的工程师。2. 动手前先立好地基ODBC驱动、系统DSN与WinCC变量表2.1 驱动选型为什么系统DSN比Jet/ACE直连更省心要把 Access 数据库里的数据搬进 WinCC 变量脚本层通常通过 ADOActiveX Data Objects来做但底层驱动有两条路OLE DB 直连和 ODBC 数据源。我第一次做这个需求时图省事直接用了 OLE DB 连接字符串结果开发机上跑得好好的打包到现场工控机就报“未找到提供程序”折腾了快半天才发现是 Access Database Engine 的位数和 WinCC 进程位数不匹配。后来所有项目都改用系统 DSN这一类的兼容性问题基本绝迹。所谓系统 DSN就是在 Windows 的 ODBC 管理器里先把 Access 数据库文件和一个名字绑定好脚本里只写DSNAccessData;这样一句话。好处有三个一是驱动位数问题只在配置 DSN 时处理一次不用每次改脚本二是 Access 文件路径变了只需要改 DSN 里的数据库路径部署人员不碰代码也能维护三是以后想把 Access 换成 SQL Server脚本主体不用重写DSN 换成 SQL Server 的驱动即可。也要说清楚边界Access 是单机文件型数据库几百条到几万条的小数据量完全没问题几十个客户端同时读写就会开始“卡”甚至锁库。如果数据量比较大、并发要求高建议直接把后端换成 SQL Server但下面这套 VBS ADO 的写法仍然沿用切换成本很低。2.2 配置系统DSN32位与64位分清楚配置 DSN 时最容易埋雷的就是位数。WinCC 7.x 的运行时多数是 32 位进程它只能看见 32 位 ODBC 数据源64 位 Windows 上控制面板里的“ODBC 数据源管理器(64位)”注册在 System32 目录给 32 位程序用是无效的。我一般直接在运行框输入C:\Windows\SysWOW64\odbcad32.exe打开的是 32 位版本的 ODBC 管理器。如果安装的 WinCC 是 64 位版本那就反过来用C:\Windows\System32\odbcad32.exe。判断方法很简单脚本里用创建出来的 DSN 连接一次报“未发现数据源名称并且未指定默认驱动程序”十有八九就是位数不对。打开管理器后按以下步骤配置切到“系统 DSN”选项卡点“添加”。在驱动列表里选“Microsoft Access Driver (*.mdb, *.accdb)”。如果列表里没有这个驱动说明本机没装 Access 数据库引擎需要先安装对应版本的 AccessDatabaseEngine 再重试。填数据源名例如 AccessData点“选择”定位到 .mdb 或 .accdb 文件。如果 Access 数据库设了密码在“高级”里填登录名称和密码。点“测试连接”弹窗提示连接成功就算配好了。注意这里要选“系统 DSN”而不是“用户 DSN”。WinCC 运行系统有时以服务方式启动用户 DSN 只对当前登录用户可见服务进程里很可能读不到系统 DSN 对所有用户可见更牢靠。另外Access 驱动选错了也会出问题旧版“Microsoft Access Driver (*.mdb)”只认 mdb 格式遇到 accdb 文件就会提示“不是有效的文件路径”这时优先找带 accdb 的那个驱动。2.3 WinCC变量表设计目标类型与Access字段一一对应DSN 只是把路修通了落数据还要靠 WinCC 变量。建议在变量管理里建一组专用的“DB_”前缀变量和 PLC 变量分开后面排查问题会省很多事。变量类型要和 Access 字段类型对应上否则脚本写入时会自动转换甚至报错。Access 字段类型WinCC 变量类型注意事项整数LONG/INT有符号 32 位数数值超过 21 亿时改用 64 位变量小数DOUBLE/FLOAT浮点数 32 位/双精度温度、压力这类过程值用浮点文本TEXT文本变量注意 Access 旧版文本字段只有 255 字符日期/时间DATETIME文本变量建议先在 SQL 里格式化别直接写原始 OLE 日期是/否BIT二进制变量读出来可能是 -1/0写入前做一下转换这里有一个容易被忽略的点画面上的文本变量默认刷新周期可能不匹配如果你把 Access 里的数值写到文本变量里画面却半天不更新先检查变量的更新周期把它改成“画面周期”或“按需”再试。内部变量一般没有刷新周期问题这也是我建议优先写内部变量的原因。2.4 用脚本先验证连接最小测试代码DSN 配好、变量表建好之后别急着写完整逻辑先跑一段最小验证脚本。这段代码可以放在画面任意一个按钮的鼠标事件里或者在全局脚本编辑器里手动执行。它只做一件事通过 DSN 打开 Access取第一条记录弹窗显示出来。Option Explicit Dim conn, rs Set conn CreateObject(ADODB.Connection) conn.ConnectionString DSNAccessData; conn.Open Set rs conn.Execute(SELECT TOP 1 id, batch_no FROM t_recipe) If Not rs.EOF Then MsgBox 连接成功第一条记录 id rs.Fields(id).Value 批次 rs.Fields(batch_no).Value Else MsgBox 连接成功但表 t_recipe 是空的 End If rs.Close Set rs Nothing conn.Close Set conn Nothing这段脚本里有两个关键点。第一conn.Execute执行的是 SQL 查询TOP 1限定了只取一条记录既验证了连接也验证了表名和字段名是否正确。第二用rs.EOF判断是否查到了数据空表时不会报“下标越界”。如果弹窗里显示的是“连接成功”说明 DSN、表名、字段名这三个基础条件都过了后面的写入逻辑可以放心往下写。如果这里弹出错误不用急着改脚本先把错误原因分成两类报“未发现数据源名称”是 DSN 位数或名称不对报“文件正由另一个用户以独占方式打开”是 Access 文件被占用了。这两类问题在脚本层怎么改都解决不了必须回到 DSN 配置或文件权限上处理。3. 写出第一版可用的VBS脚本连接、查询、写入三步走3.1 脚本放在哪全局脚本与画面脚本的取舍WinCC 里能跑 VBS 的地方就那么几个画面对象的属性/事件动作、全局脚本编辑器的函数动作。我的习惯是通用逻辑放进全局脚本函数画面按钮或者周期触发器只负责调用。这样做的原因是画面里可能要放一个“手动刷新”按钮也可能要求每 5 秒自动刷新如果把整段数据库逻辑复制到每个按钮里后面要改 SQL 时就得改好几处漏改一次就翻车。抽出全局函数后按钮动作里就一句话Call ReadAccessToWinCC()可读性也高。全局脚本编辑器在 WinCC 项目管理器的“全局脚本”节点下VBS 函数创建后先编译通过再引用。这段逻辑不依赖画面对象调试时可以在编辑器的测试环境里直接跑不用先激活项目再反复操作画面。3.2 核心VBS脚本Access查询结果写入WinCC变量下面这段脚本是完整示例功能是从 Access 的配方表里查一条“状态为 active”的记录把批次号、温度设定值和最后更新时间分别写入三个 WinCC 变量。Option Explicit 读取 Access 配方数据写入 WinCC 内部变量 Dim conn, rs, sConn, sSQL 使用系统 DSN 连接 sConn DSNAccessData; 如果 Access 设置了密码加上 PWD 参数 sConn DSNAccessData;PWD123456; 查询条件取状态为 active 的最新一条 sSQL SELECT TOP 1 batch_no, temp_set, last_time _ FROM t_recipe _ WHERE statusactive _ ORDER BY last_time DESC Set conn CreateObject(ADODB.Connection) conn.ConnectionString sConn conn.Open Set rs conn.Execute(sSQL) If Not rs.EOF Then 写入 WinCC 变量Write 方法返回布尔值 If HMIRuntime.Tags(DB_batch_no).Write(rs.Fields(batch_no).Value) Then HMIRuntime.Tags(DB_read_ok).Write 1 End If 浮点字段做一下类型转换避免字符串写入 HMIRuntime.Tags(DB_temp_set).Write CDbl(rs.Fields(temp_set).Value) 日期字段格式化成字符串再写 HMIRuntime.Tags(DB_last_time).Write FormatDateTime(rs.Fields(last_time).Value, 2) Else HMIRuntime.Tags(DB_read_ok).Write 0 End If rs.Close Set rs Nothing conn.Close Set conn Nothing脚本的逻辑是三步。第一步用 DSN 建立 ADODB 连接注意sConn只写了DSNAccessData;末尾分号是 ADO 连接字符串的固定写法别省略。第二步执行 SQL 查询得到记录集rs通过Not rs.EOF判断是否有数据。第三步用HMIRuntime.Tags(变量名).Write 值把字段值写入 WinCC 变量。这里有几个参数值得单独说。HMIRuntime.Tags()是 WinCC VBS 访问变量的标准入口变量名必须和变量管理里完全一致大小写不敏感但建议统一。Write方法会返回布尔值写入失败时是 False所以代码里用一个状态变量记录结果方便画面上显示。CDbl是把字段值强制转成双精度浮点避免 Access 里数字字段被当成字符串写入 WinCC 浮点变量时出错。3.3 连接字符串和SQL关键参数逐个说连接字符串是整段脚本里最不能错的地方。用系统 DSN 时最简写法是DSNAccessData;如果 Access 数据库设了密码追加PWD...。有人会顺手写上UIDadmin对大多数 Access 文件是多余的写了反而可能报“用户名无效”。如果你接的不是 DSN 而是直连 ACE 驱动连接字符串要写成ProviderMicrosoft.ACE.OLEDB.12.0;Data SourceD:\data\recipe.accdb;Persist Security InfoFalse;两行都能用但现场部署时 DSN 方案的重试成本更低因为驱动问题在 ODBC 管理器里就能看见而直连报错时只能对着连接字符串逐字检查。SQL 语句建议用显式列名不要写SELECT *。Access 表里如果有备注字段或 OLE 对象字段SELECT *会把大字段也读出来网络传输和记录集解析都会变慢。查询条件里用到字符串时注意单引号包裹像statusactive这样。如果这个条件来自画面输入框直接把用户输入拼进 SQL 会有 Access 注入风险稳妥做法是过滤掉单引号或者用 ADODB.Command 参数化查询Dim cmd Set cmd CreateObject(ADODB.Command) cmd.ActiveConnection conn cmd.CommandText SELECT batch_no FROM t_recipe WHERE batch_no? cmd.Parameters.Append cmd.CreateParameter(p1, 202, 1, 50, batchNo) Set rs cmd.Execute这段代码里202是 adVarChar 的类型常量1表示输入参数50是字段长度。参数化之后用户输入里的单引号不会再破坏 SQL 结构。Access 项目里日常操作也就是增删改查本文场景常用的是“查”用SELECT如果还要从 WinCC 画面回写操作记录换成UPDATE或INSERT语句即可但这类写操作建议单独放一个函数别混在周期刷新里。3.4 加定时器用触发器让数据自动刷新手动执行脚本只能刷新一次生产画面上通常要求周期性刷新。WinCC 的定时器做法有两种最常见的是在全局脚本里给函数创建周期性触发器。在全局脚本编辑器中选中函数右键“创建触发器”选“周期”并设置时间比如 5 秒。周期不建议小于 1 秒脚本每次要建连接、执行查询、解析记录集太频繁会出现上一次还没执行完下一次又触发的情况。第二种是在画面上给某个对象设置触发器。比如把一个“文本”对象的属性挂上周期性触发器并在它的动作里调用读库函数。这种做法适合只在画面打开时才刷新画面关闭就不跑能减轻数据库压力。不管用哪种定时器建议在脚本开头加一个执行时间判断逻辑是Dim startTime startTime Timer ...原有逻辑... If Timer - startTime 3 Then HMIRuntime.Tags(DB_read_ok).Write -1 Exit Function End If如果数据库文件放在网络共享盘上网络抖动会让单次查询耗时变长这个超时处理能避免脚本积压。超时时写一个 -1 到状态变量画面上就能看到这次刷新是失败的而不是傻等。4. 数据写不进去或者写错值5个高频坑与排查路径4.1 找不到提供程序Microsoft.ACE.OLEDB.12.0未注册现象脚本一执行就弹窗提示“未找到提供程序”或者“未指定的错误”用系统 DSN 时则提示“未发现数据源名称”。原因两种情况。第一种是用直连方式写了ProviderMicrosoft.ACE.OLEDB.12.0;但现场机器没装 Access Database Engine或者装的是 64 位而 WinCC 进程是 32 位驱动在 32 位环境里不可见。第二种是 DSN 配置到了错误的 ODBC 管理器里位数对不上。解决统一改回系统 DSN 方案在SysWOW64\odbcad32.exe里确认系统 DSN 列表里有 AccessData 这条记录。如果一定要用 ACE 直连就安装对应位数的 AccessDatabaseEngine装完再打开 ODBC 管理器看驱动列表里是否出现 ACE 相关条目。这里提示一点装了 Access 数据库引擎后再装 Office可能把驱动版本搞乱最好在工控机上先把驱动装好再装 Office。4.2 变量刚写进去就被打回原形外部变量被PLC侧覆盖现象脚本执行后WinCC 变量“看起来”写成功了但画面上的值闪了一下又变回原来的。脚本里 Write 返回 True但画面就是不对。原因最典型的情况是目标变量是外部变量PLC 程序每个周期都在往这个地址写值。WinCC 的Write只把数据放到通信发送队列里PLC 的下一次周期写入会把这个值覆盖回去画面因此“反弹”。这种现象经常会被误报成 WinCC 握手错误其实通道握手正常问题出在变量写入竞争上。解决Access 数据先进内部变量内部变量不与 PLC 通信不存在外部写入竞争。画面显示用内部变量如果 PLC 确实需要这份数据再做一次内部变量到外部变量的同步同步时机由 PLC 侧逻辑决定。临时排查时可以先把变量管理里的目标变量类型改成内部变量试试如果画面不再反弹就证实了是覆盖问题。4.3 “脚本语句未结束”VBS换行与符号问题现象在 WinCC VBS 编辑器里保存脚本或编译时报“脚本语句未结束”有时还带“缺少对象”之类的后续错误。原因VBS 的合法语句以换行为界一条逻辑行写不下时要加续行符“_”下划线。SQL 很长时很容易漏掉或者写了续行符但下划线后面跟了空格VBS 不认。另一个常见来源是字符串内部用了中文引号或全角分号VBS 分词直接断掉。解决SQL 拼接时每行末尾主动加一个空格再加下划线这是最省心的写法。中英文标点问题更隐蔽写完语句后检查引号、分号的输入法状态。在 WinCC 里特别容易踩的是从网页或 Word 里复制 SQL复制进来的隐形空格会让“脚本语句未结束”反复出现处理办法是在编辑器里把粘贴的内容全选后重新敲一遍关键行。4.4 Access文件被锁死.laccdb锁文件“假死”现象连接正常脚本执行时却抛“文件正由另一个用户使用”或“磁盘或网络错误”有时候只在现场出现开发机上完全复现不了。原因Access 引擎在打开文件时会生成一个 .laccdb 锁文件如果脚本没有正常关闭连接或进程异常结束残留的锁文件会继续占着文件。还有一种情况是 Access 数据库以独占方式打开比如有人正在 Access 界面里编辑表此时外部脚本只能读不能写某些操作就报错。解决脚本里务必成对执行rs.Close、conn.Close并把对象置为Nothing让连接及时释放。部署现场约定Access 文件所在目录不允许人工用 Access 界面直接打开编辑只允许部署的只读查询。如果锁文件确实残留了关闭相关进程后手动删除 .laccdb 文件再试。如果 Access 文件放在网络共享上断网瞬间产生的锁文件最难清建议用共享目录的读写权限控制代替人工管理。4.5 日期类型变了样OLE日期与文本字段转换现象Access 里的日期字段比如 2025-03-14 08:30写入 WinCC 文本变量后显示成数字比如 45745.35417。原因Access 的日期时间在底层是 OLE 自动化日期存储为双精度浮点数整数部分是天数小数部分是时间。直接把字段值写入文本变量时WinCC 拿到的是原始浮点数值没有格式化成可见日期。解决在 SQL 里格式化或者在脚本里用FormatDateTime。SQL 写法更直接SELECT TOP 1 Format(last_time, yyyy-mm-dd hh:nn:ss) AS last_time_str FROM t_recipe脚本写法是HMIRuntime.Tags(DB_last_time).Write FormatDateTime(rs.Fields(last_time).Value, 2)其中参数 2 表示短日期格式。两种方式都要注意如果字段值本身是 NullFormatDateTime会报“无效使用 Null”写之前先做IsNull判断再决定写空字符串还是写默认值。5. 更进一步批量变量刷新和多表联动5.1 批量写入循环遍历记录集时不阻塞画面刷新有时 Access 表里不只一条数据要上画面可能是一组配方参数或者一批质检结果。把这组数据写到多个 WinCC 变量时常见写法是循环记录集Dim i i 0 Do While Not rs.EOF And i 20 变量名约定为 DB_PARA_0, DB_PARA_1, ... HMIRuntime.Tags(DB_PARA_ CStr(i)).Write rs.Fields(para_value).Value i i 1 rs.MoveNext Loop前提是变量管理里已经预先建好了 DB_PARA_0 到 DB_PARA_19 这一组变量。VBS 不支持运行时动态创建 WinCC 变量变量必须提前定义这是最容易踩的一步。循环里有个上限 i 20防止 Access 表里混入异常数据把变量名拼到超出范围去。批量写内部变量通常很快但如果目标变量是外部变量一次Write会进入通信队列循环几百个会排队画面刷新看起来就像卡死。优化策略是把批量写入的临时结果先放在内部变量里再由通信驱动统一同步或者分批加延时每写 10 个变量就暂停几十毫秒避免通信队列长时间占满。WinCC 的 VBS 环境不一定提供WScript.Sleep我一般会用画面侧的周期触发把批次拆小或者干脆牺牲一点实时性把批量写放在独立脚本里一次完成画面上用状态变量提示“刷新中”。5.2 多表联动一条SQL代替多个脚本Access 项目里数据一般不会只在一张表里典型情况是配方主表和批次记录表分开。如果脚本里先查表 A再拿结果去查表 B逻辑会变得很碎。用一条 JOIN 查询能同时取出两张表的数据脚本只维护一个记录集SELECT r.batch_no, r.temp_set, p.actual_temp, p.actual_speed FROM t_recipe r INNER JOIN t_production p ON r.batch_no p.batch_no WHERE r.status active配合前面读变量的套路rs.Fields(actual_temp)就能直接取到联表后的字段。注意 Access 的 JOIN 语法里表别名写在表名后面不需要 AS 关键字别名之间用点号引用字段。如果两张表的字段名冲突SELECT 里必须用“别名.字段名”写清楚否则 ADO 解析记录集时可能取了错误的列。多表联动的另外一个好处是如果以后想让 WinCC 侧通过 Access 做“增删改查”里的写操作脚本里改成conn.Execute执行 UPDATE 或 INSERT 语句即可但因为写操作会改变库内容建议把写库动作单独放进一个函数不要在周期刷新的查询函数里混着做。写库操作还要加事务保护避免 SQL 只执行了一半。典型做法是先BEGIN TRANSACTION执行完再COMMIT TRANSACTION出错了就ROLLBACK保证整批数据一致。5.3 触发方式用内部变量做数据开关周期触发器是固定节奏运行但工程上经常希望“PLC 说刷新我这边才去刷”。做法是定义一个内部变量DB_refresh_cmd周期脚本里每个周期去读这个变量为 1 时才执行 Access 查询执行完写回 0Dim iCmd iCmd HMIRuntime.Tags(DB_refresh_cmd).Read If iCmd 1 Then Call ReadAccessToWinCC() HMIRuntime.Tags(DB_refresh_cmd).Write 0 End If配合起来PLC 侧通过 WinCC 通道把外部变量同步到这个内部变量就实现了“PLC 发刷新命令WinCC 查数据库”的联动。这个模式的优点是 Access 不会被一直轮询只在需要时被触发锁库和网络抖动的风险都会小很多。调试时可以直接在变量监视器里把DB_refresh_cmd置 1 来触发一次刷新观察DB_read_ok是不是先变 1 再被脚本复位。如果置 1 后DB_read_ok没变化问题就锁定在条件判断之前的连接和查询环节不用瞎猜整段脚本。6. 收尾怎么确认每次写入都靠得住脚本能跑、画面能看到数据只是第一步工程上还要能证明每次写入都成功。我一般在项目里加三样东西状态显示、日志记录、超时保护。状态显示不复杂在画面角落里放一个文本框绑定DB_read_ok正常写 1失败写 0超时写 -1。再放一个DB_last_refresh变量记录最后一次成功刷新的时间。操作员看到时间和状态都正常就知道这条数据链路是通的如果状态变了而时间没变说明脚本执行了但连接或查询出了问题。这一段看起来不起眼但排障时能省掉大量的对讲机沟通。日志记录建议用 FileSystemObject 写文本文件每次脚本执行后追加一行内容包括执行时间、查询条数和写入返回码Dim fso, f Set fso CreateObject(Scripting.FileSystemObject) Set f fso.OpenTextFile(D:\logs\db_access.log, 8, True) f.WriteLine Now | rows rowCount | ok HMIRuntime.Tags(DB_read_ok).Read f.Close日志文件不要放在 Access 的同一目录避免和锁文件混在一起。锁文件是 Access 自己生成的日志却可能因为目录权限问题写不进去两个问题叠在一起会很难排查。记录超过一定大小后建议按天建文件或者定期清空免得磁盘被日志塞满。日志里的时间用完整的Now只记日期不记时刻的话排障时根本分不清哪条写入是最后成功的。最后是我的一个习惯连接字符串和 SQL 语句不写死在脚本里而是放在单独的配置文件脚本启动时读进来。这样换数据库路径或改查询条件时运维只需要编辑文本文件不需要打开 WinCC 脚本编辑器重新编译。这几年我在多个项目里都是这个套路坑主要栽在驱动位数和外部变量竞争上每次写新项目都会先确认 DSN 位数、再确认目标变量是内部还是外部、最后补日志。把这三件事做在前面Access 到 WinCC 这条路基本就不会再翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表