简介面向组态王开发者的工程示例文档用于解决设备状态监控中电机启动时间、停止时间与运行时长的自动记录问题。资源以 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_RawRunStateIO离散0PLC 读取的原始运行状态1 为运行M01_ConfirmedRun内存离散0消抖确认后的运行信号M01_CurrentSeconds内存整型0当前时刻换算成的当日秒数M01_StartMoment内存整型0本次启动时的当日秒数M01_StartDate内存字符串空本次启动日期用于报表M01_RunSeconds内存整型0最近一次运行的秒数M01_TotalSeconds内存整型0累计运行总秒数M01_TotalHours内存实型0累计小时数由总秒数除以 3600 得到M01_MirrorSecondsIO整型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连接串填DSNMotorDB;。这一步不做脚本里的 SQLConnect 就会失败。4.2 停机瞬间写一条运行明细在第 3.2 节的停止事件脚本末尾追加一段写库逻辑// 电机停止结算完成后把本段运行明细写入数据库 if (\本站点\M01_RunSeconds 0) { \本站点\M01_EndDate \本站点\$日期; \本站点\M01_EndHour \本站点\$时; \本站点\M01_EndMinute \本站点\$分; \本站点\M01_EndSecond \本站点\$秒; SQLConnect(DevID, DSNMotorDB;); 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_Namestart_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 台账放到一边去。这样踩坑最少后续维护也不用半夜跑现场改脚本。希望帮到你。本文还有配套的精品资源点击获取