跳到正文
Learn-SCL
SCL 示例库/SCL 阀门控制:状态机、超时监视与错误码

SCL 阀门控制:状态机、超时监视与错误码

单/双电控阀门的状态机:开阀、关阀、开/关到位反馈、三个超时定时器和 7 种错误码。

综合 S7-1200 S7-1500 状态机 阀门 TON_TIME 错误处理 1 块 / 144 行 可仿真 TIA 已验证

一句话:用 0..3 表示关闭/正在开/已开/正在关,三个 TON_TIME 监视过渡态,超时置 1..7 错误码,到位反馈后清定时器。

导入到我的 TIA 项目

通过 TiaLink 把本示例的 1 个 SCL 块作为“外部源”导入你当前打开的 TIA Portal 项目:自动生成块、编译,并清理外部源。导入后不会自动保存;FB 示例导入后需要你在 OB 里调用。

TiaLink 设置

默认 http://localhost:8080。也可以在本页地址后加 ?tialink= 临时指定。

TiaLink 启动时若设置了 --api-token,这里要填相同的值;只保存在本机浏览器 localStorage,不会上传。

阀门控制是“状态机 + 反馈 + 超时 + 错误处理”的典型组合:开阀命令下去后,阀门不会瞬间到位;如果长时间没有到位反馈,就要报错;开/关反馈同时为 1,也要报错。

SCL 阀门控制状态机:关闭/正在开/已开/正在关,三个 TON 监视过渡态
0 关闭、1 正在开、2 已开、3 正在关;三个 TON_TIME 分别监视开阀、关阀和未知状态。

概述

FB_ValveControl 用 State 表示 0=关闭、1=正在开、2=已开、3=正在关。它支持:

  • 单电控阀(TypeOfValve=0):失电即关,程序用 NOT OpenCmd 生成关阀命令;
  • 双电控阀(TypeOfValve=1):开/关命令分开;
  • 三个 TON_TIME 分别监视开阀、关阀和“无反馈”超时;
  • 7 种错误码和对应的英文错误文本。

原工程用 UDT udt_valveStruct 把状态、反馈、输出、定时器和错误文本打包传递;本页改成 FB 接口 + 静态定时器,更适合单个阀门实例和逐扫描仿真。

你将学到

  • 用 CASE 写一个带反馈的阀门状态机;
  • 单电控/双电控的差异;
  • 三个超时定时器如何每周期调用、条件不满足时自动复位;
  • 错误码的优先级和错误文本映射。

接口与变量

下表由构建器从 SCL 源码的接口声明自动生成;页面上展示的代码就是导入 TIA 的同一份源码。

FB_ValveControl FB

区块名称类型说明
输入OpenCmdBool
输入CloseCmdBool
输入FbOpenedBool
输入FbClosedBool
输入TypeOfValveInt
输入OpenTimeTime
输入CloseTimeTime
输入UnknownTimeTime
输出YOpenBool
输出YCloseBool
输出StateInt
输出ErrorStateUInt
输出ErrorTextSTRING[60]
静态TimerOpenTON_TIME
静态TimerCloseTON_TIME
静态TimerUnknownTON_TIME
静态StateIntInt
静态ErrorStateIntUInt
临时CloseEffectiveBool
常量STATE_CLOSEDInt
常量STATE_OPENINGInt
常量STATE_OPENEDInt
常量STATE_CLOSINGInt

程序结构

导入顺序以下表为准:被依赖的块/类型先导入;本页已按依赖排好。

  1. FB_ValveControl FB 阀门状态机与超时监视 · 144 行 · 6 段

FB_ValveControl FB

阀门状态机与超时监视 · 144 行 · 源文件 blocks/FB_ValveControl.scl

