3. C脚本基础语法与WinCC API
WinCC 的 C 脚本(ANSI-C Script)运行在 WinCC 运行系统内部,与标准 C 语言高度相似,但它并不是独立编译执行的程序,而是由 WinCC 在触发时机(按钮点击、变量变化、画面打开等)调用的一段函数体。理解这一点非常关键:脚本的“入口”不是 main(),而是由 WinCC 指定的动作函数;脚本的“输入输出”也不是键盘和屏幕,而是 WinCC 变量系统(Tag Management)与画面对象。因此,本节把语法与 API 放在一起讲,因为脱离 API 的语法在 WinCC 中几乎没有意义。
变量声明与数据类型
WinCC C 脚本支持标准 C 的基本类型:int、long、double、float、char、BOOL(本质是 int 的别名)。此外,WinCC 提供了与变量系统对应的类型别名,例如 DWORD(32 位无符号)、WORD(16 位无符号)、BYTE(8 位无符号)。声明方式与标准 C 一致:
int iCount;
double dTemp;
BOOL bEnable;
DWORD dwStatus;
char szMsg[64];
需要特别注意:WinCC C 脚本中的局部变量在每次动作触发时重新创建,不保留上一次的值。如果希望某个值在多次触发之间保持,必须使用 WinCC 变量(内部变量或过程变量)或静态变量(static),但静态变量在画面切换、脚本重载时可能被重置,工程上更推荐用内部变量保存状态。
运算符与流程控制
运算符与标准 C 完全一致,包括算术运算符 + - * / %、关系运算符 == != > < >= <=、逻辑运算符 && || !、位运算符 & | ^ ~ << >>、赋值与复合赋值 = += -= *= /=,以及自增自减 ++ --。在 WinCC 中,位运算尤其常用,因为很多 PLC 变量是按位打包的(例如一个 WORD 的 16 个位分别代表 16 个状态)。
流程控制同样沿用标准 C:if / else if / else、switch / case / default、for、while、do...while、break、continue、return。下面是一个典型的“按位判断并回写”的示例,假设 MotorStatus 是一个 16 位内部变量,bit0 表示运行、bit1 表示故障:
WORD wStatus;
wStatus = GetTagWord("MotorStatus");
if (wStatus & 0x0001) {
SetTagBit("RunLamp", 1);
} else {
SetTagBit("RunLamp", 0);
}
if (wStatus & 0x0002) {
SetTagBit("FaultLamp", 1);
SetTagWord("FaultCode", wStatus >> 8);
} else {
SetTagBit("FaultLamp", 0);
}
函数定义与调用
WinCC C 脚本允许在动作中定义函数,但更规范的做法是把通用逻辑写成“项目函数”(Project Functions)或“标准函数”(Standard Functions),然后在动作中调用。项目函数在全局脚本编辑器中创建,可以被多个动作复用。函数定义格式与标准 C 相同:
int CalcAverage(int a, int b, int c)
{
return (a + b + c) / 3;
}
调用时直接写函数名和实参即可。需要注意的是,WinCC 项目函数不能有与 WinCC API 冲突的名称,也不建议在函数内部直接操作画面对象,画面对象操作应放在动作中,函数只做纯计算或变量读写,这样便于测试和复用。
WinCC C API 常用函数
WinCC C API 是脚本与变量系统之间的桥梁。最常用的读写函数如下表所示。所有函数都返回一个状态值,通常 0 表示成功,非 0 表示失败。
| 函数 | 作用 | 典型返回 |
|---|---|---|
GetTag(TagName, &value) | 读取任意类型变量到指定地址 | 0 成功,非 0 失败 |
SetTag(TagName, value) | 写入任意类型变量 | 0 成功,非 0 失败 |
GetTagWord(TagName) | 读取 16 位无符号变量 | 返回 WORD 值 |
SetTagWord(TagName, value) | 写入 16 位无符号变量 | 0 成功,非 0 失败 |
GetTagBit(TagName) | 读取位变量 | 返回 0 或 1 |
SetTagBit(TagName, value) | 写入位变量 | 0 成功,非 0 失败 |
GetTagDouble(TagName) | 读取浮点变量 | 返回 double |
SetTagDouble(TagName, value) | 写入浮点变量 | 0 成功,非 0 失败 |
其中 GetTag 和 SetTag 是通用版本,需要传入变量地址;而 GetTagWord、GetTagBit 等是专用版本,直接返回值,使用更方便。实际工程中,读取单个位或字优先用专用函数,读取结构体或数组时才用通用版本。
返回值处理与错误代码
WinCC API 的返回值必须检查,否则一旦变量名拼错、变量不存在或类型不匹配,脚本会静默失败,画面没有任何反应,排查非常困难。推荐的做法是:对每个写操作检查返回值,失败时写入一个诊断变量或弹出消息。例如:
int nRet;
nRet = SetTagWord("MotorSpeed", 1500);
if (nRet != 0) {
SetTagWord("ScriptErrorCode", nRet);
SetTagBit("ScriptErrorFlag", 1);
}
常见的错误代码含义:0 表示成功;非 0 通常表示变量不存在、类型不匹配、变量未连接或权限不足。具体数值随 WinCC 版本略有差异,工程上不必死记,而是把返回值原样写入诊断变量,在画面上显示出来,配合 WinCC 的“变量管理”检查变量名和类型即可快速定位。
一个完整的可运行示例
下面这段脚本放在“启动按钮”的鼠标点击事件中,实现:读取设定速度、限幅、写入过程变量、置运行位、并检查每一步的返回值。假设变量均为内部变量,类型分别为:SetSpeed(WORD)、MotorSpeed(WORD)、RunCmd(BIT)、ScriptErrorCode(WORD)。
WORD wSet;
int nRet;
wSet = GetTagWord("SetSpeed");
if (wSet > 3000) {
wSet = 3000;
}
if (wSet < 0) {
wSet = 0;
}
nRet = SetTagWord("MotorSpeed", wSet);
if (nRet != 0) {
SetTagWord("ScriptErrorCode", nRet);
return;
}
nRet = SetTagBit("RunCmd", 1);
if (nRet != 0) {
SetTagWord("ScriptErrorCode", nRet);
return;
}
SetTagWord("ScriptErrorCode", 0);
这段脚本体现了本节的核心要点:变量先声明再使用;用 if 做限幅;每次写操作都检查返回值;出错时记录错误码并提前 return,避免后续操作在错误状态下继续执行。实际调试时,可以在画面上放一个 I/O 域显示 ScriptErrorCode,点击按钮后若显示 0 表示全部成功,若显示非 0 则根据变量名和类型逐项排查。
GetTagWord 读取一个浮点变量,此时返回值可能为 0 或异常值,必须通过返回值检查发现。