上周群里有人贴了段脚本,七行,说在自己电脑上跑得好好的,拷到现场那台工控机上,按钮按下去,屏幕一点反应都没有。
代码我看了,语法没问题。问题出在三个前提上:画面当时没激活、标签是 UINT 却拿去和字符串比、那台机器的区域设置是德语。
三件事凑一块,脚本静默失败。不弹窗,不报错,什么线索都不给你。
入门教程教会你“点一下加一”。但没人告诉你,“能跑”和“敢交付”之间还隔着七八个坎。
01脚本挂在哪,比怎么写更决定成败
很多人写脚本的第一反应,是找一个“能放代码的地方”。这件事在两个平台上的答案完全不同。
WinCC 有全局脚本,项目模块、标准模块,可以周期执行,也能被多个画面调用。TIA Portal 的 WinCC 没有这个概念——所有 VB 脚本都必须挂在某个对象的某个事件上。
这个差别直接决定代码怎么组织:
● WinCC 全局脚本:写一次全局可用,适合报警抑制、班次切换这类系统级逻辑。
● TIA Portal:只能挂按钮按下/释放、I/O 域值变化、画面打开/关闭这些事件。要做周期任务,就挂画面的“更新”事件,或者拿一个周期变量去触发。
第二件事更关键:事件是同步的。
脚本一开始执行,画面的消息循环就被占住了。你在里面写个循环等三秒,操作员就盯着一个卡死的画面三秒;等三十秒,他大概率以为死机了,然后去拍急停。
所以第一条纪律:事件里只放短动作。任何需要等待、需要轮询的流程,拆成步骤,用变量推着走。
02SmartTags 和 HMIRuntime 是两条路,别串道
这是新手最容易混的地方,也是我见过最多“写了半天没反应”的原因。
● SmartTags 走的是标签。你读写的是一块和 PLC、归档、报警共用的数据。
● HMIRuntime.Screens 走的是画面对象。你改的是一个控件此刻显示什么。
判据一句话:这个值需不需要被 PLC 或归档看见?需要,写标签。只是一句临时提示、一次颜色变化,改对象属性。
反过来用会出两种典型故障。想改 I/O 域的显示,却去写了一个根本没绑到该域的标签,屏幕上纹丝不动;想改标签,却去改控件的 Text,运行中画面一刷新,你写的东西就没了。
另外,WinCC 经典版的读写是 HMIRuntime.Tags 配 Read/Write,TIA Portal 才是 SmartTags。老项目往新平台搬,这一层基本要重写,别指望复制粘贴。
03类型:VBS 不报错,它只是安静地转错
VBScript 只有一种变量类型 Variant。好处是不用声明,坏处是它会在你背后做隐式转换,转错了也不吭声。
几条从现场换来的经验:
● 累加量先算范围。UINT 顶到 65535,一个班次的产量完全能越过去。做累计的标签一律用 UDINT,别等数字卡住才想起来改。
● 数字和字符串别混着比。VBS 的隐式转换规则不直观,你以为是数值比较,它可能按字符串走。要么两边都 CStr,要么两边都 CLng,让类型在代码里是明的。
● 小数点是会咬人的。浮点数转字符串跟系统区域设置有关,欧系系统上小数点会变成逗号,HMI 上显示 12,5,PLC 读回去可能就是 12,或者直接类型错误。凡是数值要变成文字展示、再被解析回来的地方,用 FormatNumber 显式指定格式,或者干脆手工拼字符串。
● Mod 只认整数。拿浮点去求余,会给你一个很意外的结果。
04Set 拿不到对象时,什么都不会发生
画面对象必须用 Set 赋值。写的时候多拆一行,比事后查半天划算:
Dim scr, txtObj
Set scr = HMIRuntime.Screens("Main")
Set txtObj = scr.ScreenItems("Txt_1")
txtObj.Text = "1 号线 · 实时参数"
Set txtObj = Nothing
这里有三个不写在手册显眼处的前提。
● 第一,画面必须是激活状态。屏幕都没打开,ScreenItems 里当然是空的。
● 第二,画面窗口里的对象,路径要带层级。主画面 Main 上放了一个画面窗口 WinDetail,里面显示 Detail 画面,那 Detail 里的控件叫 Main.WinDetail:Detail,少一层就取不到。
● 第三,取不到时通常不报错,只是给你一个 Nothing,后面所有属性赋值静默失败。
所以:每次 Set 之后判断一下,用完记得 Set obj = Nothing 释放。常年运行的项目,不释放的引用会一点点堆积。
还有个更隐蔽的坑——画面对象改名。脚本里写的是字符串,改名之后编译不会报错,跑起来才炸。命名定下来就别动;实在要动,把名字全提到脚本头部的常量区,改一处就够。
05VBS 里没有 Sleep,这是好意
想“等两秒再执行”的人,通常会写一个 While 加 Time 的空转。这一招在桌面上能用,在 HMI 上等于让画面死两秒。
正确的做法是把时间维度交给系统:一个 INT 类型的步骤标签,加一段挂在画面更新事件(或 250ms 周期变量)上的脚本,每次只推进一步。
挂在画面的“更新”事件,或一个 250ms 周期变量上
Sub Step_Machine()
Dim s
s = SmartTags("Seq_Step").Value
Select Case s
Case 0 ' 待触发
Case 1
SmartTags("ValveOpen").Value = True
SmartTags("Seq_Step").Value = 2
Case 2
If SmartTags("Flow_OK").Value Then
SmartTags("Seq_Step").Value = 3
End If
Case 3
SmartTags("PumpStart").Value = True
SmartTags("Seq_Step").Value = 0
End Select
End Sub
这段每 250ms 只跑几行,不阻塞,断线重连后还能从当前步骤接着走。它比“睡两秒”多写十行,但它是能交付的。
06On Error Resume Next 不是“忽略错误”
VBScript 只有 On Error Resume Next,没有 GoTo。这句话的意思是“出错不要停”,不是“出错不用管”。
真正要做的,是每段关键读写后面立刻查一次 Err:
curS = SmartTags("Prod_Shift").Value
If Err.Number <> 0 Then
Log "读产量失败 " & Err.Description
Err.Clear
Exit Sub
End If
查完记得 Err.Clear。不清的话,错误会一直挂在那儿,后面每一个判断都成立,你会在一个错误上反复排查半小时。
日志往哪写?HMIRuntime.Trace 配 vbCrLf 换行。V7 打开 APDiag 的 Trace 页就能看;TIA Portal 要先在运行系统设置的“诊断→Trace”里开启并设定条数,否则写了也看不见。
还有一个交付前必查项——MsgBox。调试时它很好用,但它是模态的,一弹出来就挡住整个画面,无人值守的设备会一直卡在那儿等人点确定。上线前全局搜一遍,全部清掉。
07一个能直接抄的骨架
'=====================================
' 脚本:ShiftReport.vbs
' 用途:总产量减当班产量,写入报表
' 版本:v1.2 修改:2026-10-20
' 说明:标签名集中在常量区
'=====================================
Const TAG_SHIFT = "Prod_Shift"
Const TAG_TOTAL = "Prod_Total"
Const TAG_REPORT = "Report_Text"
Sub Log(msg)
Dim s
s = "[SR] " & msg & vbCrLf
HMIRuntime.Trace s
End Sub
Sub OnClick_Btn_Report()
Dim curS, curT, dlt, s
On Error Resume Next
curS = SmartTags(TAG_SHIFT).Value
curT = SmartTags(TAG_TOTAL).Value
If Err.Number <> 0 Then
Log "读产量失败 " & Err.Description
Err.Clear
Exit Sub
End If
dlt = CLng(curT) - CLng(curS)
If dlt < 0 Then dlt = 0 ' 清零保护
s = CStr(Year(Now)) & "-" _
& Right("0" & Month(Now), 2) _
& "-" & Right("0" & Day(Now), 2)
s = s & " 差:" & CStr(dlt)
SmartTags(TAG_REPORT).Value = s
If Err.Number <> 0 Then
Log "写报表失败 " & Err.Description
Err.Clear
End If
End Sub
这个骨架里四件事值得照搬:标签名集中在常量区、错误检查紧跟读写、版本号和修改记录写在文件头、折算逻辑显式转型。
08什么情况下别用 VBS
拼报表、算班次、按条件跳画面
用 VBS 字符串处理省事,改起来快
100ms 级高频处理、大量数学运算
用 C 脚本 编译执行,不受解释器拖累
要调 Windows API、读写外部文件
用 C 脚本 VBS 在 RT 里访问 COM 受限
新项目,WinCC Unified
用 JavaScript Unified 不支持 VBS
给还在写 VBS 的人一个建议:把脚本分成两半——“和 WinCC 打交道”的那几行(读标签、写标签、取对象),和“纯计算”的那部分(算法、判断、拼字符串)。分开了,将来换语言,只有前面那几行要重写,后面的逻辑一行不动。
脚本写出来能跑,只说明语法对了。
能不能上线,看的是它在别人机器上、在断网的时候、在操作员连点十下的时候,还是不是对的。
你在现场被脚本坑得最惨的一次是什么?评论区聊聊。
2026年11月