查看 / 复制完整块源码
FB_ValveControl.scl
1FUNCTION_BLOCK "FB_ValveControl"
2{ S7_Optimized_Access := 'TRUE' }
3VERSION : 0.1
4   VAR_INPUT
5      OpenCmd : Bool;        // 开阀命令
6      CloseCmd : Bool;       // 关阀命令(单电控阀由程序自动取反)
7      FbOpened : Bool;       // 开到位反馈
8      FbClosed : Bool;       // 关到位反馈
9      TypeOfValve : Int;     // 0=单电控 1=双电控
10      OpenTime : Time := T#3S;    // 开阀超时,T#0MS 关闭监视
11      CloseTime : Time := T#3S;   // 关阀超时
12      UnknownTime : Time := T#3S; // 未知状态超时
13   END_VAR
14 
15   VAR_OUTPUT
16      YOpen : Bool;          // 开阀输出
17      YClose : Bool;         // 关阀输出
18      State : Int := 0;      // 0=关闭 1=正在开 2=已开 3=正在关
19      ErrorState : UInt := 0; // 0=无错误,1..7 见错误文本
20      ErrorText : String[60] := ''; // 当前错误说明
21   END_VAR
22 
23   VAR
24      TimerOpen : TON_TIME;    // 开阀超时监视
25      TimerClose : TON_TIME;   // 关阀超时监视
26      TimerUnknown : TON_TIME; // 未知状态监视
27      StateInt : Int := 0;     // 内部状态(避免输出未初始化的编译告警)
28      ErrorStateInt : UInt := 0; // 内部错误码
29   END_VAR
30 
31   VAR_TEMP
32      CloseEffective : Bool;   // 实际参与逻辑的关阀命令
33   END_VAR
34 
35   VAR CONSTANT
36      STATE_CLOSED : Int := 0;
37      STATE_OPENING : Int := 1;
38      STATE_OPENED : Int := 2;
39      STATE_CLOSING : Int := 3;
40   END_VAR
41 
42BEGIN
43   REGION 阀门类型处理
44      #CloseEffective := #CloseCmd;
45      IF #TypeOfValve = 0 THEN
46         // 单电控阀:失电即关,所以“开命令取反”就是关命令。
47         #CloseEffective := NOT #OpenCmd;
48      END_IF;
49   END_REGION
50 
51   REGION 状态机
52      CASE #StateInt OF
53         0:
54            IF #OpenCmd AND NOT #CloseEffective THEN
55               #StateInt := #STATE_OPENING;
56            END_IF;
57         1:
58            IF #FbOpened THEN
59               #StateInt := #STATE_OPENED;
60            ELSIF #CloseEffective THEN
61               #StateInt := #STATE_CLOSING;
62            END_IF;
63         2:
64            IF #CloseEffective AND NOT #OpenCmd THEN
65               #StateInt := #STATE_CLOSING;
66            END_IF;
67         3:
68            IF #FbClosed THEN
69               #StateInt := #STATE_CLOSED;
70            ELSIF #OpenCmd THEN
71               #StateInt := #STATE_OPENING;
72            END_IF;
73         ELSE
74            #StateInt := #STATE_CLOSED;
75      END_CASE;
76   END_REGION
77 
78   REGION 超时监视
79      // 三个定时器每周期都调用,条件不满足时自动复位。
80      #TimerOpen(IN := (#StateInt = #STATE_OPENING) AND NOT #FbOpened,
81                 PT := #OpenTime);
82      #TimerClose(IN := (#StateInt = #STATE_CLOSING) AND NOT #FbClosed,
83                  PT := #CloseTime);
84      #TimerUnknown(IN := ((#StateInt = #STATE_CLOSED) OR (#StateInt = #STATE_OPENED))
85                        AND NOT #FbOpened AND NOT #FbClosed,
86                    PT := #UnknownTime);
87   END_REGION
88 
89   REGION 输出
90      #State := #StateInt;
91      IF #StateInt = #STATE_CLOSED OR #StateInt = #STATE_CLOSING THEN
92         #YOpen := FALSE;
93         #YClose := TRUE;
94      ELSE
95         #YOpen := TRUE;
96         #YClose := FALSE;
97      END_IF;
98   END_REGION
99 
100   REGION 错误处理
101      IF #TimerOpen.Q AND #OpenTime <> T#0MS THEN
102         #ErrorStateInt := 1; // 开阀超时
103      ELSIF #TimerClose.Q AND #CloseTime <> T#0MS THEN
104         #ErrorStateInt := 2; // 关阀超时
105      ELSIF #CloseEffective AND #OpenCmd THEN
106         #ErrorStateInt := 3; // 开关命令同时为 1
107      ELSIF #TimerUnknown.Q AND #UnknownTime <> T#0MS THEN
108         #ErrorStateInt := 4; // 无反馈,状态未知
109      ELSIF #FbClosed AND #FbOpened THEN
110         #ErrorStateInt := 5; // 开/关反馈同时为 1
111      ELSIF #StateInt = #STATE_CLOSED AND #FbOpened THEN
112         #ErrorStateInt := 6; // 已关但开反馈为 1
113      ELSIF #StateInt = #STATE_OPENED AND #FbClosed THEN
114         #ErrorStateInt := 7; // 已开但关反馈为 1
115      ELSE
116         #ErrorStateInt := 0;
117      END_IF;
118   END_REGION
119 
120   REGION 错误文本
121      CASE #ErrorStateInt OF
122         0:
123            #ErrorText := 'No error';
124         1:
125            #ErrorText := 'Valve not opening';
126         2:
127            #ErrorText := 'Valve not closing';
128         3:
129            #ErrorText := 'Open and close input at the same time';
130         4:
131            #ErrorText := 'Valve status unknown';
132         5:
133            #ErrorText := 'Open and Close Feedback at the same time';
134         6:
135            #ErrorText := 'Valve is closed, but open feedback is active';
136         7:
137            #ErrorText := 'Valve is opened, but closed feedback is active';
138         ELSE
139            #ErrorText := 'Unknown error';
140      END_CASE;
141      #ErrorState := #ErrorStateInt;
142   END_REGION
143END_FUNCTION_BLOCK
144 

