简介:这是一份面向SCADA系统工程师与HMI开发者的Word操作文档,专门解决WonderWare Intouch 2014 R2(IDE)环境下的数据库对接与Excel报表生成问题。文档从基础配置讲起,包括通过SQL Server Management Studio修改身份验证模式、新建登录名、创建数据库与表单;随后详细说明Intouch标记名字典中内存标记的创建、SQL访问管理器绑定列表的字段对应,以及利用SQLConnect/SQLInsert/SQLDisconnect函数编写条件脚本完成数据写入;同时覆盖Excel报表模板的控制面板与参数日报设计、VBA引用配置,以及SQL数据库文件的备份恢复和附加操作。整份资源为单个doc文档,共1个文件,压缩包大小约2.48MB,内容按章节排布,适合需要将实时数据接入SQL Server并输出报表的工程人员对照操作。目前已有531人学习,对于正在搭建SCADA数据采集与报表系统的读者,此文档提供了从连接配置到脚本调试的完整思路和可复用细节。
1. 为什么Intouch 2014R2和SQL Server 2012这对组合总在报表上翻车
在我经手的SCADA上位机项目里,WonderWare Intouch 2014R2(IDE版)配SQL Server 2012几乎是固定搭配,数据先写库、再用Excel报表系统从库里拉出来做日报,这套流程看着不复杂,但真正落地时,十个项目里至少有六七个卡在数据库连接、绑定列表和脚本时序上。最典型的翻车现场是:Intouch里数据采集一切正常,报表却全是空的,查半天才发现是连接字符串里的服务器名没改,或者系统日期格式把SQL写入时间整乱了。
这篇文章就是我实际装这套环境时沉淀下来的操作记录,从SQL Server 2012建库建表讲起,到Intouch侧标记字典、绑定列表、SQLConnect连接脚本,再到Excel报表模板的VBA引用配置和数据库附加恢复,每一步都拆开说清楚。适合正在给Intouch配数据库的工控工程师、做HMI二次开发的人,也适合刚接手老旧SCADA系统、需要把历史数据库迁移到新服务器上的运维同事——照着走一遍,能省掉不少试错时间。
2. SQL Server 2012准备:身份验证模式、登录名与数据表
2.1 先搞清楚为什么必须改混合身份验证
Intouch通过SQLConnect()往SQL Server写数据,本质是走OLEDB的远程连接,脚本里会显式给出User ID和Password。如果SQL Server只开着Windows身份验证,那Intouch脚本里写的用户名密码就完全不生效,连接永远建立不起来。所以第一步必须先进SQL Server Management Studio,把身份验证改成“SQL Server和Windows身份验证模式”。
这个改动在服务器属性里完成,右键数据库实例,选择“属性”,左侧选“安全性”,右侧在“服务器身份验证”一栏切换到混合模式,确定后会提示重启SQL服务。注意:改完模式后,旧有的Windows登录不受影响,但只有这一步改完,后面新建的SQL Server登录名才有意义。
-- 如果允许用命令行验证当前身份验证模式(以sysadmin身份执行) SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') AS IsWindowsOnly -- 返回 0 表示混合模式已开启,返回 1 表示仍然仅有Windows身份验证这段查询是用来确认服务器是否真的切到了混合模式。很多人在属性页里点了确定,但SQL服务没重启,或者改的是默认实例而Intouch连的是命名实例,查询结果会直接暴露问题。我一般改完安全性设置后会顺手跑一遍,避免后面接不上时还要回头猜。
2.2 新建登录名:权限给到什么级别
切换完身份验证模式,下一步是新建登录名。在SSMS的“对象资源管理器”中展开“安全性”→“登录名”,右键选“新建登录名”。登录名建议单独建,不要直接拿sa在Intouch连接串里跑,虽说省事,但一旦报表服务被人扫到,sa权限泄漏的代价太大。
新建登录名时,“常规”页设置登录名和密码,“服务器角色”页勾选public和sysadmin。如果是纯写日报数据的库,给db_owner即可;但Intouch那种老环境里,报表和诊断经常要跨库查询,我一般会直接给sysadmin,够用也省得后续排查时还要逐个库补权限。密码强度要注意,SQL Server 2012默认强制密码策略,密码太短会直接拒绝创建。
如果现场已经有现成登录名,就不必新建,直接在“登录名”下找到该用户,右键进入属性,“常规”页改密码,“服务器角色”页调整角色,确认无误后保存。这里有个小坑:改完密码后,正在运行的Intouch连接不会马上断开,但下次重连时如果脚本里的密码没同步更新,就会出现“登录失败”的报错,所以改密码一定要记得同步改Intouch连接字符串。
2.3 建库建表:列名和类型要跟Intouch标记对齐
登录名准备好后,回到SSMS的“数据库”节点,新建一个数据库,名称建议用报表相关的业务名,比如YADC_BB。不建议用中文库名,OLEDB连接字符串对中文库名的兼容性在不同驱动上表现不一致,我们现场就遇到过中文库名在sqloledb下能连、换到SQLNCLI后又报找不到库的情况。
库建好后,在库下新建表,这张表就是Intouch写入的目标表。表里的每一列都要和Intouch绑定列表中的变量一一对应,列名和数据类型都必须匹配。比如Intouch的内存整形标记,对应SQL Server的int;内存消息标记对应nvarchar(50)或者nvarchar(100);内存离散型标记对应bit。
| Intouch标记名 | 标记类型 | SQL Server列名 | SQL Server数据类型 | 说明 |
|---|---|---|---|---|
| Connid1 | 内存整形 | Connid1 | int | 记录SQL连接编号,不参与报表数据 |
| GetDateTimeS | 内存消息 | GetDateTimeS | nvarchar(50) | 记录写入时间 |
| NodeName | 内存消息 | NodeName | nvarchar(50) | 记录计算机名 |
| WSQLS | 内存离散 | WSQLS | bit | 写入完成标记 |
这张对照表建议单独贴到工程文档里。我见过太多人建表时图省事,列名用中文或者缩写,结果到绑定列表里一个个去猜对应关系,最后SQLInsert直接报列名无效。最稳妥的做法是:先在标记名字典里把所有要写库的标记建好,再照着标记名建表列,两边保持完全一致。
3. Intouch侧连接配置:标记字典、绑定列表与连接脚本
3.1 标记名字典:四个标记一个都不能少
在Intouch IDE的“标记名字典”里,先新建四个内存变量。Connid1是内存整形,用来接收SQLConnect()返回的连接编号;GetDateTimeS是内存消息,用来存记录写入的时间;NodeName是内存消息,存放本机计算机名;WSQLS是内存离散型,用作向数据库写入的完成标记,防止同一个秒周期里重复写入。
// 标记名字典新建变量时,对应字段按下表填写 标记名: Connid1 类型: 内存整形 初始值: 0 标记名: GetDateTimeS 类型: 内存消息 初始值: "" 标记名: NodeName 类型: 内存消息 初始值: "" 标记名: WSQLS 类型: 内存离散 初始值: 0标记类型一旦建好,改起来麻烦,尤其是消息型变量的长度。Intouch的内存消息默认长度够用,但如果你要存长中文文本,建议先把长度调大再投入使用。Connid1这个整型标记务必用内存型,不要用I/O型,因为SQLConnect返回的编号只在内存中存在,I/O型标记会跟外部设备绑定,反而会让连接逻辑混乱。
3.2 系统日期格式:报表时间错乱的隐形原因
这一步容易被忽略:在Windows“控制面板”→“区域和语言”→“格式”里,把短日期格式改成yyyy/M/d,把时间格式改成24小时制HH:mm:ss。Intouch的$Date、$Time等系统变量会直接继承Windows区域格式,如果系统日期是M/d/yyyy,SQLInsert写入GetDateTimeS时就会出现月日年的乱序,Excel报表排序时看起来就是乱的。
改完区域格式后,必须重启Intouch的WindowViewer(运行时),因为格式设置在启动时读入内存,运行中修改不会生效。这也是为什么很多人在Intouch里怎么调整脚本都取不到正确时间——问题根本不在Intouch,而在Windows区域设置。
3.3 SQL访问管理器:绑定列表
在Intouch IDE左侧工具视图中找到“SQL访问管理器”,展开后选择“绑定列表”,新建绑定列表(比如命名TDays)。绑定列表的作用是把Intouch标记映射到SQL表的列上,SQLInsert()执行时,Intouch会按这个列表找到每个标记对应的表列,自动生成INSERT语句。
绑定列表里添加的每一项,都要保证标记名和SQL表的列一一对应,且数据类型匹配。比如表里的GetDateTimeS列是nvarchar,绑定列表里对应项的数据类型也要是消息型;表里的Connid1列是int,对应项的类型必须是整形。类型不匹配时,有些版本不报错,只是写入NULL,这种静默失败最坑。
提示:绑定列表项一旦建好,Intouch不会自动感知SQL表结构的变更。如果你修改了表的列名或类型,必须回到绑定列表里删掉旧项重新添加,否则SQLInsert会按缓存的结构生成语句,结果要么报列名无效,要么写入类型转换失败。
3.4 SQLConnect连接字符串:五要素逐个改
在“脚本”视图的“应用程序”脚本中,打开“启动时”选项卡,写入SQLConnect()。这个函数负责建立Intouch到SQL Server的连接,并把连接编号保存到Connid1里。
SQLConnect(Connid1, "provider=sqloledb;Data Source=OP_01;Initial Catalog=YADC_BB;User ID=sa;Password=single");连接字符串里五个部分用分号隔开,顺序可以调整,但每项内部不要有空格。Provider指定OLEDB驱动类型,SQL Server用sqloledb;Data Source是数据库所在计算机名,如果SQL Server是命名实例,要写成“计算机名\实例名”;Initial Catalog是数据库名;User ID是登录名;Password是密码。现场迁移时最容易漏改的,就是Data Source——从旧服务器拷过来工程,计算机名没跟着改,连接直接指向不存在的机器。
如果sqloledb在目标机器上报“未找到提供程序”,常见做法是换成本地驱动的SQLNCLI11,连接字符串写法一致,只改Provider即可。不要两种驱动混用,同一套Intouch工程的绑定列表和脚本都保持同一个Provider。
// 关闭时脚本,断开与SQL数据库的连接 SQLDisconnect(Connid1);在“应用程序脚本”的“关闭时”选项卡中,必须有SQLDisconnect(Connid1)。很多项目调试时反复重启WindowViewer,如果不主动断开连接,SQL Server端会堆积大量孤儿连接,最终把连接数打满,导致后续SQLConnect失败。我一般在关闭时脚本里加一行注释,说明这里是为了防止连接泄漏,后面维护的人就不会随手删掉。
3.5 定时写入脚本:$Second==3和$Second==5各干各的
数据写入的逻辑分两个时基:$Second==3时,获取当前时间和计算机名,供后续写入使用;$Second==5时,执行条件判断并调用SQLInsert()写入数据。两个秒点分开写,是为了避免在同一秒内既取时间又做写入判断,把操作串行化。
// 条件: $Second==3 条件类型: 为真时 // 执行: 获取当前时间戳,存到GetDateTimeS GetTime(GetDateTimeS);前半段脚本负责把系统当前时间写入GetDateTimeS。GetTime()是Intouch内置函数,它会用当前系统时间更新标记值,配合我们在3.2节改好的时间格式,写入SQL的就是标准化的日期时间字符串。如果这里取到的时间不对,先检查Windows区域格式,而不是怀疑脚本。
// 条件: $Second==5 条件类型: 为真时 // 按照文档整理后的完整定时写入脚本 IF NodeName == "OP_01" THEN IF $Minute >= 58 THEN IF WSQLS == 0 THEN SQLInsert(Connid1, "TDays", "TDays"); WSQLS = 1; ENDIF; ENDIF; ENDIF;逐行说明:第一层IF判断当前计算机名是否为OP_01,只有主操作员站才会写入;第二层IF判断当前分钟数是否大于等于58,这是为了在整点前把当天数据落库,方便跨天报表统计;第三层IF检查WSQLS是否为0,为0代表这次数据还没有写入,执行SQLInsert()后立刻把WSQLS置1,防止后续秒周期重复写入。SQLInsert(Connid1, "TDays", "TDays")中第一个TDays是SQL表名,第二个TDays是绑定列表名,两者可以不同名,但建议保持一致省得混淆。
以每5秒一次的频率执行这个脚本,只要分钟条件满足且WSQLS为0,就会写一条记录。实战中要注意:如果现场报表要求按整点时刻写一条,而不是分钟大于等于58就写,那你需要把$Minute==59改成取分钟边界+秒边界组合判断。这里文档用的是大于等于58,如果你的现场有跨天补数需求,它可以保证58分和59分两个周期内写入,但WSQLS只在第一轮置1,实际只会写入一次,后面轮次被WSQLS挡住,这正是设计意图。
4. 常见问题排查与避坑:四条高频踩坑记录
4.1 先分清是“连不上”还是“没写入”
遇到报表没数据,先别急着改脚本。打开Intouch运行时,观察Connid1的值:如果SQLConnect成功,Connid1会是一个非零整数;如果连接失败,Connid1保持0,且WindowViewer的日志窗口会打印OLEDB错误信息。确认连接成功后,再去看WSQLS有没有在$Second==5的周期里被置1。把这两步做了,能定位掉八成问题:连接失败查数据源和登录名,连接成功但WSQLS没变化,则查脚本条件和绑定列表。
4.2 四条高频踩坑记录
坑一:SQLConnect报“未找到数据源”或“登录失败”现象:Intouch运行时日志窗口出现连接错误,Connid1始终为0。 原因:连接字符串里的Data Source沿用旧项目里的计算机名,或者改成计算机名后没带实例名;还有一种可能是身份验证没切混合模式,导致User ID/Password完全不生效。 解决:先把Data Source改成SQL Server所在机器的实际计算机名,命名实例写成“计算机名\实例名”;再用SSMS用同样的登录名密码手动登录一次,确认登录名本身没问题。我习惯把连接字符串先放在一个临时窗口测试,排错通了再贴回Intouch脚本。
坑二:脚本逻辑看着对,但数据一条都不写现象:Connid1正常非零,WSQLS却一直为0,$Second==5的脚本似乎没有执行。 原因:计算机名判断写死成“OP_01”,但实际机器名是“OP_01”加后缀或全大写,字符串比较严格区分大小写和空格。文档里还有一个经典的笔误变体:把计算机名写成OP_OP1,然后和OP_01比较,永远不相等。 解决:在画面上加一个临时窗口显示NodeName的值,确认Intouch实际拿到的计算机名,再把IF条件里的字符串改成完全一致。此后我无论做哪个项目,都会先在画面里显示NodeName,而不是靠猜。
坑三:SQLInsert报语法错误或列名无效现象:SQLConnect正常,WSQLS已置1,但SQL表里没有新记录,日志提示INSERT语句失败。 原因:绑定列表里的列名和SQL表的实际列名不一致,或者绑定类型与表字段类型不匹配。尤其容易发生在表字段定义为datetime,而绑定列表的标记是内存消息的情况下。 解决:回到SQL访问管理器,逐项核对绑定列表的列名和类型,删除不一致项重新添加。如果需要更精确的报错信息,可以在脚本临时改动SQLInsert语句走向:先把SQLInsert换成SQLExecute,在运行时里用临时消息标记接收返回的错误说明,以此精确定位是哪一列出了问题。排查完再改回SQLInsert。
坑四:报表里时间错乱或出现AM/PM后缀现象:Excel报表取出的GetDateTimeS列显示“2024/1/5 03:10:00 PM”,排序和日期统计全部错乱。 原因:Windows区域格式用的是M/d/yyyy或12小时制,Intouch的$Time取值受此影响,写入SQL的时间字符串格式不统一。 解决:按3.2节的方法,把短日期改成yyyy/M/d,时间改成24小时制,然后重启WindowViewer。这个操作做完,GetDateTimeS写入的值就统一成“2024/1/5 15:10:00”。从那以后我每到一个新现场,第一件事就是先检查区域格式,再碰Intouch。
5. Excel报表系统:控制面板、参数日报与VBA引用
5.1 报表模板的两个页面
Excel报表系统由两个工作表构成:控制面板页和参数日报页。控制面板页放查询条件,比如起始日期、结束日期、班次选择和一个“刷新”按钮;参数日报页放从SQL Server 2012里查出来的明细数据,按日期排列。两个页面配合,形成一个完整的数据报表工具。
新建Excel文件后,把默认的Sheet1重命名为“控制面板”,再新建一个工作表命名为“参数日报”。控制面板页的格式建议预先画好:日期输入单元格用“yyyy/M/d”格式,和SQL表里的GetDateTimeS字符串保持一致;刷新按钮在Excel的“开发工具”菜单里插入表单控件,指定到一段VBA宏。
如果工具栏里没有“开发工具”选项卡,需要在Excel选项里手动开启。不开启的话没法插入按钮,也没法进入VBA编辑器——后面所有代码都写不了,所以这个开关务必提前确认。
5.2 VBA编辑器与三个必需引用
在“参数日报”工作表标签上右键,选择“查看代码”,进入VBA编辑器。VBA编辑器默认引用的类库不全够用,必须手动添加三个引用,否则代码里连接SQL Server和操作单元格的对象会直接报“用户定义类型未定义”。
在VBA编辑器菜单栏“工具”→“引用”中,至少钩选以下三项:
| 引用名称 | 作用 | 缺失时的报错 |
|---|---|---|
| Microsoft ActiveX Data Objects 2.x Library | 建立ADO连接,查询SQL Server数据 | 运行时提示ActiveX组件无法创建对象 |
| Microsoft Excel 对象库 | 操作工作表单元格和区域 | 代码中Cells、Range不可用 |
| Microsoft Internet Transfer Control 或 Microsoft Office 库 | 补充日期时间转换和网络相关对象 | 编译报“子过程或函数未定义” |
引用钩选完成后点确定,VBA工程会重新编译一遍。这里要注意:如果Excel是32位或64位版本不同,ADO的版本号会显示为2.8或6.1,不必纠结具体版本,只要列表中能看到带“ActiveX Data Objects”字样的项并且能钩上就行。我遇到过一台机器上只有4.0的旧版ADO,新版Excel打开引用对话框后钩选报“名称与现有模块冲突”,解决方法是先取消所有引用,重新打开Excel再进入引用界面,按顺序先钩ADO再钩Excel对象库。
5.3 报表数据刷新代码的常见写法
控制面板的刷新按钮背后,一般挂一个Sub过程,先用ADO连接SQL Server,执行SELECT查询,再把结果集逐行写入参数日报页。连接字符串的写法跟Intouch里很相似,只是Provider和数据库信息要按Excel所在机器的实际环境改。
Sub RefreshReport() Dim conn As Object Dim rs As Object Dim startDate As String Dim endDate As String Dim ws As Worksheet Set conn = CreateObject("ADODB.Connection") Set rs = CreateObject("ADODB.Recordset") Set ws = ThisWorkbook.Sheets("参数日报") startDate = ThisWorkbook.Sheets("控制面板").Range("B2").Value endDate = ThisWorkbook.Sheets("控制面板").Range("B3").Value ' 清空参数日报原有内容,保留表头 ws.Rows("2:1000").Delete conn.Open "Provider=sqloledb;Data Source=OP_01;Initial Catalog=YADC_BB;User ID=sa;Password=single;" rs.Open "SELECT GetDateTimeS, NodeName FROM TDays WHERE GetDateTimeS >= '" & startDate & "' AND GetDateTimeS <= '" & endDate & "' ORDER BY GetDateTimeS", conn ' 从第2行开始写入查询结果 If Not rs.EOF Then ws.Range("A2").CopyFromRecordset rs End If rs.Close conn.Close Set rs = Nothing Set conn = Nothing End Sub这段代码里,连接字符串的数据源和Intouch里保持一致,都是OP_01,但实际使用时要把OP_01替换成你自己的SQL Server机器名。CopyFromRecordset是ADO提供的高效方法,整表复制查询结果到单元格,比循环逐行赋值快得多。日期范围的参数从控制面板页的B2和B3单元格读取,再拼进SQL语句,这里要注意SQL注入问题:如果报表文件只在厂内使用且输入来源受限,直接拼接问题不大;如果担心,可以用参数化查询,但那个写法需要额外引用ADODB的Command对象。
提示:Excel报表的VBA里,SQL字符串中的日期建议用单引号包起来。SQL Server的nvarchar列能自动转换格式化的日期字符串,但如果控制面板的单元格没设成日期格式,而是带着AM/PM的文本,这里查询结果就会是空。
6. 数据库恢复与跑通验证:附加库、查数据、刷报表
数据库的备份恢复,在日常运维里比新建库更常见。SQL Server的数据文件有两个:一个是主数据文件YADC_BB.mdf,一个是日志文件YADC_BB_log.ldf,正常情况下它们存放在E:\DataBase目录下。迁移历史数据服务器时,整个DataBase目录拷过去,然后在目标机器上执行附加数据库操作,就能把整套报表历史数据完整带过来。
在SSMS的“数据库”节点上右键,选择“附加”,在弹出的“附加数据库”界面中点击“添加”,定位到E:\DataBase目录,选中YADC_BB.mdf,确定后SQL Server会自动匹配同目录下的日志文件并完成附加。附加完成后,展开数据库节点看能否看到YADC_BB,能展开并且表目录下有TDays,就说明物理文件挂载成功。
注意:附加完成后不要直接给Intouch用。在“安全性”→“登录名”里确认原来访问该库的登录名是否存在,如果不存在,SQL Server会把库文件里的用户映射到一个孤立的SID上,Intouch连接时会报“用户登录失败”。常见做法是重建一个同名登录名,然后在数据库的“用户”节点下把它映射到新登录名,权限给db_owner,再测试连接。
验证跑通分三步走。第一步,在SSMS里执行一条查询,确认数据表里有今天的记录:
SELECT TOP 20 GetDateTimeS, NodeName FROM TDays ORDER BY GetDateTimeS DESC如果返回结果里存在时间戳接近当前时间的记录,说明Intouch到SQL Server的写入链路是通的。如果表是空的,回到第3章检查$Second==5脚本里的分钟判断和WSQLS状态,重点确认WindowViewer是否正在运行且没有处于暂停状态。
第二步,在Intouch运行画面里加一个临时显示,观察Connid1和WSQLS两个值。Connid1为0说明连接断了,查连接字符串和SQL Server服务状态;WSQLS在秒点跳变到1说明写入动作已经执行,此时SQL表还空的话,问题就在绑定列表和列名对应上。
第三步,打开Excel报表,在控制面板页填好今天的起止日期,点“刷新”按钮。如果参数日报页出现了按时间排序的数据行,整个采集、入库、出报的闭环就算完整跑通。报表为空但SQL表里有数据时,排查方向切换到VBA连接串和日期范围,而不是再去Intouch里折腾。
我现在的习惯是:每次给Intouch和SQL Server做完联调,都会强制自己在SSMS查一次表数据、在WindowViewer看一次标记状态、在Excel里刷一次报表,三个动作全走完才敢交付。这套三连验证帮我挡掉了好多返工。如果你也卡在Intouch连SQL Server或者Excel报表拉数据这一步,希望这份笔记帮到你。
本文还有配套的精品资源,点击获取