ARTICLE DETAIL

资讯详情

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

KingSCADA连接外部数据库与报表系统实现:数据落库与脚本实战

KingSCADA连接外部数据库与报表系统实现:数据落库与脚本实战 做工业组态的人应该都有同感画面上的数据和曲线只是给人看的真正给客户创造价值的是这些数据能不能变成报表、能不能被统计、能不能被其他系统调用。我最近刚完成的一个项目就是这个典型场景——车间里十几台设备的数据通过 KingSCADA 采集上来了客户要求每 5 秒把关键工艺参数写进外部数据库再按班次、按天生成产量和运行时长报表。整个过程绕了不少弯也踩了不少坑今天就把“KingSCADA 链接外部数据库处理以及报表系统”这条完整链路拆开讲一遍重点放在数据记录插入数据库的脚本实现方法上给后面做同类项目的朋友一个可以直接抄的作业。这个案例不复杂但很典型采集端是 PLC上位机用 KingSCADA 组态数据库用 SQL Server报表先通过 KingSCADA 自带的报表功能做了一版后期又接入了更灵活的第三方报表工具。整个项目的核心难点不在画面组态而在三件事外部数据库怎么连、数据怎么稳定地写进去、报表怎么随查随有。下面我从头到尾捋一遍顺便把现场踩过的坑也一并发出来。1. 项目背景与整体设计思路1.1 为什么必须上外部数据库很多刚开始用组态软件的人会有一个错觉KingSCADA 自己不是有历史库和报警记录吗为什么还要费劲去连外部数据库有这个疑问很正常因为在演示环境里几十个变量的历史数据用自带库完全够用。但到了实际生产项目自带库就顶不住了。原因有三条第一自带历史库的数据格式不透明客户想用 Excel 或者 ERP、MES 去读这些数据基本不可能。任何第三方系统要对接都希望数据在 SQL Server、Oracle、MySQL 这种标准数据库里。第二自带历史库的查询能力有限。客户最常见的需求是“把上个月 3 号白班的产量统计出来”这种带条件和聚合的查询在自带历史库里操作起来很别扭。第三稳定性和容量问题。现场设备一多数据量大起来自带库在长期运行时可能会出现文件膨胀、历史区间覆盖等情况。放到外部数据库以后备份、归档、清理都交给专业的数据库去处理心里踏实得多。所以只要项目里有“报表”“对接其他系统”“数据二次利用”这些字眼我的建议都是尽快把数据落到外部数据库里。这也是我这个项目一开始就确定的架构方向。1.2 这套系统的数据流怎么设计这个项目的现场情况是车间里有 14 台注塑机每台设备有一个 PLCPLC 通过以太网把压力、温度、运行状态等信号送到 KingSCADA。KingSCADA 里的变量分为两种I/O 变量直接对应 PLC 的寄存器内存变量则在组态内部参与计算和逻辑处理。整个系统的数据流可以概括成一条线PLC 采集 → KingSCADA 变量刷新 → 脚本按周期读取变量 → 拼接/绑定 → 写入 SQL Server 数据库 → 报表系统查询展示我特意把“脚本按周期读取变量”这个环节放在中间是因为这是整个链路里最容易出问题的地方。数据采不上来可以查网络和驱动数据库连不上可以查 ODBC 和防火墙。但数据能不能稳定地、不重复地、不错乱地写进数据库完全取决于脚本怎么写。后面第 3 章会详细展开。报表系统放在数据库之后实际上已经和 KingSCADA 解耦了。报表查询的是数据库里的历史记录而不是直接读组态变量。这样做的好处很明显哪怕 KingSCADA 停机了报表依然能从数据库里查出历史数据这对于生产追溯非常重要。1.3 数据库选型与对接方式选择数据库选型上这个项目用的 SQL Server 2008 R2。选它不是因为性能多强而是客户现场的 IT 环境就是 Windows机房已经有 SQL Server 的运维经验。如果让我自己选其实 SQL Server、MySQL 都可以关键是看现场谁在维护。在对接方式上有两类选择一类是标准数据库访问接口比如 ODBC、OLEDB、ADO。KingSCADA 自己的控件和脚本函数大多走 ODBC所以配置一个系统 DSN 是最稳妥的做法。另一类是直接写 TCP/IP 或者 REST API 去操作某个中间服务让中间服务再去访问数据库。这种方式适合数据量巨大、需要复杂业务逻辑的情况但这个项目用不上反而会把链路拉长。所以在设计阶段我直接定了 ODBC 直连 SQL Server 的方案。后面发现ODBC 方案最需要小心的就是 32 位和 64 位的问题这个在第 5 章会重点讲。2. 外部数据库链接配置详解2.1 先把 ODBC 数据源配好KingSCADA 要连外部数据库第一步不是写脚本而是把 ODBC 数据源配好。这一步看着简单实际最容易踩坑。我这里说的“配置 ODBC”不是直接打开系统里的 ODBC 数据源管理器就可以的。KingSCADA 是 32 位程序在 64 位 Windows 上如果你用默认的 64 位 ODBC 管理器控制面板里的“ODBC 数据源”建出来的系统 DSN 是 64 位的32 位的 KingSCADA 根本认不到。正确做法是打开 32 位 ODBC 管理器路径在C:\Windows\SysWOW64\odbcad32.exe在“系统 DSN”页签下添加一个新的 SQL Server 数据源填写服务器地址。这里有两个细节第一服务器名要写对。如果是本机数据库可以写“localhost”也可以写“服务器名\实例名”。如果有命名实例千万不要漏掉实例名。第二推荐使用“SQL Server Native Client 11.0”或“ODBC Driver 17 for SQL Server”驱动不要用老掉牙的“SQL Server”驱动否则容易出现中文乱码和兼容性问题。配置完成后一定要点“测试数据源”确认连接成功再往下走。很多脚本连不上数据库的问题其实在这一步就已经能发现了。2.2 KingSCADA 里连接数据库的两种路子DSN 配好以后KingSCADA 里连接数据库一般有两种路子。一种是通过 SQL 访问管理器这是组态软件推荐的方式。在开发环境中找到“SQL 访问管理器”先配置记录体把数据库表的字段和组态变量绑定起来然后通过脚本里的 SQLConnect、SQLInsert 这些函数来操作。这种方式的好处是字段对应关系可视化不容易把字段名弄错。另一种是用通用数据库控件在画面上放一个数据表格控件把控件的数据源直接指到 ODBC DSN然后通过 SQL 查询语句把结果显示在画面上。这种方式适合做查询界面但不太适合高频写入。我的习惯是高频写入用脚本 记录体查询展示用控件 SQL。这套组合在项目里跑得很稳。2.3 报表控件的数据源配置项目里的报表系统一开始用 KingSCADA 自带的报表控件。在画面上插入报表控件以后右键属性里可以设置数据源。报表控件的数据源配置和数据库连接类似本质上还是通过 ODBC 走到后台的 SQL Server。我的建议是在配置阶段先接一个最简单的测试用表确认控件能查出数据再去接真正的业务表。这样能快速区分问题是出在数据源配置上还是出在 SQL 语句上。如果后期要用帆软、FastReport 这类第三方报表工具那就更简单了直接在报表工具里新建数据库连接填上 SQL Server 地址、账号、密码之后所有报表数据查询都跟 KingSCADA 没关系了。说白了只要你把数据规规矩矩地写进了数据库报表工具的选型空间就大了。3. 数据记录插入数据库的脚本实现方法这章是整个项目的核心也是标题里最让我有表达欲的部分。数据记录插入数据库说白了就是一个很常规的数据库 INSERT 操作但在组态软件里做这件事跟写普通后端程序还不太一样。3.1 先搞明白组态软件的脚本机制KingSCADA 的脚本语言风格接近 C 和 VBScript 的混合体支持变量、分支、循环、函数调用也提供了一批内置的 SQL 访问函数。在写脚本之前一定要搞清楚触发方式。我这次用的是周期触发脚本——意思是脚本每隔固定时间自动执行一次不管画面当前在哪个页面。这种触发方式最适合“定时落库”的场景因为数据写入不能依赖操作员去按按钮。周期脚本的间隔可以设置我现场用的是 5 秒。注意频率不是越高越好具体怎么定我在第 3.4 节会细说。3.2 方法一记录体绑定 SQLInsert第一种实现方法是通过记录体绑定把组态变量和数据库表的字段一一对应然后调用 SQLInsert 函数插入。在 SQL 访问管理器里我先建一个记录体名称叫“rec_dev_data”绑定的表是“dev_data”然后做字段映射数据库字段组态变量dev_no\本站点\设备编号pressure\本站点\压力temperature\本站点\温度tag_time\本站点\当前时间然后在周期脚本里写这样一段int sql_handle; sql_handle SQLConnect(kingdb); if (sql_handle 0) { SQLSetField(sql_handle, dev_no, \\本站点\设备编号); SQLSetField(sql_handle, pressure, \\本站点\压力); SQLSetField(sql_handle, temperature, \\本站点\温度); SQLSetField(sql_handle, tag_time, \\本站点\当前时间); SQLInsert(sql_handle, dev_data); SQLDisconnect(sql_handle); }这段代码的逻辑很清晰先用 SQLConnect 连接 DSN 名为 kingdb 的数据源连接成功以后通过 SQLSetField 把每个字段要写入的值传进去然后 SQLInsert 真正执行插入最后断开连接。这里有一个非常关键的细节SQLConnect 的返回值一定要判断。如果返回 -1说明连接失败这时候再去执行后面的 SQLSetField、SQLInsert 都没有意义而且会在组态软件的输出窗口刷出一堆错误信息。我项目里最开始就吃过这个亏脚本一直报错后来加了这个判断问题立刻清楚了。3.3 方法二SQLExec 直接拼接 INSERT第二种方法更灵活也更“程序员思维”不通过记录体而是直接把一条 INSERT 语句拼好交给 SQLExec 执行。同样实现每 5 秒写入一条数据代码是这样int sql_handle; string sql_str; sql_handle SQLConnect(kingdb); if (sql_handle 0) { sql_str INSERT INTO dev_data (dev_no, pressure, temperature, tag_time) VALUES (; sql_str sql_str StrFromInt(\\本站点\设备编号, 10); sql_str sql_str , StrFromReal(\\本站点\压力, 2); sql_str sql_str , StrFromReal(\\本站点\温度, 2); sql_str sql_str , GETDATE()); SQLExec(sql_handle, sql_str); SQLDisconnect(sql_handle); }注意这里我用了StrFromReal和StrFromInt把数值型变量转成字符串再拼进 SQL 语句里。这是组态脚本里最常见的写法之一。如果不做转换直接把数值变量放进去很多时候会得到意外的结果。另外注意到这条 INSERT 语句没有拼时间字段进去而是直接调用了数据库的GETDATE()函数。这是一个小技巧让数据库自己生成写入时间避免了你花时间去格式化系统时间字符串。因为组态软件里的时间变量类型五花八门拼字符串很容易出现“2025-01-01 12:00:00”带不带毫秒、带不带引号这类问题。我建议只要业务允许时间字段一律让数据库生成。3.4 定时触发让脚本按周期落库无论是方法一还是方法二都只是“执行一次”的逻辑。真正的“每 5 秒自动执行”要依赖组态软件里的周期脚本或数据变化脚本。我的实现是在 KingSCADA 开发环境的“应用程序命令语言”或“工程脚本”里新建一个周期脚本把上面的代码贴进去然后把执行周期设为 5000 毫秒。这里要说一下频率选择的问题。很多人一上来就把写入频率设成 1 秒甚至更短觉得数据越密集越好。实际上大多数生产报表根本用不到秒级数据。像注塑机的压力、温度变化没那么剧烈5 秒一条已经足够。频率过高会带来三个问题数据库压力大、磁盘 I/O 高、历史数据冗余严重。所以设置周期前先问客户一句话报表上需要精确到什么粒度如果只做班报、日报10 秒甚至 30 秒一条都够用。如果后续要做曲线回溯再考虑把关键变量的写入频率提高。我一直建议的做法是分级存储核心变量高频记录普通变量低频记录这样数据库不会爆。3.5 脚本的调试与运行日志组态软件里的脚本调试跟写普通程序的体验完全没法比没有断点没有单步也没有变量监视。因为它的运行环境是组态软件的进程你要是把画面关了脚本还在不在跑都得打个问号。我的经验是一定要自己做日志。在脚本里把关键节点的信息写到一个文本文件里每次连接数据库返回什么值、执行完插入后的返回码是多少都记录下来。一旦现场出问题看日志就比看画面上的状态灯可靠得多。代码可以这么写int log_fp; string log_msg; log_fp FileOpen(C:\\kingscada_log\\db_log.txt, 1); if (log_fp 0) { if (sql_handle 0) log_msg connect ok, handle StrFromInt(sql_handle, 10); else log_msg connect failed!; FileWriteLine(log_fp, log_msg); FileClose(log_fp); }日志文件一定要按日期拆否则时间一长文件会非常大。我习惯在文件名的位置用系统日期变量拼一个带日期的文件路径这样每天一个文件方便排查。4. 报表系统的落地与数据查询4.1 报表需求拆解从数据库查询开始数据进了数据库后面的报表就是纯业务的事了。做报表前我习惯先把需求拆成几个可以直接用 SQL 回答的问题而不是急着去拖控件。这个项目客户的需求看起来复杂拆完之后就是三个问题第一每个班次产量是多少第二每台设备运行了多少小时第三一天的报警次数有多少把这几个问题翻译成 SQL其实就是带条件的分组统计。比如班次产量可能对应这样一条语句SELECT dev_no, SUM(output_count) AS total_output, CONVERT(varchar(10), tag_time, 120) AS work_date FROM dev_data WHERE CONVERT(varchar(10), tag_time, 120) 2025-01-01 GROUP BY dev_no, CONVERT(varchar(10), tag_time, 120)这条语句的逻辑是从 dev_data 表里找到指定日期的记录按设备分组累加每个设备的产量。这就是最基础的日报表数据来源。4.2 用自带的报表控件展示数据有了 SQL 查询结果第一版的展示我用了 KingSCADA 自带的报表控件。在画面上放一个报表控件然后在脚本里执行 SQLSelect把查询结果绑定到控件上。注意 SQLSelect 和前面写的 SQLExec 不是一回事。SQLExec 是执行增删改不返回结果集SQLSelect 是执行查询返回一个结果集需要用 SQLGetField 之类的函数把数据一行行取出来再填到报表控件里。这一步的坑在于拿到查询结果以后报表控件有可能不自动刷新。解决方法是执行完查询后强制调用报表控件的刷新方法或者在脚本里先清空控件里的旧数据再填充新数据。4.3 常用报表统计 SQL 写法报表做多了以后我发现下面几类 SQL 是最常用的建议直接收藏。按小时统计平均值SELECT DATEADD(hour, DATEDIFF(hour, 0, tag_time), 0) AS hour_slot, AVG(pressure) AS avg_pressure FROM dev_data GROUP BY DATEADD(hour, DATEDIFF(hour, 0, tag_time), 0)按设备统计累计运行时长假设运行状态字段为 1 表示运行中SELECT dev_no, SUM(CASE WHEN run_status 1 THEN 1 ELSE 0 END) * 5 / 3600.0 AS run_hours FROM dev_data WHERE tag_time 2025-01-01 AND tag_time 2025-01-02 GROUP BY dev_no这里的 * 5 表示每 5 秒一条记录如果写入间隔变了这个系数也要跟着改。这就是为什么我强调写入频率一定不能随便改——报表里的统计逻辑是依赖写入周期的。4.4 面向智能报表系统的扩展思路数据库里的数据稳定了之后能玩的花样就多了。最近 GitHub 上出现了一批通过自然语言描述生成报表的开源智能报表系统比如一些 ChatBI 项目Dify 这类平台也支持把外部结构化数据导入存储到数据库再通过对话的方式做数据问答。对 KingSCADA 项目来说这意味着一个很现实的扩展方向把已经落库的工艺数据接入这些智能报表工具让业务人员直接用自然语言问“昨天 A 线注塑机的平均压力是多少”系统自动生成 SQL 并返回结果。当然这个扩展目前在实际工厂里还谈不上大规模落地因为涉及数据安全、权限控制和 LLM 的准确性但它确实是一条技术趋势。从组态软件的角度看我们只要保证外部数据库结构清晰、字段标准化、数据质量可靠将来无论接什么智能报表系统都不会有底层障碍。5. 常见问题与排查技巧实录5.1 SQLConnect 一直返回 -1这是出现频率最高的问题。排查顺序可以按下面这张表来检查项位置/方法常见原因DSN 是否存在32 位 ODBC 管理器建到 64 位 ODBC 里了DSN 名称是否正确脚本里的 SQLConnect 参数名称拼写不一致数据库服务是否启动服务管理器重启机器后没启动防火墙是否放行 1433 端口防火墙入站规则默认没放行SQL Server 是否允许远程连接SQL Server 实例属性未启用 TCP/IP 协议我调试最多的是 32/64 位问题。明明在“控制面板”里能看到 DSN测试也通过但 KingSCADA 就是连不上。最后发现控制面板打开的是 64 位 ODBC 管理器而 KingSCADA 认的是 32 位。所以每次排查连接问题第一步就是先确认 DSN 是在哪个管理器里建的。5.2 中文写入变问号另一个典型的坑是中文乱码。现场设备名称、操作员姓名难免有中文写入数据库以后变成了“????”或者乱码原因大多出在三个方面。第一数据库表字段是 varchar不是 nvarchar。varchar 只能存 ASCII 字符中文必须用 nvarchar。所以建表的时候凡是可能存中文的字段一律用 nvarchar。第二ODBC 驱动太老。老的“SQL Server”驱动对汉字兼容性不好建议换成“ODBC Driver 17 for SQL Server”或“SQL Server Native Client 11.0”。第三INSERT 语句里的中文字符串要加 N 前缀。如果通过 SQLExec 拼 SQL 语句中文字符串要写成N中文告诉 SQL Server 这是 Unicode 字符串能避免很多乱码问题。5.3 数据写入慢、数据库 CPU 飙升系统跑了几天后数据库 CPU 开始飙升排查下来发现原因有二一是写入频率太高二是单条单条地插入产生了大量的磁盘日志和锁竞争。解决办法主要有三种。降低写入频率。把无关紧要的变量从周期落库里移除只保留报表和追溯真的需要的变量。批量插入。在脚本里累积 10 条或者 20 条记录拼接成一条多值 INSERT 语句再一次执行。SQL Server 支持INSERT INTO ... VALUES (...), (...), (...)这种批量写法执行效率高得多。适当使用事务。把多条插入包在一个事务里减少日志刷盘次数。但事务不能包太多条否则锁的时间太长反而影响其他查询。5.4 报表显示数据不刷新画面上的报表控件查了一次以后第二次查询发现数据还是老样子。这个问题的原因是报表控件没有刷新或重新绑定数据源。解决办法是在每次查询前先调用报表控件的清空方法把旧内容清掉再执行 SQLSelect 并重新填充数据。如果用的动态表还要注意表格结构会不会变化字段名不一致也会导致数据不显示。5.5 时间字段不对差了 8 小时这类问题在多个项目里都能遇到。表现是页面上显示的当前时间是正常的但写进数据库的时间比实际少了 8 小时。原因多半是 ODBC 驱动和数据库之间的时区设置不一致或者组态软件内部使用的时间格式和 SQL Server 默认格式不同。我的建议像前面说的那样在 INSERT 语句里直接用GETDATE()让数据库按服务器本地时间写入基本能从根源上绕开时区问题。6. 实操心得与后续扩展方向6.1 几个让我印象深刻的坑整个项目做下来我最想提醒大家的是三件事。第一数据表结构一定要先设计好再动脚本。这个项目前期因为表字段类型定得不对导致后面脚本改了好几次。设备编号用 int 还是 string时间字段用 datetime 还是 varchar压力值保留几位小数都要在建表前想清楚不然后期返工成本很高。第二脚本里一定要有错误判断和日志。组态软件不像后端程序那样有完善的日志框架你只能自己动手。哪怕就是简单的“连接失败写一行文件”也能在排查问题时节省大量时间。第三不要把写入频率当成唯一的性能优化手段。频率设得低固然能减轻压力但报表的统计逻辑也是依赖周期的。一旦改了频率报表里的运行时长、累计值这些统计结果就全变了必须同步调整 SQL 里的系数。所以频率定下来以后尽量不要动要好写在注释里。6.2 后续还能怎么做这个项目的收尾阶段我还做了两件事。一是把数据库历史数据做归档超过 3 个月的数据按月份迁移到归档表主表只保留近期数据报表查询速度快了不少。二是接了一个开源报表工具的试用版直接把 KingSCADA 写入的数据作为数据源在网页上做报表展示客户反馈比单机版报表灵活得多。如果你也在做类似的项目我建议把眼光放远一点数据落库不是终点而是起点。外部的智能报表系统、移动端数据看板、MES 数据接口都在等着消费这些数据。只要数据库结构设计得规范后续的扩展会很顺畅。
返回列表