FB_ValveControl / 阀门类型处理 — 单电控自动取反

FB_ValveControl.scl · 阀门类型处理
43   REGION 阀门类型处理
44      #CloseEffective := #CloseCmd;
45      IF #TypeOfValve = 0 THEN
46         // 单电控阀:失电即关,所以“开命令取反”就是关命令。
47         #CloseEffective := NOT #OpenCmd;
48      END_IF;
49   END_REGION

目的:把“开/关命令”统一成内部使用的 CloseEffective。

条件 → 动作:TypeOfValve=0(单电控)→ CloseEffective := NOT OpenCmd;否则 CloseEffective := CloseCmd。

为什么:单电控阀只有一个线圈,失电就关;如果直接使用 CloseCmd,会与“失电即关”的物理行为不一致。

注意:CloseEffective 是 VAR_TEMP,每个周期重新计算;不要把它写成静态变量。

FB_ValveControl / 状态机 — 关闭、正在开、已开、正在关

FB_ValveControl.scl · 状态机
51   REGION 状态机
52      CASE #StateInt OF
53         0:
54            IF #OpenCmd AND NOT #CloseEffective THEN
55               #StateInt := #STATE_OPENING;
56            END_IF;
57         1:
58            IF #FbOpened THEN
59               #StateInt := #STATE_OPENED;
60            ELSIF #CloseEffective THEN
61               #StateInt := #STATE_CLOSING;
62            END_IF;
63         2:
64            IF #CloseEffective AND NOT #OpenCmd THEN
65               #StateInt := #STATE_CLOSING;
66            END_IF;
67         3:
68            IF #FbClosed THEN
69               #StateInt := #STATE_CLOSED;
70            ELSIF #OpenCmd THEN
71               #StateInt := #STATE_OPENING;
72            END_IF;
73         ELSE
74            #StateInt := #STATE_CLOSED;
75      END_CASE;
76   END_REGION

目的:根据命令和反馈推进阀门状态。

