简介:面向组态王开发者的工程示例文档,用于解决设备状态监控中电机启动时间、停止时间与运行时长的自动记录问题。资源以 docx 格式提供完整示例说明,共 1 个文件,压缩包仅 423KB,便于下载后直接查阅。目前已有 2947 人学习,适合正在使用组态王报表功能做二次开发的工程师参考。文档从新建“报表 10”、创建内存整形变量保存设备所在行,到编写自定义函数,均给出清晰步骤;重点展示了日期字符串补零固定格式、用 HTConvertTime 将开始与结束时间转换为秒、再换算为“x天x时x分x秒”时长的方法。读者可依此思路,快速在自己的组态王工程里实现多台设备运行周期的顺序记录与时长统计,避免逐台重复编写脚本。
1. 组态王电机运行时间记录:把设备保养台账从月底补写变成自动累积
组态王电机运行时间记录,是所有带设备维护压力的上位机项目里最常被提、也最容易做走样的需求。现场不关心画面多漂亮,只想知道这台电机累计转了多少小时、什么时候该换油加脂。靠巡检本记开机时间、月底补 Excel,误差半天起步;用组态王接进 PLC 里现成的运行状态点,按启停事件自动累计运行秒数,写进数据库生成日报月报,半小时把台账从人工追改成系统自动记。这篇笔记讲清信号从哪取、计时逻辑怎么排、掉电怎么不丢、报表怎么出。
适合管维护台账的工程师、被审计追着要记录的车间设备员,还有做上位机集成的调试人员。前提不高——现场只要有一个稳定可用的运行状态信号,不用大改 PLC 主控逻辑。
2. 方案骨架:信号从哪取、计时放哪做、存储归谁管
动手建工程之前,先把三条主线钉死:运行状态信号、累计逻辑、存储位置。这三件事不定,画面做完一半返工是常事。下面按现场最常见的做法逐条拆。
2.1 运行状态信号:三个可靠来源怎么选
电机运行时间记录的第一步是判断“电机到底转没转”。现场能拿到的信号通常有三种。
第一种是 PLC 程序里的运行状态点,这是我最推荐的一种。只要电机回路的控制逻辑在 PLC 里,程序里必然有一个线圈或反馈点表示运行中,组态王直接建一个 IO 离散变量去读它就行。这个信号经过了程序处理,稳定性最好,PLC 侧还能顺带加滤波。缺点就一条:得确认这个点确实反映电机主回路带载运行,而不是把“允许启动”之类的中间信号接进来,否则计时全错。
第二种是接触器的辅助触点或热继电器的常开触点,直接接到组态王所在上位机的 IO 采集模块上。这种方式不依赖 PLC,适合老设备改造、现场 PLC 程序已经固化的场合。但机械触点抖动明显,吸合和释放瞬间会出现多次 0/1 跳变,必须在组态王侧做消抖确认,否则运行记录会被拆成几十段。
第三种是电流变送器输出的模拟量,用 AI 通道采集电流值,以“电流超过阈值”作为运行判据。它能在没有开关量反馈、只有电流互感器的场合兜底,比如一些老式星三角柜。坑也很明显:轻载、空载和堵转电流差距大,一个固定阈值很难通吃,标定了小电机就误判大电机空载,而且启停瞬间的电流尖峰也容易造成误计。
选型建议按这个顺序来:PLC 运行状态点优先,接触器辅助触点次之,电流判运行只作为最后手段。固定阈值判电流时,建议阈值取启动峰值电流的 1.2~1.5 倍,同时在组态王脚本里做持续 3 秒确认,避免启动浪涌误触发。
| 信号来源 | 信号类型 | 优点 | 局限 |
|---|---|---|---|
| PLC 运行状态点 | 离散量 | 现成、稳定、可加程序滤波 | 需要 PLC 程序里有对应点位 |
| 接触器辅助触点进 IO | 离散量 | 不依赖 PLC,改造快 | 机械抖动明显,必须消抖 |
| 电流变送器 AI | 模拟量 | 无开关量时唯一选择 | 阈值难定,空载/轻载易误判 |
2.2 计时逻辑:周期扫描和事件时间戳差在哪
信号定了,接下来是累计逻辑。组态王里有两种做法,误差等级完全不同。
第一种是周期扫描,也叫轮询。在应用程序命令语言里建一个周期脚本,每 500 毫秒或 1 秒执行一次,检测到运行状态为 1 就给累计变量加上一个周期。逻辑极其简单,新手三分钟能写完,但误差和扫描周期同量级。状态翻转发生在周期边界和检测点之间时,这一小段时间就丢了,每一次启停最多丢一个完整周期。现场一天启停几十次甚至上百次的设备,误差能堆到几分钟,做保养提醒够用,做能耗核算和审计就不够看了。
第二种是事件时间戳,也就是先记录启动时刻,再在停止时用当前时间减启动时间,把差值累加。组态王的数据变化命令语言正好能实现:变量从 0 变 1 时记一次时间,从 1 变 0 时算一次时长。这种方式的精度取决于系统时钟,误差通常在秒级,而且启停次数再多也不会叠加丢时。唯一要处理的是跨天:停止时刻的“当日秒数”小于启动时刻时,说明跨过了零点,要补加 86400 秒,否则一次跨天运行会被算成负数。
我一般这样选:只做保养提醒和粗略班报,周期扫描可以接受;要做月度审计、能耗分摊、设备寿命分析,一律用事件时间戳。下面的实现部分全部按事件时间戳来讲,它是这套方案里性价比最高的选择。
2.3 存储分工:内存变量、PLC保持区、数据库的搭配
累计值算出来之后放哪,直接决定掉电后数据还在不在。很多人第一次做的时候把累计值放在组态王内存变量里就交付了,客户重启一次电脑,几个月的数据清零,这是最典型的翻车。
组态王内存变量不保持,运行系统退出或断电后归零或者回到初始值。要可靠记录,至少要把数据落在两个地方。一个是 PLC 的保持型寄存器,另一个是本地关系数据库。前者适合放“累计总秒数”这类单一数值,后者适合放每一段启停的明细记录。
我的推荐组合是:实时累计用内存变量负责运算和画面显示;每 5 分钟把累计值镜像写入 PLC 保持区,组态王启动时读回来;每次电机停止时,把本段运行明细写入数据库,作为日报、月报和审计的最终来源。这样一来,组态王崩溃、电脑断电、PLC 和组态王同时重启,任何情况下都能从 PLC 保持区或数据库里把账找回来。
| 存储位置 | 掉电保持 | 组态王重启恢复 | 用途 |
|---|---|---|---|
| 组态王内存变量 | 不保持 | 不恢复 | 实时显示、运算中间量 |
| PLC 保持区 | 保持 | 启动时读回 | 累计值兜底镜像 |
| 本地数据库 | 保持 | 启动时查询/写入 | 明细台账、报表来源 |
如果没有 PLC 保持区可用,数据库就是最后的保底;再退一步,至少要在组态王停止时把累计值写到本地文本文件,启动时读回。这三个层次的可靠性往下递减,但都比“裸奔”的内存变量强得多。
3. 组态王里的核心实现:变量定义、启停事件脚本、重启恢复
骨架定完,这一章直接把组态王工程建起来。以一台电机 M01 为例,从数据词典开始,到事件脚本,再到重启恢复,按顺序做。
3.1 数据词典里先钉好这九类变量
进工程浏览器的“数据词典”,新建下面这些变量。运行状态是 IO 离散,对应 PLC 点位;其余全是内存变量,负责组态王内部的运算和显示。
| 变量名 | 类型 | 初值 | 说明 |
|---|---|---|---|
| M01_RawRunState | IO离散 | 0 | PLC 读取的原始运行状态,1 为运行 |
| M01_ConfirmedRun | 内存离散 | 0 | 消抖确认后的运行信号 |
| M01_CurrentSeconds | 内存整型 | 0 | 当前时刻换算成的当日秒数 |
| M01_StartMoment | 内存整型 | 0 | 本次启动时的当日秒数 |
| M01_StartDate | 内存字符串 | 空 | 本次启动日期,用于报表 |
| M01_RunSeconds | 内存整型 | 0 | 最近一次运行的秒数 |
| M01_TotalSeconds | 内存整型 | 0 | 累计运行总秒数 |
| M01_TotalHours | 内存实型 | 0 | 累计小时数,由总秒数除以 3600 得到 |
| M01_MirrorSeconds | IO整型 | 0 | 镜像到 PLC 保持区的累计秒数 |
这里有个关键点:M01_TotalHours 之所以定义成实型,是因为累计几百小时后除以 3600 会出现小数,整型会直接截断。M01_RunSeconds 和 M01_TotalSeconds 用整型就够,秒级误差对这个场景完全可接受。M01_ConfirmedRun 是消抖后的信号,所有计时逻辑都绑它,不绑原始 IO 点,这一点在第五章还会展开讲。
3.2 用数据变化事件写启停计时脚本
在工程浏览器里找到“命令语言 → 数据变化命令语言”,新建一条,变量选 M01_ConfirmedRun,分两段写:值由 0 变 1 时执行启动逻辑,值由 1 变 0 时执行停止逻辑。组态王的命令语言语法接近 C,引用变量统一用\本站点\变量名。
启动事件脚本:
// 电机由停止变为运行:记录本次启动的当日秒数 \本站点\M01_CurrentSeconds = \本站点\$时 * 3600 + \本站点\$分 * 60 + \本站点\$秒; \本站点\M01_StartMoment = \本站点\M01_CurrentSeconds; \本站点\M01_StartDate = \本站点\$日期;这段脚本做的事情只有一件:把当前时刻换算成秒并保存。$时、$分、$秒是组态王系统变量,分别代表本机时钟的时、分、秒,换算成当日秒数后,启动和停止之间就可以直接做减法,不涉及日期格式问题。M01_StartDate 单独存一份日期字符串,是为了后面写数据库时能直接按天分组,不用在 SQL 里做时间转换。
停止事件脚本:
// 电机由运行变为停止:结算本段时长并累加 \本站点\M01_CurrentSeconds = \本站点\$时 * 3600 + \本站点\$分 * 60 + \本站点\$秒; \本站点\M01_CrossDayFix = 0; if (\本站点\M01_CurrentSeconds < \本站点\M01_StartMoment) { \本站点\M01_CrossDayFix = 86400; } \本站点\M01_RunSeconds = \本站点\M01_CurrentSeconds + \本站点\M01_CrossDayFix - \本站点\M01_StartMoment; if (\本站点\M01_RunSeconds > 0) { \本站点\M01_TotalSeconds = \本站点\M01_TotalSeconds + \本站点\M01_RunSeconds; \本站点\M01_TotalHours = \本站点\M01_TotalSeconds / 3600; }跨天修正的逻辑在 M01_CrossDayFix 这一句。电机 23:59 启动、00:01 停止时,停止时的当日秒数只有 60,比启动时刻的 86340 小,如果不加修正,本段时长会被算成负的。加上 86400 后就变成 60 + 86400 - 86340 = 120 秒,正好是真实运行时长。M01_RunSeconds 大于 0 才累加,是为了挡住启停状态抖动时可能出现的小于等于 0 的异常值。
提示:这个跨天修正只解决一次启停跨零点的情况。如果一台电机连续几天不停机,这条 M01_RunSeconds 会累计出几天的秒数,单纯靠加减法切不动。现场常在 PLC 侧做每日零点切分,或者在数据库里按停机日归属,后面报表章节会提到。
3.3 重启不丢累计:PLC镜像优先,数据库兜底
事件脚本算出的 M01_TotalSeconds 在内存里,掉电就丢。给它加一个镜像机制。在应用程序命令语言里建一个周期为 1000 毫秒的脚本:
// 周期脚本:每 300 秒把累计值镜像到 PLC 保持区 \本站点\M01_MirrorCounter = \本站点\M01_MirrorCounter + 1; if (\本站点\M01_MirrorCounter >= 300) { \本站点\M01_MirrorCounter = 0; \本站点\M01_MirrorSeconds = \本站点\M01_TotalSeconds; }M01_MirrorSeconds 是 IO 整型,在设备配置里对应 PLC 的保持型寄存器地址。PLC 侧的保持区断电不丢,所以这个镜像值能扛住组态王崩溃和上位机断电。写周期不建议太短,保持型寄存器普遍有写入寿命限制,5 分钟一次足够,极端情况 1 分钟一次也行。
组态王启动时,在应用程序命令语言的“启动时”脚本里把镜像读回来:
// 启动脚本:从 PLC 保持区恢复累计值 \本站点\M01_TotalSeconds = \本站点\M01_MirrorSeconds; \本站点\M01_TotalHours = \本站点\M01_TotalSeconds / 3600;这里有一个细节:IO 变量在组态王启动后会自动读取一次 PLC 值,所以启动脚本里的赋值动作等于把 IO 变量的当前值复制给内存变量。如果现场 PLC 侧改不了程序、没有保持区可用,就把恢复数据的来源换成数据库:启动时查询明细表里这台电机最新一条记录的 total_seconds 字段,赋值给 M01_TotalSeconds。这个查询需要在 SQL 访问管理器里建好绑定关系,脚本写法在下一章结合 SQL 一起说。
4. 把时间记录变成台账:SQL表设计、入库脚本、日报月报导出
累计值只是实时数值,客户真正要的是台账。这一章把每次启停的明细写进数据库,再做成日报月报。
4.1 表结构先定好,ODBC数据源同步建好
先建一张明细表,记录每次从启动到停止的完整信息。下面是一份适配常见关系数据库的建表语句:
CREATE TABLE motor_run_log ( id INTEGER PRIMARY KEY, -- 自增主键,按实际数据库调整 motor_name VARCHAR(32) NOT NULL, -- 电机编号 run_date VARCHAR(20) NULL, -- 启动日期 start_h INTEGER NULL, -- 启动时 start_m INTEGER NULL, -- 启动分 start_s INTEGER NULL, -- 启动秒 end_h INTEGER NULL, -- 停止时 end_m INTEGER NULL, -- 停止分 end_s INTEGER NULL, -- 停止秒 run_seconds INTEGER NULL, -- 本段运行秒数 total_seconds INTEGER NULL -- 本次停止后的累计秒数 );时间字段我特意拆成了时、分、秒三个整型列,而不是存成一个时间字符串。原因是组态王脚本拼接日期时间字符串时常踩格式坑,不同版本对字符串变量和系统变量的处理有差异,存整数列最稳,报表端要展示时自己拼即可。run_date 存启动日期,用于日报按天聚合。id 如果用的数据库不支持INTEGER PRIMARY KEY自增,改成对应数据库的自增写法,比如IDENTITY(1,1)或AUTO_INCREMENT。
表建好后,在 Windows ODBC 数据源管理器里配一个系统 DSN,指向这个库,名字取 MotorDB。组态王里再打开“SQL 访问管理器”,新建一个 SQL 设备,设备 ID 填 0,连接串填DSN=MotorDB;。这一步不做,脚本里的 SQLConnect 就会失败。
4.2 停机瞬间写一条运行明细
在第 3.2 节的停止事件脚本末尾,追加一段写库逻辑:
// 电机停止:结算完成后,把本段运行明细写入数据库 if (\本站点\M01_RunSeconds > 0) { \本站点\M01_EndDate = \本站点\$日期; \本站点\M01_EndHour = \本站点\$时; \本站点\M01_EndMinute = \本站点\$分; \本站点\M01_EndSecond = \本站点\$秒; SQLConnect(DevID, "DSN=MotorDB;"); SQLInsert(DevID, "motor_run_log", "motor_name,run_date,start_h,start_m,start_s,end_h,end_m,end_s,run_seconds,total_seconds"); SQLDisconnect(DevID); }SQLConnect 里的 DevID 就是 SQL 访问管理器里建的设备 ID,写成 0。SQLInsert 的第二个参数是表名,第三个参数是绑定列名列表,列的顺序要和 SQL 访问管理器里配置的绑定表一致。绑定关系在“SQL 访问管理器”里定义:把 motor_run_log 的每一列绑定到一个组态王变量,比如 motor_name 绑到 M01_Name,start_h 绑到 M01_StartHour,这样 SQLInsert 执行时会自动把对应变量的当前值填进 SQL 语句。
这里有个取舍:每停机一次就连接一次数据库,启停频繁的场合会拖慢组态王运行系统。如果电机一天启停几十次以上,我一般改成定时批量写——先在内存变量里存最近一段记录,周期脚本每 5 分钟连一次库,把这期间的所有记录一次性插完。停机事件里只做内存暂存,不再直接 SQLConnect。
注意:SQLInsert 的参数绑定顺序错了不会报明细错误,而是写错列或者写入失败,最容易翻车。配完绑定表后先用一条测试数据插入,再用数据库工具查一遍,确认字段对应正确再投运。
4.3 日报月报:报表窗口 + 定时刷新 + CSV导出
明细在数据库里,报表就好办了。组态王的报表窗口支持绑定变量和历史数据聚合,我通常这么搭。
先建一个日报报表窗口,里面放一个表格区域,每台电机一行,列是当天运行累计小时和启动次数。组态王的报表单元格可以直接填变量,比如把 M01 的累计小时显示在固定格子里,但这不是报表,只是实时值快照。真正的日报要从明细表聚合:用 SQLSelect 查出当天每台电机的 SUM(run_seconds) 和 COUNT(*) 记录数,把结果集绑定到报表或表格控件上。
下面的 SQL 思路可以直接复制进查询数据源测试:
SELECT motor_name, SUM(run_seconds) AS day_seconds, COUNT(*) AS start_count FROM motor_run_log WHERE run_date = '2025-01-08' GROUP BY motor_name;这条语句按电机编号分组,统计某一天的运行总秒数和启动次数。run_date 这里写死成示例日期,组态王脚本里可以把它换成参数,用系统变量$日期动态组装当天日期。月报就是把 WHERE 条件换成日期范围,GROUP BY 不变。
报表的自动刷新用组态王的定时功能:新建一个周期命令语言,到每个整点或者每天 0 点执行一次 SQLSelect,把结果集刷新到报表窗口。导出 CSV 不需要写脚本,报表窗口打开后右键“另存为”,格式选 CSV 或文本,就能拿到 Excel 能直接打开的文件。如果客户要求每天自动生成带日期的文件,可以在报表的保存函数里用变量拼文件名,把$日期带进去。
5. 避坑:电机运行时间记录最常见的五个翻车点
这个功能看似简单,投运后出的问题基本集中在下面五个地方。每条都按现象、原因、解决来写,遇到类似情况可以直接对着排查。
5.1 接触器抖动让累计时间翻倍
现象:明明只开了一次机,台账里却多出好几段几秒钟的小记录,累计时间明显比实际大。原因:接触器触点在吸合和释放瞬间会抖动,PLC 输入点或 IO 端子短时间内 0/1 跳变多次,事件脚本每跳变一次就算一次启停。解决:PLC 侧能加 10~20ms 延时滤波最好,组态王侧也要加消抖确认。把计时事件绑到消抖后的变量上:
// 周期脚本,500ms执行一次:连续3个周期为1才确认运行 if (\本站点\M01_RawRunState == 1) { \本站点\M01_ConfirmCounter = \本站点\M01_ConfirmCounter + 1; if (\本站点\M01_ConfirmCounter >= 3) { \本站点\M01_ConfirmedRun = 1; } } else { \本站点\M01_ConfirmCounter = 0; \本站点\M01_ConfirmedRun = 0; }M01_ConfirmCounter 是计数变量,M01_ConfirmedRun 才是参与计时的信号。原始接触器信号不管怎么抖,计数达不到 3 就不会置位。多台电机要做同样的消抖时,优先在 PLC 侧统一处理,组态王里复制脚本容易漏改变量名。
5.2 组态王重启后累计值归零
现象:运行系统退出重进,或者电脑断电再开机,累计小时数从零开始跳。原因:内存变量不保持,重启后回到初值 0。解决:按第 3.3 节做 PLC 保持区镜像;没有 PLC 保持区就用数据库里最近一条 total_seconds 恢复。这里补一个容易忽略的点:如果启动脚本是在画面加载后才执行,画面上的累计值会先显示 0 再跳成真实值,值班员看到以为数据丢了。把恢复动作放在应用程序命令语言的“启动时”脚本里,并且让画面变量从这个内存变量取数,能避免大部分误解。
5.3 电脑时钟不准,报表时间全乱
现象:明细表里的启停时间比实际早或晚几个小时,跨班统计时日期边界错位。原因:组态王用的是上位机本机时钟,PC 时钟漂移后,所有时间戳跟着偏。解决:条件允许就把 PLC 时钟作为计时基准,事件脚本里的秒数从 PLC 读取;上位机加入 NTP 校时;每天换班时人工对比一次。跨天记录要特别注意归属:一条记录 start_date 和 end_date 不同但 run_seconds 合理,报表里统一归到启动日,并在备注里标“跨天”,不要两头都算或者两头都不算。
5.4 SQL写入失败把整段记录丢掉
现象:数据库服务没启动、ODBC 断连,停机时 SQLInsert 执行失败,这段运行没入账,月底汇总和画面显示对不上。原因:脚本只写一次数据库,没有失败重试和本地缓存。解决:写入逻辑加重试,失败后隔 2 秒再试,最多三次;仍然失败就把本段记录追加到本地 CSV 文件或者临时变量表,等数据库恢复后人工导入。更重要的是设一个“SQL写入异常”报警变量,哪怕只是画面右下角一个红点,也比静默丢数强。累计值计算和 SQL 写入要解耦,SQL 失败绝不影响 M01_TotalSeconds 的累加,否则数据库一抖动,实时显示也跟着出错。
5.5 多台电机变量复制后串值
现象:1 号机的运行时长跑到了 2 号机上,报表里两台电机数据打架。原因:建多台电机时从已有变量复制,脚本里的站点变量名没有逐个改成对应编号,事件绑定也选错了变量。解决:变量命名强制带机器号前缀,比如 M01_RunState、M02_RunState;每台电机的数据变化事件单独建,复制脚本后逐行核对变量名;验收时让 1 号机单独运行 10 分钟,确认 2 号机累计值纹丝不动。这条是投入生产前最值得做的一次回归测试,能筛掉一半的串值问题。
6. 进阶:保养到期提醒、计时精度验证、移植到更多设备
毛坯功能跑通后,往上加两个高价值功能,再花半天做一次精度验证,这套记录系统就真正能顶账用了。
保养到期提醒是最容易见效的增量。在数据词典里加一个M01_RemainHours内存实型,脚本里每次都跟着累计小时更新:M01_RemainHours = 保养周期小时 - M01_TotalHours;然后给这个变量设定一个越下限报警,阈值设成 24 小时。组态王报警窗口会在剩余不到 24 小时时自动弹提醒,画面上的保养状态字段也能按报警状态变颜色。不同电机的保养周期不一样,周期值要么分开存内存变量,要么存数据库里的配置表,千万别图省事所有电机共用一个固定值。
精度验证是交付前必须做的环节。找一块独立的电度表或者机械小时计做参照,让电机连续运行 8 小时,组态王累计误差应该控制在 1% 以内,也就是 288 秒以内。再故意在 23:59 前后做一次启停,验证跨天修正逻辑,确认算出的秒数是真实时长而不是负数。多台电机验收时只动一台设备,观察其他电机变量是否变化。这套验证做完,报表数据才敢让审计看。
我现在做这类项目的习惯是:不管客户只要一个显示还是完整报表,计时核心都尽量放在 PLC 保持区,组态王负责算、存、报这三件事;数据库表一落地,就让客户把 Excel 台账放到一边去。这样踩坑最少,后续维护也不用半夜跑现场改脚本。希望帮到你。
本文还有配套的精品资源,点击获取