在结构化文本(Structured Text,ST)编程中,END、PROGRAM、FUNCTION、FUNCTION_BLOCK、VAR、IF、WHILE、FOR 等是 IEC 61131-3 标准定义的保留字(Reserved Identifiers)。它们构成 ST 语言的语法骨架,用于声明程序结构、控制流程和数据区域。一旦将这些词用作变量名、数组名、函数调用名或自定义类型名,编译器将无法区分“语法指令”与“用户标识符”,直接报错或引发隐性逻辑错误——轻则编译失败,重则运行时跳过关键分支、覆盖内存、导致设备误动作。
以下为可立即执行的冲突规避指南,覆盖识别、检测、修复、预防四大环节,所有操作均基于主流 PLC 开发环境(如 CODESYS、TIA Portal、Unity Pro、Automation Studio)通用逻辑,无需额外插件。
一、明确哪些词绝对不可用作变量名
IEC 61131-3 标准将保留字分为五类。任何一类中的词,只要出现在 VAR 块、赋值语句左侧、函数参数列表或结构体成员声明中,即构成语法冲突。
-
程序结构关键字
PROGRAM、FUNCTION、FUNCTION_BLOCK、METHOD、ACTION、CONFIGURATION、RESOURCE -
声明与作用域关键字
VAR、VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT、VAR_GLOBAL、VAR_TEMP、VAR_EXTERNAL、END_VAR -
控制流关键字
IF、ELSIF、ELSE、END_IF、CASE、OF、END_CASE、WHILE、DO、END_WHILE、REPEAT、UNTIL、END_REPEAT、FOR、TO、BY、END_FOR -
终止与返回关键字
END、RETURN、EXIT -
类型与转换关键字
BOOL、INT、DINT、REAL、STRING、TIME、DATE、TOD、DT、ARRAY、STRUCT、POINTER、TO_BOOL、TO_INT、TO_REAL等所有标准类型名及类型转换函数名
⚠️ 注意:大小写不敏感。
end、End、END全部等效;program与PROGRAM同样被禁止。
二、快速识别已存在的冲突变量(3步排查法)
1. 扫描 VAR 块:查找非法左值
打开任意 POU(Program Organization Unit)的 ST 代码,在 VAR 与 END_VAR 之间逐行检查:
- 若某行形如
END : INT;、PROGRAM_NAME : STRING;、IF_FLAG : BOOL;,即存在冲突(END、PROGRAM、IF均为保留字)。 - 若某行形如
TIME : DINT;,也属冲突——TIME是标准数据类型,不可重定义。
✅ 正确写法示例:
VAR
bEnableMotor : BOOL; // ✅ 使用描述性前缀 + 合法名称
nSetSpeed : INT; // ✅ 小写开头,无保留字
tDelay : TIME; // ✅ 类型名仅用于声明右侧,左侧不可用
END_VAR
❌ 错误写法示例:
VAR
END : BOOL; // ❌ 保留字作变量名 → 编译器报错 "Syntax error: expected identifier"
PROGRAM : STRING; // ❌ 同上
TIME : DINT; // ❌ 类型名作变量名 → 报错 "Identifier 'TIME' already declared as type"
IF : BOOL; // ❌ 控制关键字作变量名
END_VAR
2. 搜索赋值语句左侧:捕获隐性冲突
在 BEGIN 与 END_PROGRAM(或对应结束关键字)之间,查找所有 := 左侧的标识符:
END := TRUE;→ 冲突:END是终止关键字,不可赋值。PROGRAM := 'RUN';→ 冲突:PROGRAM是结构关键字。FOR := 0;→ 冲突:FOR是循环关键字。
✅ 正确写法:
bRunFlag := TRUE; // ✅ 描述性布尔变量
nLoopCounter := 0; // ✅ 避免使用 FOR/WHILE/IF 等词根
3. 检查函数调用与结构体成员:防止间接冲突
-
若定义了结构体
TYPE MOTOR_CTRL : STRUCT ... END_STRUCT,则结构体内成员不可命名为END或PROGRAM:TYPE MOTOR_CTRL : STRUCT bStart : BOOL; // ✅ nRPM : INT; // ✅ END : BOOL; // ❌ 冲突!结构体成员仍受保留字约束 END_STRUCT -
函数调用参数名亦需合规:
MyFunction(ENABLE := TRUE, END_TIME := T#5S); // ✅ 参数名合法 MyFunction(END := TRUE, PROGRAM := 1); // ❌ 参数名非法
三、一键修复冲突变量的标准化流程
按顺序执行以下四步,确保修复彻底且可追溯:
1. 全局替换前先备份
- 在开发环境中执行
File → Save As,另存为MyProject_v2_backup.pro。 - 或导出当前 POU 为
.st文本文件,本地存档。
2. 启用编辑器高亮与语法检查
- CODESYS:勾选
Tools → Options → Editor → Syntax Highlighting,并开启Real-time syntax checking。 - TIA Portal:在
Options → Settings → PLCSIM Advanced / Compiler中启用Show all warnings and errors。 - Automation Studio:
Tools → Options → Editor → Highlight reserved keywords。
此时,所有保留字在代码中将以深红色背景+白色文字高亮显示(具体颜色依主题而定),便于肉眼定位。
3. 执行精准替换(推荐正则表达式)
在支持正则的编辑器(如 VS Code、Notepad++、CODESYS 内置编辑器)中,打开全部 .st 文件,执行以下替换:
| 查找内容(正则) | 替换为 | 说明 |
|---|---|---|
\b(END|PROGRAM|FUNCTION|FUNCTION_BLOCK|VAR|IF|WHILE|FOR|RETURN|EXIT)\b |
g_$1` | `\b` 表示单词边界,避免匹配 `END_OF_CYCLE` 中的 `END`;`$1 引用捕获组,保留原词根 |
|
\b(BOOL|INT|DINT|REAL|STRING|TIME|DATE|TOD|DT|ARRAY|STRUCT)\b(?!\s*:) |
`t_$1` | 仅匹配非声明位置的类型名(如 `TIME := T#1S` 中的 `TIME`),`(?!\s*:)` 排除 `TIME : TON;` 这类合法声明 | ✅ 替换后效果: ```pascal // 替换前(错误) VAR END : BOOL; PROGRAM : STRING; TIME : DINT; END_VAR // 替换后(正确) VAR g_END : BOOL; g_PROGRAM : STRING; t_TIME : DINT; END_VAR ``` > 💡 命名规范建议: > - `g_` 前缀 = global scope(全局变量) > - `b_` 前缀 = boolean > - `n_` 前缀 = numeric (INT/DINT) > - `t_` 前缀 = time/duration > - `s_` 前缀 = string > 统一前缀既规避冲突,又提升可读性与维护性。 #### 4. **重新编译并验证逻辑完整性** - 执行 `Build → Rebuild All`。 - 若编译通过,进入在线调试模式,监控新变量 `g_END` 是否按预期更新(例如,原 `END := TRUE` 逻辑是否被 `g_END := TRUE` 完全替代)。 - 对照原始需求文档,逐条验证功能点:启动/停止、计时、状态切换等行为未因变量名变更而偏移。 --- ### 四、长效预防机制:从源头杜绝冲突 #### 1. **建立团队级命名白名单** 在项目根目录下创建 `ST_NAMING_RULES.md`,强制规定: - 禁止使用完整保留字(含大小写变体); - 禁止在变量名中**连续包含**保留字字符序列(如 `ENDBIT`、`PROG_CTRL`、`IF_STATE`),因其易被误读且部分旧版编译器会触发模糊匹配警告; - 允许使用缩写(如 `bEn` 代替 `bEnable`),但缩写必须为行业通用(`bEn`、`nSpd`、`tDly`),禁用自创缩写(如 `bE`、`nS`)。 #### 2. **配置编辑器自动校验** 在 CODESYS 中,通过 `Tools → Scripting → Editor Events` 添加脚本,实现实时拦截: ```python # auto_check_reserved.py reserved_words = { "END", "PROGRAM", "FUNCTION", "FUNCTION_BLOCK", "VAR", "IF", "WHILE", "FOR", "RETURN", "EXIT", "BOOL", "INT", "DINT", "REAL", "STRING", "TIME" } def on_text_changed(editor): text = editor.get_selected_text() or editor.get_text() for word in reserved_words: if re.search(rf"\b{word}\b", text, re.IGNORECASE): editor.show_message(f"⚠️ 保留字 '{word}' 不可用作变量名", "Error") ``` TIA Portal 用户可启用 `PLC → Options → Check naming conventions` 并导入自定义规则 XML。 #### 3. **CI/CD 流水线自动扫描(适用于大型项目)** 在 Git 提交钩子(pre-commit)或 Jenkins 构建步骤中加入 Shell 脚本: ```bash #!/bin/bash # scan_st_reserved.sh find . -name "*.st" | while read file; do grep -nE '\b(END|PROGRAM|FUNCTION|IF|WHILE|FOR|RETURN|EXIT|BOOL|INT|DINT|REAL|STRING|TIME)\b' "$file" | \ |
| grep -v "^\s*//" | grep -v "^\s\/\" && exit 1 |
done
echo "✅ 所有 .st 文件通过保留字检查"
返回非零码即中断构建,强制开发者修正后再提交。
---
### 五、典型误用场景与纠正对照表
| 场景描述 | 错误代码片段 | 风险等级 | 正确方案 |
|----------|--------------|----------|-----------|
| 将 `END` 用作状态标志位 | `END := bCycleComplete;` | ⚠️⚠️⚠️ 高(编译失败) | `bCycleDone := bCycleComplete;` |
| 用 `PROGRAM` 记录当前运行程序号 | `PROGRAM := 3;` | ⚠️⚠️⚠️ 高(语法错误) | `nActiveProgID := 3;` |
| 以 `TIME` 存储毫秒计数值 | `TIME := nMsCounter;` | ⚠️⚠️ 中(类型混淆,可能隐式转换错误) | `nMsCounter := nMsCounter;`(直接用变量名)或 `tElapsedTime := TIME_TO_TOD(TIME());`(若真需时间类型) |
| 在结构体中定义 `IF` 成员 | `TYPE ALARM : STRUCT IF : BOOL; END_STRUCT` | ⚠️⚠️⚠️ 高(结构体解析失败) | `TYPE ALARM : STRUCT bAlarmActive : BOOL; END_STRUCT` |
| 函数参数名为 `FOR` | `MyFunc(FOR := 10)` | ⚠️⚠️ 高(调用语法解析异常) | `MyFunc(nRepeatCount := 10)` |
---
### 六、进阶提醒:保留字与厂商扩展的叠加风险
部分厂商在 IEC 61131-3 基础上添加专属关键字,例如:
- Siemens TIA Portal:`AT`, `AT %MB100`, `RETAIN`, `NON_RETAIN`
- Beckhoff TwinCAT:`PRAGMA`, `FB_INIT`, `FB_EXIT`
- Rockwell Logix:`INIT`, `MAJOR_FAULT`, `MINOR_FAULT`
这些词虽非国际标准保留字,但在对应平台中同样不可用作变量名。务必查阅所用 PLC 厂商的《ST 语言参考手册》附录“Reserved Identifiers”,将其一并纳入白名单。
例如,在 TIA Portal 中使用 `AT` 作为变量名:
```pascal
VAR
AT : INT; // ❌ 编译报错:"'AT' is a reserved keyword"
END_VAR
应改为 nAnalogThreshold 或 nAtValue。
修正后的变量名必须满足三个条件:非保留字、具描述性、符合团队命名规范。

暂无评论,快来抢沙发吧!