条件 → 动作:

当前状态 条件 下一状态
0 关闭 OpenCmd=1 且 CloseEffective=0 1 正在开
1 正在开 FbOpened=1 2 已开
1 正在开 CloseEffective=1 3 正在关
2 已开 CloseEffective=1 且 OpenCmd=0 3 正在关
3 正在关 FbClosed=1 0 关闭
3 正在关 OpenCmd=1 1 正在开

为什么:状态机把“命令”和“反馈”分开处理,避免用一堆 IF 互相覆盖。

注意:状态 1 和 3 是过渡态;如果反馈一直不来,靠超时定时器报错,而不是在状态机里死等。本页把状态放在静态 StateInt、错误码放在 ErrorStateInt,输出 State/ErrorState 在末尾镜像;这是为了消除 TIA 对“输出变量可能未初始化”的警告(真机编译已从 13 条警告降到 0/0)。

FB_ValveControl / 超时监视 — 三个定时器每周期都调用

FB_ValveControl.scl · 超时监视
78   REGION 超时监视
79      // 三个定时器每周期都调用,条件不满足时自动复位。
80      #TimerOpen(IN := (#StateInt = #STATE_OPENING) AND NOT #FbOpened,
81                 PT := #OpenTime);
82      #TimerClose(IN := (#StateInt = #STATE_CLOSING) AND NOT #FbClosed,
83                  PT := #CloseTime);
84      #TimerUnknown(IN := ((#StateInt = #STATE_CLOSED) OR (#StateInt = #STATE_OPENED))
85                        AND NOT #FbOpened AND NOT #FbClosed,
86                    PT := #UnknownTime);
87   END_REGION

目的:检测“该开没开”“该关没关”“没有任何反馈”三种异常。

条件 → 动作:

  • TimerOpen:State=1 且 FbOpened=0,超时 OpenTime;
  • TimerClose:State=3 且 FbClosed=0,超时 CloseTime;
  • TimerUnknown:State=0/2 且两个反馈都为 0,超时 UnknownTime。

为什么:定时器每周期都调用,条件不满足时 IN=0 会自动复位;如果把定时器调用放在 CASE 分支里,状态切换后可能残留旧状态。

注意:OpenTime=T#0MS 表示关闭该监视;错误处理里会检查这个条件。

FB_ValveControl / 输出 — 开/关线圈互锁

FB_ValveControl.scl · 输出
89   REGION 输出
90      #State := #StateInt;
91      IF #StateInt = #STATE_CLOSED OR #StateInt = #STATE_CLOSING THEN
92         #YOpen := FALSE;
93         #YClose := TRUE;
94      ELSE
95         #YOpen := TRUE;
96         #YClose := FALSE;
97      END_IF;
98   END_REGION

目的:根据状态驱动开阀/关阀输出。

条件 → 动作:State=0/3 → YClose=1、YOpen=0;State=1/2 → YOpen=1、YClose=0。

为什么:单电控阀只有一个线圈,双电控阀有两个线圈;无论哪种,开/关输出都不应该同时为 1。

注意:本示例用软件互锁;安全相关场合应加硬件互锁和反馈监视。

FB_ValveControl / 错误处理 — 7 种错误码的优先级

FB_ValveControl.scl · 错误处理
100   REGION 错误处理
101      IF #TimerOpen.Q AND #OpenTime <> T#0MS THEN
102         #ErrorStateInt := 1; // 开阀超时
103      ELSIF #TimerClose.Q AND #CloseTime <> T#0MS THEN
104         #ErrorStateInt := 2; // 关阀超时
105      ELSIF #CloseEffective AND #OpenCmd THEN
106         #ErrorStateInt := 3; // 开关命令同时为 1
107      ELSIF #TimerUnknown.Q AND #UnknownTime <> T#0MS THEN
108         #ErrorStateInt := 4; // 无反馈,状态未知
109      ELSIF #FbClosed AND #FbOpened THEN
110         #ErrorStateInt := 5; // 开/关反馈同时为 1
111      ELSIF #StateInt = #STATE_CLOSED AND #FbOpened THEN
112         #ErrorStateInt := 6; // 已关但开反馈为 1
113      ELSIF #StateInt = #STATE_OPENED AND #FbClosed THEN
114         #ErrorStateInt := 7; // 已开但关反馈为 1
115      ELSE
116         #ErrorStateInt := 0;
117      END_IF;
118   END_REGION

