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 的“变量管理”检查变量名和类型即可快速定位。

WinCC C 脚本执行链与 API 读写关系 触发源 按钮 / 变量变化 / 画面打开 C 动作函数 变量声明 / 流程控制 / 函数调用 WinCC C API GetTag / SetTag / GetTagWord 变量系统 Tag Management 读路径(Get) GetTagWord GetTagBit GetTagDouble GetTag(通用) 返回值直接使用;通用版本需传入变量地址 写路径(Set)与返回值检查 SetTagWord SetTagBit SetTagDouble SetTag(通用) 返回 0 → 成功,继续执行 返回非 0 → 写入诊断变量,画面报警 示例:SetTagWord("MotorSpeed",1500) 返回 0 表示写入成功;非 0 需检查变量名、类型与连接状态

一个完整的可运行示例

下面这段脚本放在“启动按钮”的鼠标点击事件中,实现:读取设定速度、限幅、写入过程变量、置运行位、并检查每一步的返回值。假设变量均为内部变量,类型分别为: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 则根据变量名和类型逐项排查。

建议在项目初期就建立“诊断变量”习惯:为每个关键脚本分配一个错误码变量,脚本中所有 API 写操作都检查返回值。这样后期排查时,不需要逐行加打印,直接看画面上的错误码即可定位到具体是哪个变量、哪一步失败。
WinCC C 脚本对变量名大小写敏感,且变量必须已在变量管理中定义。常见错误是变量名拼写正确但类型不匹配,例如用 GetTagWord 读取一个浮点变量,此时返回值可能为 0 或异常值,必须通过返回值检查发现。