接手过好几个用 InTouch 做上位机的现场,几乎每个厂都会提同一个需求:把上个月某个时间段的历史报警和操作员操作记录导出来看一下。这句话听着简单,真去实现的时候,很多人会被 InTouch 自带的历史报警查询方式卡住——要么查不全,要么得登录每台机器分别看,要么干脆就没有操作记录可查。所以后面我自己整理了这么一套可部署的查询控件方案,V4.0 版本把数据采集、数据库归档、Web 查询页面、Excel 导出全部打包封装好,下载解压后按顺序配置三步就能用。这篇实战指南就是围绕这套东西展开的:先说清楚它解决什么痛点,再讲数据链路和表结构怎么设计,然后是部署步骤和查询逻辑,最后把我现场踩过的坑和排查思路也一并交个底。
1. 先搞清楚:InTouch 历史报警为什么查起来这么费劲
很多刚开始接触 InTouch 的人会以为报警记录在系统里是现成的、随时能查的,实际上现场用起来完全不是这么回事。先不急着上方案,我把痛点拆开讲清楚,你就明白为什么需要额外做一个查询控件。
1.1 自带报警查询能力的真实体验
InTouch 本身确实有历史报警记录机制。运行期间产生的报警确认、恢复、报警值变化等信息,可以归档到本地文件或者数据库里。听起来没问题,但实际用的时候有四个让人头疼的地方:
第一,默认情况下的历史报警数据存在每台节点机器的本地文件里。你想想常见场景:中控室一台工程师站、现场两台操作员站,报警发生时 RAM 里能看到实时报警,一旦翻页或重启,想查上一个小时谁触发了什么报警,那可就费劲了。你还得知道报警文件存在哪台机器上,然后跑到那台机器去翻文件。
第二,自带的报警显示和查询界面功能非常基础。它能按时间顺序滚,但你想按变量名、按报警级别、按确认状态做组合筛选,基本做不到。更别提把报警和操作记录放在同一个界面里对照看了。
第三,文件格式不友好。文件归档模式下,想要的数据得先转出来,再导入 Excel 手工处理。值班长每个月想要一份报警统计报表,维护人员得花半天时间“搬砖”。
第四,报警文件和操作记录是割裂的。厂里出了生产事故,最常问的一句话是:“当时操作员点了什么?报警在什么时间触发的?”但两套数据不在一起,追溯的时候只能凭感觉拼时间线。
1.2 操作记录管理为什么几乎是空白
报警好歹还有个历史记录机制,操作记录就真的是空白了。InTouch 本身没有一个“操作记录查询”的面板给你用。谁在哪个画面点了哪个按钮、修改了什么参数、什么时候登录系统,这些信息内置功能基本不带。
现场常见的做法有几种,但都各有问题:
- 在关键按钮的脚本里写 AppendText / WriteFile 等文件写入函数,把操作追加到一个文本文件里。这个能用,但文本文件不便于组合搜索,而且一旦操作频繁,文件会很大,经常得手动清理。
- 直接存到数据库,但如果不做统一设计,今天往 A 表写、明天往 B 表写,到了月底想查询依然是一团乱麻。
- 还有更原始的,安排人员在交接班记录本上手写。这个就不用多说了,追溯性差、可靠性差,也容易漏记。
但现在的工厂管理要求越来越严,不管是内部事故分析、质量管理体系审核,还是客户验厂,都要求提供具有可追溯性的报警与操作记录。光靠 InTouch 自带功能根本招架不住。
1.3 V4.0 查询控件补齐了什么短板
后面我整理的这套控件,核心就做三件事:
- 把散落在各台 InTouch 节点上的报警记录集中收到一个 SQL Server 数据库里;
- 把操作员的关键操作,比如按钮点击、参数修改、登录退出,通过脚本统一写入同一套数据库;
- 提供基于浏览器的查询页面,支持时间、变量名、报警级别、操作人、操作内容等多条件组合筛选,查询结果可以直接导出 Excel。
适用的人群也相对明确:现场维护工程师、自控工程师、生产管理人员,以及做 MES 或者数据采集项目的实施人员。如果你只是在家里用 InTouch 做学习试验,那这个控件偏重了,不需要上数据库和 Web 服务,直接用开发环境自带的功能观察报警就够。
不过需要注意一点:这个控件解决的是“查询、归档、导出”层面的问题,它不替代 InTouch 的实时报警显示。实时报警还是要靠系统自带的 Alarm 显示画面来处理。
2. 数据链路设计:报警和操作记录是怎么进入数据库的
要让查询控件好用,底层数据链路必须先顺清楚。很多项目失败就失败在这:上来就写查询页面,结果报警数据没进库、操作记录漏记,界面再漂亮也白搭。V4.0 的数据链路核心是“采集—写入—存储”三个环节,下面分别展开。
2.1 报警数据落库的两种路径
先说报警数据。InTouch 版本不同、授权不同,现实里能用的落库方式有两种:
第一种是使用 InTouch 自带的历史报警归档配置。在 WindowMaker 里找到报警配置,将报警归档模式设置为数据库或文件输出。如果现场授权里带数据库日志功能,可以让 InTouch 直接把报警写入 SQL Server。优点是系统级采集,报警类型全,不遗漏;缺点是需要授权配合,而且不同版本菜单不一样,找起来费时间。
第二种是脚本转发。用 QuickScript 在报警事件里触发写库逻辑。InTouch 的标记名脚本支持在报警条件成立、报警确认、恢复正常时执行脚本,在这几个点里去写数据库就行。这种方式的好处是可控性强、不依赖额外授权;坏处是如果脚本写得不够严谨,报警量大的时候可能导致性能开销上升,而且历史报警状态变化的组合逻辑容易写漏。
我实际用的思路:优先看一看现场授权能不能开数据库归档,能开就双保险——数据库归档为主,脚本写关键流转状态为辅;授权开不了,就用脚本转发方案。无论哪种,最终落库的表结构是一致的,这样查询页面不用关心数据来源。
2.2 操作记录捕获的三种实现方式对比
操作记录的捕获难度比报警高,因为 InTouch 没有一个“全局钩子”能自动记录每个按钮动作。我梳理下来,有这几种方案可以选择:
| 捕获方式 | 实现思路 | 优点 | 缺点 |
|---|---|---|---|
| 按钮脚本显式记录 | 在关键按钮 Click 脚本里增加写库语句 | 记录内容准确、可附带参数值 | 改造工作量大,容易遗漏 |
| 标记值变化捕获 | 对重要控制标签做值变化脚本 | 无需改动每个画面 | 不知道是谁操作的 |
| 系统消息/审计日志 | 借助 InTouch 消息机制或审计功能,统一转发到数据库 | 覆盖面广、自动化 | 配置复杂、需要额外调试 |
我的方案里推荐“按钮脚本显式记录 + 系统审计转发”组合。关键参数修改、关键动作按钮,用显式记录方式,操作人、时间、画面、动作内容、结果全部写清楚;画面级别的进入离开、登录退出,用审计日志兜底,做到尽量不漏。
2.3 表结构与索引规划
查询快不快、扩展顺不顺,全看表结构设计。V4.0 采用两张主表加一张汇总视图的思路。
报警记录表字段建议:
- RecordID:自增主键
- TagName:报警变量名
- AlarmType:报警类型,比如 HI/LO/HH/LL
- Priority:报警优先级
- State:报警状态,发生/确认/恢复
- OccurTime:发生时间
- AckTime:确认时间
- ReturnTime:恢复时间
- OperatorName:确认人
操作记录表字段建议:
- OperationID:自增主键
- OperatorName:操作员登录名
- OccurTime:操作时间
- ScreenName:画面名称
- ObjectName:操作对象
- ActionType:动作类型,登录/修改/启动/停止
- Description:动作描述
- Result:执行结果
索引规划这块我多说一句。历史报警和操作记录查询的核心条件是时间,因此 OccurTime 字段必须建立索引;如果经常按 TagName 或 OperatorName 来查,建议建联合索引。SQL Server 下我一般这样建:
CREATE INDEX IX_Alarms_OccurTime ON dbo.Alarms(OccurTime); CREATE INDEX IX_Alarms_Tag_Time ON dbo.Alarms(TagName, OccurTime); CREATE INDEX IX_Operations_Operator_Time ON dbo.Operations(OperatorName, OccurTime);另外一定要考虑数据保留策略。我在现场默认按月分区,或者每月执行一次历史归档,把三个月之前的数据搬到历史表。否则这张表常年累积,就算索引建得好,查询还是会变慢。
3. 部署步骤:从解压到查询页面出数据
我打包这个 V4.0 的时候,定的目标就是“下载解压后按文档三步能跑起来”。下面把部署包里的内容和具体配置步骤一起说清楚,照着做就能复现。
3.1 部署包里都有什么
拿到压缩包解压后,会有这么几个目录:
- Database:初始化脚本,包含建库、建表、建索引、建登录账号的 SQL 脚本;
- Service:WCF 数据服务程序,负责向 Web 页面提供查询接口;
- Web:查询网站的发布文件,包含 HTML、CSS、JavaScript 等前端页面;
- Config:数据库连接串配置文件;
- Docs:部署说明和二次开发接口说明。
这个控件不依赖 InTouch 安装目录,只要数据库服务、IIS 和查询网站能跑就行。InTouch 服务器上只需要跑数据写入部分的脚本,查询页面部署在单独一台能访问数据库的机器上就行。
3.2 数据库初始化和连接账号配置
数据库初始化是最容易出错的一步。我先说标准操作:
在 SQL Server 中新建一个数据库,建议命名 IntouchHistory,然后执行 Database 目录下的 Init.sql 脚本。脚本里会自动建表、建索引,还会创建两个数据库账号:一个用于 InTouch 脚本写入数据,只给 insert/update 权限;另一个用于查询网站读取数据,只给 select 权限。
这里有个非常关键的点:数据库的排序规则要选择 Chinese_PRC_CI_AS。如果安装 SQL Server 时选的是默认英文排序规则,后面中文报警描述会出现乱码或者排序异常。我遇到过好几次,改排序规则比重新建库麻烦得多,所以建议大家建库时就先确认。
连接串的配置在 Config 文件里。写入端和查询端是分开配置的,不要共用一个高权限账号。我见过很多工厂项目图省事,直接用 sa 连接,一旦网页被挂在公网或者被内网扫描,安全问题非常严重。
3.3 IIS 站点与 WCF 服务的配置
查询网站发布到 IIS 后,需要配置一个应用程序池,建议使用 .NET Framework 4.0 或以上版本,启用集成托管管道模式。绑定端口现场自己定,我一般习惯用 8090 或者 8080,避免跟 InTouch 自带 Web 服务的 80 端口冲突。
WCF 服务可以独立寄宿,也可以和 Web 网站一起寄宿在同一个 IIS 站点下。我为了减少部署节点,直接把 WCF 服务放进了网站的 Service 目录里,通过 *.svc 地址对外提供接口,省一个进程,现场维护也方便。
配置文件里主要注意三个地方:
- 数据库连接字符串。这个不用多说,但是注意服务器名写 IP 加端口,不要只写机器名,因为工控现场经常有工作组环境,机器名解析不稳定;
- WCF 绑定的超时时间。历史报警查询经常跨月甚至跨年,数据量一大,默认一分钟的超时时间不够,我把 SendTimeout 和 ReceiveTimeout 都调成了 00:10:00;
- 最大接收消息大小。如果一个月的报警数据导出请求,客户端传来的筛选条件本身不大,但是返回值很大,所以 maxReceivedMessageSize 要调大,默认值是 65536,我一般设成 2147483647。
3.4 InTouch 侧脚本如何写入数据库
查询页面的部署是“读”的部分,InTouch 侧还要做“写”的部分。在 WindowMaker 里,需要给报警相关标记名加上报警脚本,并在关键按钮脚本里调用写库函数。
以一个报警状态变化为例,在标记名脚本里找到报警触发点,调用写库存储过程的方法:
// 伪代码示例,实际以部署包脚本为准 InsertAlarmRecord( tagName, alarmType, priority, alarmState, currentTime, currentUser );注意 InTouch 脚本语法和 C# 不同,我们不能直接复制上面的代码,部署包里已经写好了对应的 InTouch QuickScript 函数模板,直接粘贴到脚本编辑器里改参数即可。
还有一点:InTouch 脚本在报警量特别大的瞬间可能会排队,建议写库逻辑采用异步方式,不要把写库阻塞报警主流程。如果 InTouch 内置脚本不好做异步,就在数据库端加一个消息队列表,脚本只负责往队列表插数据,后台作业再定期把队列表的数据转移到主表。
4. 查询页面的功能逻辑:每个筛选条件背后是怎么实现的
部署完成后就是实际使用环节。很多人用查询页面只是为了“能出数据”,但遇到筛选边界不对、导出卡死、中文乱码这些问题就不知道怎么办了。这节我把查询页面的几个核心功能逻辑拆开讲,顺便把容易出问题的地方标出来。
4.1 时间范围筛选为什么用“闭开区间”
时间筛选是历史报警查询最常用的条件。刚开始我做这个功能的时候,用的是 between 写法,后来发现一个典型的丢数据问题。
假设某条报警发生在 2024-11-18 08:00:00.000,用户选择查询区间是“从 2024-11-18 08:00:00 到 2024-11-18 18:00:00”。如果用 between @StartTime and @EndTime,毫秒精度下经常会出现边界记录查不到,或者查询结果里多出边界时刻重复数据的情况。
后面我统一改成“闭开区间”写法:
SELECT * FROM dbo.Alarms WHERE OccurTime >= @StartTime AND OccurTime < @EndTime;界面上的“结束时间”控件,我会在 SQL 层面对结束时间自动加一天或加一秒处理。这样做的好处是语义清晰,查询结果不会因为毫秒级边界产生漏数据。
4.2 报警级别、变量名、关键词的组合查询
V4.0 查询页面的左侧是条件区,包含时间、变量名关键字、报警级别、操作类型、操作人等;右侧是结果表格和统计条。
变量名筛选这个坑需要说一下。用户经常输入“液位”两个字,希望在报警记录里模糊匹配所有跟液位有关的变量。但 SQL 里 LIKE ‘%液位%’ 这种写法,由于前导通配符的存在,数据库无法走索引,数据量大了以后查询会非常慢。
我的处理方式是在界面上给两种模式:模糊匹配和精确匹配。模糊匹配时,程序会把变量名关键字转成多个并列条件,并限制查询时间范围;精确匹配则走索引,性能高得多。同时后台对变量名列单独建了全文索引,真正需要跨海量数据搜索的时候可以启用全文检索模式。
4.3 报警和操作记录联动查看的时间轴方式
纯粹把报警或者操作记录单独列出来,其实价值有限。最有价值的是把报警和操作记录放在同一条时间线上对比:某个参数开始波动,然后报警触发,然后操作员修改了设定值,再然后报警恢复。这一整个链条才是事故分析时最需要的东西。
V4.0 在详情页面里支持“同步对比模式”。前端发一个请求,后端同时查报警表和操作表,按时间字段合并排序,渲染成时间线。实现起来不复杂,核心是两个查询结果合并后统一排序:
var alarms = GetAlarms(startTime, endTime); var operations = GetOperations(startTime, endTime); var timeline = alarms.Cast<TimelineItem>() .Concat(operations.Cast<TimelineItem>()) .OrderBy(item => item.OccurTime) .ToList();这种模式特别适合交接班追溯。交接班会议直接投屏打开时间线,谁动了什么参数、哪个报警先触发的,一目了然,不用再对着两张表来回翻。
4.4 导出 Excel 的大数据量处理
用户导出一周甚至一个月的报警记录,单量经常在几万到几十万行。如果做成同步导出,浏览器基本会卡死。所以导出功能我改成了“异步任务”模式:
- 用户在页面点击导出,写入一条导出任务记录;
- 后台服务轮询任务表,发现新任务后执行查询并生成 Excel 文件;
- 生成完成后把下载地址更新到任务记录里,前端轮询到任务状态为“完成”,弹出下载按钮。
这样用户体验会好很多,不会一把查询卡死整个页面。
生成 Excel 时有个细节:历史报警里经常有类似“012345678901”这种长数字设备编码,如果直接用常规单元格格式导出,Excel 会把它变成科学计数法,看着完全不是原来的值。解决方式是把这类列显式设置为文本格式,或者在导出逻辑里将单元格类型设为 Text。这个坑我踩过很多次,导出的报警确认人编号、工单编号如果被转成科学计数法,后面的数据核对就会非常痛苦。
5. 现场实战:几个高频问题及其完整排查链路
工具用得久了,各种环境现场都会来一遍。下面这四个问题是我在多个项目实战里遇到的最高频故障,每个都给出完整的排查链路,方便你照着复现排查思路。
5.1 查询界面显示时间和实际时间相差 8 小时
症状:报警实际发生在下午两点,查询页面显示早上六点,或者反过来。
这个问题很多人第一反应是“数据库时间不对”,但其实大多不是数据库的问题。完整排查链路如下:
第一步,用数据库管理工具直接查报警表里的 OccurTime,看原始数据是否正确。如果原始数据正确,说明问题出在 Web 服务或前端显示层。
第二步,检查 Web 服务和数据库服务器时区是否一致。如果 Web 服务部署的机器时区是 UTC 标准时间,而数据库是本地时间,查询接口取出来以后会产生偏移。
第三步,检查前端 JavaScript 的日期格式化逻辑。我最初版本里为了统一显示,用了new Date(value).toLocaleString(),这个方法会按浏览器本机时区做转换。如果浏览器机器时区不对,显示就跟着错。
处理方式:数据库统一以本地时间存储,Web 服务返回原始时间字符串,前端不额外转换;或者数据库统一存 UTC,前端统一转本地。无论哪种,全链路只认一种时间口径,不要中间任意转换。
5.2 查询越来越慢,数据量大了以后索引失效
症状:刚部署时查询很流畅,运行三个月后,查一个月的数据要十几秒甚至更久。
排查顺序:
先看数据库索引碎片情况。频繁的插入和删除会让索引碎片率上升,ALTER INDEX REORGANIZE和ALTER INDEX REBUILD定期处理就能恢复大部分性能。
再看执行计划。如果查询 SQL 里用了前导通配符的 LIKE 条件,索引失效是必然的。V4.0 里对这类条件会自动换用全文索引,但如果你改了查询 SQL,需要注意这一点。
再看分页方式。数据量大以后,传统的 OFFSET 分页会越来越慢,因为数据库要把前面所有行都扫一遍再跳过。这种时候换成基于主键的游标分页,比如WHERE RecordID < @LastID ORDER BY RecordID DESC,性能会稳定很多。
最后,如果前面都排除了,那就看表里有没有归档任务在跑。如果归档任务和查询任务在同一时间并发,会产生资源争用。归档时间建议避开白班高峰,选凌晨两三点执行。
5.3 报警描述中文乱码
症状:报警变量名和描述里的中文变成问号或者乱码。
诊断顺序是:先看数据库里存的原始数据是否正常。如果数据库里存进去就已经乱码,说明问题出在 InTouch 脚本写入那一步;如果数据库正常,说明问题出在查询服务返回和前端展示。
InTouch 脚本写库乱码,常见原因是数据库表字段类型用成了 varchar 而不是 nvarchar。varchar 默认按数据库排序规则编码,而 InTouch 脚本传过来的中文字符串按 Unicode 处理,落库时转换不对就变成乱码。解决方案很直接:所有可能存中文的字段全部用 nvarchar 类型,连接字符串里加Unicode=true;Character Set=UTF8这种类似的参数。
另外提醒一下,数据库端如果已经建成 varchar 字段且里面有乱码,中途修改字段类型并不能修复已存在的乱码,需要把数据重写一遍。所以设计表结构时就要定好 nvarchar,这个返工成本很高。
5.4 查询页面能打开,但查出来是空白
症状:页面正常显示,时间条件也选了,点击查询按钮,表格里没有数据。
排查链路我总结成了一个标准化顺序,现场照着走,基本五分钟定位:
- 用数据库查询工具直接查报警表,确认当前时间范围内有没有数据。如果没数据,怀疑 InTouch 侧写库部分,去操作一台测试变量,看队列表是否新增记录。
- 如果数据库里有数据但页面空白,检查查询网站对应服务账号的数据库权限。我遇到过把查询账号误建成只读账号,但只读账号没有对某一张新表的 SELECT 权限,导致查询接口抛异常。
- 检查 Web 服务日志和 WCF 日志。如果数据库查询异常,日志里会有明显报错;如果是空结果集,日志不会有异常,那就要检查前端传参格式。
- 检查前端时间控件格式。尤其 IE 兼容模式下,时间控件解析
2024-11-18 08:00:00可能出问题,导致传来的是NaN或者空字符串。 - 最后查一下页面是否有锁表。历史表在归档作业执行期间如果长时间持有锁,查询会被阻塞,表现为页面一直转圈但查不出数。
这个排查链路看着简单,但熟练之后能省下大量沟通成本。我有一次在客户现场排查了近两小时,最后发现是前端一台客户机的 IE 浏览器缓存了旧版时间控件,升了个级就好了。
6. 关于授权、版本兼容和部署边界的一些务实提醒
最后这部分是我在交付过程中被认为最有价值的内容,都是文档里不写的东西:怎么结合现场实际情况去调整方案边界,以及如何避免把工具用歪。
6.1 InTouch 授权路径配置对报警归档的影响
网上搜 InTouch 相关问题,很大比例是在问授权路径怎么设置。我强调一次:授权路径配置不正确,可能导致报警归档服务启不来,进而影响历史报警写入。
排查报警没有正常归档时,先看一眼授权路径是否正确指向授权文件所在目录,这一步很多人会漏。但需要说明的是,我这边控件本身不处理授权问题,也不涉及授权文件的生成和修改,它只依赖 InTouch 运行环境和合理的授权路径配置。如果现场 InTouch 自己起不来或者报警功能不完整,优先检查原厂授权,而不是先怀疑查询控件。
6.2 版本兼容和操作系统层面的兼容清单
V4.0 客户端查询页面基于浏览器访问,所以操作系统和浏览器兼容性比较友好。建议配置清单如下:
| 组件 | 建议配置 | 说明 |
|---|---|---|
| InTouch 版本 | 2012 R2 及以上 | 已测试到 2023 版本 |
| 数据库 | SQL Server 2012/2016/2019/2022 | 不建议用 Express 版存历史数据 |
| 查询服务 | Windows Server 2016/2019 | 工控机也可部署 |
| 浏览器 | Chrome 90+ / Edge | 不建议老版 IE 兼容模式 |
如果现场还在用 Windows 7 专业版工控机,需要注意 IIS 版本和 TLS 兼容问题。Win7 自带 IIS 7.5,老版本对 HTTPS 证书和现代加密协议支持不够好,建议查询服务单独部署在 Server 系统上,InTouch 那台 Win7 只负责脚本写库。工控机多数处于隔离网段,不依赖互联网,一次性部署就能长期使用,这点也是很多传统工厂愿意用它的原因。
6.3 从查询工具到追溯闭环的进一步扩展
V4.0 已经解决了“查得到、导得出”的问题,但后面如果工厂管理要求再往上走,这个架构支撑起来也不难。常见扩展方向有三个:
一是报警统计分析报表。在现有 Alarms 表基础上,按日、按班次、按报警类型做聚合统计,可以给生产部门做月度设备可靠性分析。这个不需要改现场采集,只需要新增报表页面对接同一张表。
二是操作行为审计。把 Operations 表里的操作人、动作类型、结果状态按时间维度汇总,能直接支撑质量管理体系里的“人员操作可追溯”要求。配合操作员账号管理,还能排查出哪些人有频繁的未被授权的修改动作。
三是与 MES/ERP 的上层系统对接。因为数据已经结构化存在 SQL Server 里,上层系统通过标准数据库接口或者 Web API 就能取数,不需要再去打扰 InTouch 运行时。
我在实际使用中的一个体会是:工控软件领域,很多时候不是功能越花哨越好,而是要把最基础的数据链路打通、把最容易出错的细节处理干净。这一版 V4.0 从初次部署到现在,真正花功夫的地方不在于查询页面本身,而在于打磨数据写入的稳定性、边界时间的处理、大数据量下的导出体验。如果你现场也遇到同类问题,建议不要一上来就追求大而全的平台化系统,先把历史报警和操作记录这两样核心数据收拢好、查询顺,后面再往上报表、统计、对接,都会轻松很多。