目的:把定时器、命令冲突和反馈异常映射成唯一错误码。

条件 → 动作:按 IF/ELSIF 顺序判断:开阀超时 → 1;关阀超时 → 2;开关命令同时为 1 → 3;未知状态超时 → 4;开/关反馈同时为 1 → 5;已关但有开反馈 → 6;已开但有关反馈 → 7;否则 0。

为什么:错误码需要唯一,优先级决定“同时满足多个条件时先报哪个”;这里把“超时”放在“反馈冲突”之前。

注意:错误码是 UInt,HMI 上要显示 ErrorText,不要只显示数字。

FB_ValveControl / 错误文本 — 给 HMI 的可读说明

FB_ValveControl.scl · 错误文本
120   REGION 错误文本
121      CASE #ErrorStateInt OF
122         0:
123            #ErrorText := 'No error';
124         1:
125            #ErrorText := 'Valve not opening';
126         2:
127            #ErrorText := 'Valve not closing';
128         3:
129            #ErrorText := 'Open and close input at the same time';
130         4:
131            #ErrorText := 'Valve status unknown';
132         5:
133            #ErrorText := 'Open and Close Feedback at the same time';
134         6:
135            #ErrorText := 'Valve is closed, but open feedback is active';
136         7:
137            #ErrorText := 'Valve is opened, but closed feedback is active';
138         ELSE
139            #ErrorText := 'Unknown error';
140      END_CASE;
141      #ErrorState := #ErrorStateInt;
142   END_REGION

目的:把错误码翻译成英文短句。

条件 → 动作:CASE ErrorState OF 0..7 赋不同字符串,ELSE 赋 Unknown error。

为什么:现场人员看到 “Valve not opening” 比看到 “1” 更快定位问题。

注意:String[60] 要足够长;如果改成中文,注意 TIA 字符串长度按字符计。

仿真验证:让 Agent 写场景并执行(不需要博途)

推荐:让 Agent 自己设计场景

不需要你手写 scenario。把下面的提示词复制给已连接 PLC 云仿真 MCP 的 AI 助手(Claude Code / Cursor / VS Code 等),它会阅读源码、自己设计正常/边界/异常场景,并调用 scl_run_scenario 执行;新用户有 100 次免费调用。

  1. 复制提示词(包含完整 SCL 源码和任务说明)。
  2. 粘贴给已配置 sim MCP 的 Agent,让它先 scl_validate,再执行自己写的 scl_run_scenario。
  3. 看 Agent 返回的 PASS/FAIL、输入/期望/实际;需要精确复现时,再对照下方本站回归基线。
AGENT PROMPT
你已连接 PLC 云仿真 MCP(sim-cloud)……

任务:阅读下面的 SCL 示例,自己设计 3 个场景并用 scl_run_scenario 执行:
1) 正常路径
2) 边界/临界值
3) 异常、互锁或超时
每个场景请给出输入、期望、实际结果和 PASS/FAIL,并说明场景设计理由。
不要照抄现成场景。

示例:SCL 阀门控制:状态机、超时监视与错误码
// 复制后包含完整 SCL 源码和 MCP 参数说明

页面上只显示提示词开头;复制的内容包含全部块源码和 MCP 参数说明。

查看本站回归基线(固定场景,不是让你手写)

下面是本站构建时跑过的固定场景和预期输出,用来做回归对比或精确复现;不是要求你手写 scenario。推荐先让 Agent 自己写场景,再和这份基线对照。

EXPECTED
PASS  单电控阀开到位再关到位 (4 step(s), 4 scan(s))
PASS  开阀 1 秒无反馈:报超时 (1 step(s), 11 scan(s))
PASS  开/关反馈同时为 1:报错 5 (1 step(s), 1 scan(s))
RESULT: ALL PASS (16 scan(s))
BASELINE JSON
{
  "sources": [
    {
      "name": "blocks/FB_ValveControl.scl",
      "source": "FUNCTION_BLOCK \"FB_ValveControl\"\n{ S7_Optimized_Access := 'TRUE' }\nVERSION : 0.1\n   VAR_INPUT\n      OpenCmd : Bool;        // 开阀命令\n      CloseCmd : Bool;       // 关阀命令(单电控阀由程序自动取反)\n      FbOpened : Bool;       // 开到位反馈\n      FbClosed : Bool;       // 关到位反馈\n      TypeOfValve : Int;     // 0=单电控 1=双电控\n      OpenTime : Time := T#3S;    // 开阀超时,T#0MS 关闭监视\n      CloseTime : Time := T#3S;   // 关阀超时\n      UnknownTime : Time := T#3S; // 未知状态超时\n   END_VAR\n\n   VAR_OUTPUT\n      YOpen : Bool;          // 开阀输出\n      YClose : Bool;         // 关阀输出\n      State : Int := 0;      // 0=关闭 1=正在开 2=已开 3=正在关\n      ErrorState : UInt := 0; // 0=无错误,1..7 见错误文本\n      ErrorText : String[60] := ''; // 当前错误说明\n   END_VAR\n\n   VAR\n      TimerOpen : TON_TIME;    // 开阀超时监视\n      TimerClose : TON_TIME;   // 关阀超时监视\n      TimerUnknown : TON_TIME; // 未知状态监视\n      StateInt : Int := 0;     // 内部状态(避免输出未初始化的编译告警)\n      ErrorStateInt : UInt := 0; // 内部错误码\n   END_VAR\n\n   VAR_TEMP\n      CloseEffective : Bool;   // 实际参与逻辑的关阀命令\n   END_VAR\n\n   VAR CONSTANT\n      STATE_CLOSED : Int := 0;\n      STATE_OPENING : Int := 1;\n      STATE_OPENED : Int := 2;\n      STATE_CLOSING : Int := 3;\n   END_VAR\n\nBEGIN\n   REGION 阀门类型处理\n      #CloseEffective := #CloseCmd;\n      IF #TypeOfValve = 0 THEN\n         // 单电控阀:失电即关,所以“开命令取反”就是关命令。\n         #CloseEffective := NOT #OpenCmd;\n      END_IF;\n   END_REGION\n\n   REGION 状态机\n      CASE #StateInt OF\n         0:\n            IF #OpenCmd AND NOT #CloseEffective THEN\n               #StateInt := #STATE_OPENING;\n            END_IF;\n         1:\n            IF #FbOpened THEN\n               #StateInt := #STATE_OPENED;\n            ELSIF #CloseEffective THEN\n               #StateInt := #STATE_CLOSING;\n            END_IF;\n         2:\n            IF #CloseEffective AND NOT #OpenCmd THEN\n               #StateInt := #STATE_CLOSING;\n            END_IF;\n         3:\n            IF #FbClosed THEN\n               #StateInt := #STATE_CLOSED;\n            ELSIF #OpenCmd THEN\n               #StateInt := #STATE_OPENING;\n            END_IF;\n         ELSE\n            #StateInt := #STATE_CLOSED;\n      END_CASE;\n   END_REGION\n\n   REGION 超时监视\n      // 三个定时器每周期都调用,条件不满足时自动复位。\n      #TimerOpen(IN := (#StateInt = #STATE_OPENING) AND NOT #FbOpened,\n                 PT := #OpenTime);\n      #TimerClose(IN := (#StateInt = #STATE_CLOSING) AND NOT #FbClosed,\n                  PT := #CloseTime);\n      #TimerUnknown(IN := ((#StateInt = #STATE_CLOSED) OR (#StateInt = #STATE_OPENED))\n                        AND NOT #FbOpened AND NOT #FbClosed,\n                    PT := #UnknownTime);\n   END_REGION\n\n   REGION 输出\n      #State := #StateInt;\n      IF #StateInt = #STATE_CLOSED OR #StateInt = #STATE_CLOSING THEN\n         #YOpen := FALSE;\n         #YClose := TRUE;\n      ELSE\n         #YOpen := TRUE;\n         #YClose := FALSE;\n      END_IF;\n   END_REGION\n\n   REGION 错误处理\n      IF #TimerOpen.Q AND #OpenTime <> T#0MS THEN\n         #ErrorStateInt := 1; // 开阀超时\n      ELSIF #TimerClose.Q AND #CloseTime <> T#0MS THEN\n         #ErrorStateInt := 2; // 关阀超时\n      ELSIF #CloseEffective AND #OpenCmd THEN\n         #ErrorStateInt := 3; // 开关命令同时为 1\n      ELSIF #TimerUnknown.Q AND #UnknownTime <> T#0MS THEN\n         #ErrorStateInt := 4; // 无反馈,状态未知\n      ELSIF #FbClosed AND #FbOpened THEN\n         #ErrorStateInt := 5; // 开/关反馈同时为 1\n      ELSIF #StateInt = #STATE_CLOSED AND #FbOpened THEN\n         #ErrorStateInt := 6; // 已关但开反馈为 1\n      ELSIF #StateInt = #STATE_OPENED AND #FbClosed THEN\n         #ErrorStateInt := 7; // 已开但关反馈为 1\n      ELSE\n         #ErrorStateInt := 0;\n      END_IF;\n   END_REGION\n\n   REGION 错误文本\n      CASE #ErrorStateInt OF\n         0:\n            #ErrorText := 'No error';\n         1:\n            #ErrorText := 'Valve not opening';\n         2:\n            #ErrorText := 'Valve not closing';\n         3:\n            #ErrorText := 'Open and close input at the same time';\n         4:\n            #ErrorText := 'Valve status unknown';\n         5:\n            #ErrorText := 'Open and Close Feedback at the same time';\n         6:\n            #ErrorText := 'Valve is closed, but open feedback is active';\n         7:\n            #ErrorText := 'Valve is opened, but closed feedback is active';\n         ELSE\n            #ErrorText := 'Unknown error';\n      END_CASE;\n      #ErrorState := #ErrorStateInt;\n   END_REGION\nEND_FUNCTION_BLOCK\n"
    }
  ],
  "scenario": {
    "docs": [
      "blocks/FB_ValveControl.scl"
    ],
    "block": "FB_ValveControl",
    "tickMs": 100,
    "always": [
      "!(YOpen && YClose)"
    ],
    "scenarios": [
      {
        "name": "单电控阀开到位再关到位",
        "steps": [
          {
            "set": {
              "OpenCmd": true
            },
            "expect": {
              "State": 1,
              "YOpen": true,
              "YClose": false,
              "ErrorState": 0
            }
          },
          {
            "set": {
              "FbOpened": true
            },
            "expect": {
              "State": 2
            }
          },
          {
            "set": {
              "OpenCmd": false,
              "FbOpened": false
            },
            "expect": {
              "State": 3,
              "YOpen": false,
              "YClose": true
            }
          },
          {
            "set": {
              "FbClosed": true
            },
            "expect": {
              "State": 0,
              "YClose": true
            }
          }
        ]
      },
      {
        "name": "开阀 1 秒无反馈:报超时",
        "steps": [
          {
            "set": {
              "OpenCmd": true,
              "OpenTime": "T#1S"
            },
            "ticks": 11,
            "expect": {
              "State": 1,
              "ErrorState": 1,
              "ErrorText": "'Valve not opening'"
            }
          }
        ]
      },
      {
        "name": "开/关反馈同时为 1:报错 5",
        "steps": [
          {
            "set": {
              "FbOpened": true,
              "FbClosed": true
            },
            "expect": {
              "ErrorState": 5
            }
          }
        ]
      }
    ]
  }
}

打开 PLC 云仿真

关键 SCL 语法

写法 含义
CASE ... OF ... ELSE ... END_CASE 状态机/错误码分支
TON_TIME IEC 定时器实例的数据类型
#TimerOpen(IN := ..., PT := ...) 调用定时器实例
TimerOpen.Q 定时器到点输出
String[60] 最大 60 字符的字符串

常见坑

  • 把定时器调用放在 CASE 分支里:状态切换后定时器可能不复位;每周期都调用。
  • 单电控阀同时给开/关命令:CloseEffective = NOT OpenCmd 后不会同时为 1;如果业务需要“关优先”,要另外处理。
  • 忘记处理“无反馈”:阀门卡住时,状态机会停在过渡态;TimerUnknown 负责这种异常。
  • 错误码优先级不清:多个错误同时满足时,先报哪个要写进需求;本页的顺序只是一个合理默认。
  • OpenTime=T#0MS 仍报超时:错误处理里必须检查 <> T#0MS。

变体与扩展

  • 增加 Maintenance 模式,手动强制开/关,并屏蔽超时。
  • 增加 OpenLimit/CloseLimit 的硬件反馈去抖,避免反馈抖动导致状态来回跳。
  • 把三个定时器改成 TON 数组,支持多阀门共用同一个 FB 类型。
  • 把错误码映射成中文报警文本,接入 HMI 报警系统。

常见问题

SCL 阀门控制状态机怎么写?

用 0..3 表示关闭、正在开、已开、正在关;OpenCmd/CloseCmd 触发状态迁移,FbOpened/FbClosed 确认到位;每个过渡态挂一个 TON_TIME,超时置错误码。

阀门超时报警怎么实现?

进入过渡态时启动对应 TON(OpenTime/CloseTime/UnknownTime),Q 为 TRUE 就写 ErrorState(1..7)和 ErrorText;收到到位反馈或 Reset 时清定时器和错误。OpenTime=T#0MS 表示关闭该监视。

验证状态

项目结果日期证据
本地仿真场景PASS(16 scans)2026-11-01页面下方 `scl_run_scenario` 请求可复现
PLC 云仿真PASS2026-11-01`scl_run_scenario`(托管 MCP)
TIA 导入编译PASS(0 error / 0 warning)2026-11-01本地 SCL 仿真与 PLC 云仿真(scl_run_scenario)场景通过;TIA Portal V19 + TiaLink 1.4.0.0 批量导入 PLC_2,编译 0 错误 / 0 警告,已保存。

本地仿真引擎验证语法、结构和逐扫描行为;TIA 编译验证在 Windows + TIA Portal + TiaLink 上完成。两者不能互相替代。

来源与许可

本页示例改编自 github.com/lopez-dev/Siemens-SCL-Source-Files 的 valveControl/valveControl.scl,原作者 Daniel Cáceres López,以 MIT 许可发布。中文注释、接口调整、仿真场景与页面讲解由 Learn-SCL 编写。

原文件用 UDT 传递阀门结构;本站改为 FB 接口 + 静态 TON_TIME,保留状态机、超时和错误码。

示例源码以 MIT 许可发布(上游仓库);中文讲解与页面内容版权归汇智网(Hubwiz)。官方参考:Siemens TIA Portal。

把这个示例带进你的工程

TiaLink

把 TIA Portal 接到你的智能体:导入、编译、读回核对、PLCSIM 仿真。本地运行,工程数据不出厂。

了解 TiaLink →

PLC 云仿真

不装博途,把 SCL 放到云端逐扫描执行:校验、仿真、场景验收。新用户 100 次免费。

打开云仿真 →

Learn-LAD

同一个团队的梯形图示例库:逐网络讲解、变量表、TiaLink 一键导入。

浏览 LAD 示例 →