该功能块(FB520)是西门子SCL语言编写的SICK RFU630射频识别(RFID)设备通信模块,核心功能是通过TCP协议与SICK RFU设备交互,实现RFID标签的读取、写入、故障诊断、数据可视化及报警上报,广泛用于工业自动化场景,如汽车生产线物料追踪)。
HMI面板:
功能块:
功能块大纲:
输入参数
名称 | 类型 | 描述 |
---|---|---|
Frg_oProz | BOOL | 预选无过程使能 |
Ver_Lesen | BOOL | 读联锁 |
Ver_Schreiben | BOOL | 写保护 |
Lesen | BOOL | 外部读,读门打开上升沿 (GateOn) -> 读门关闭下降沿 (GateOff),变量数据传输 |
Schreiben | BOOL | 启动外部读操作 |
StartAdd | INT | 起始地址,用于写数据载体 |
Laenge | INT | 将要写入数据载体的字数量 |
FM_Reset | BOOL | 清除循环监控 |
ZUeGes | TIME | 写 / 读过程超时 (T#10s) |
VerbID | STRUCT | 本地 TCP 连接 |
_Cfg | WORD | 组态字 |
VkStoe_In | BOOL | 连接故障 |
组态位
参数 | 位 | 状态 | 描述 |
---|---|---|---|
Cfg | 00 | 0 | 对内部时间上的 “门关闭” 进行外部读 - 不 |
1 | 对内部时间上的 “门关闭” 进行外部读 - 是 | ||
01 | 0 | 出错后重复读 - 不 | |
1 | 出错后重复读 - 是 | ||
02 - 15 | - | 无 |
输出参数
名称 | 类型 | 描述 |
---|---|---|
oProz | BOOL | 无过程预选 |
Data_UII | 字节数组 [1..30] | UII 区的数据 |
FM_Lesen | BOOL | 读操作结束 |
ERROR | BOOL | 故障 |
FM_Schreiben | BOOL | 写完成 |
BUSY | BOOL | 写 / 读处于活跃状态 |
Stoe | BOOL | 组故障 |
VkStoe_Out | BOOL | 连接故障 |
输入和输出参数
名称 | 类型 | 描述 |
---|---|---|
ST_MELD | 消息缓冲区 | 消息结构,用于记录全部错误消息的全局数据结构,可被所有块写,由消息管理功能评估,含时间戳、设备标签和错误文本并输入到该结构中 |
ST_BA | ST_Betriebsarten | 运行模式结构 |
ST_RFU_DATA | ST_RFU_DATA | 接收和发送缓冲区 |
功能块深度解读:
变量声明(输入/输出/In-Out/临时/静态)
变量按功能分为:控制信号、TCP参数、状态标志、数据缓冲区、可视化变量,是功能块逻辑的“数据载体”。
1.?输入变量(VAR_INPUT):外部控制与配置
代码片段:
VAR_INPUT
? ?Frg_oProz ? ?: BOOL; ? ? ? ??// Freigabe Vorwahl ohne Prozess → 允许“无流程”模式
? ?Ver_Lesen ? ?: BOOL; ? ? ? ??// Verriegelung Lesen → 读取使能(锁定/解锁读取)
? ?Ver_Schreiben: BOOL; ? ? ? ??// Verriegelung Schreiben → 写入使能(锁定/解锁写入)
? ?Lesen ? ? ? ?: BOOL; ? ? ? ??// Lesen extern pos.Flanke Start Tag lesen → 外部读取触发(上升沿启动读标签)
? ?Schreiben ? ?: BOOL; ? ? ? ??// Start Schreiben extern → 外部写入触发
? ?StartAdd ? ? : INT:=?0; ? ? ?// Start Wort beim Schreiben auf Datentraeger max. 31 → 写入起始地址(最大31)
? ?Laenge ? ? ? : INT:=?32; ? ??// Anzahl Worte beim Schreiben auf Datentraeger → 写入数据长度(字)
? ?FM_Reset ? ? : BOOL; ? ? ? ??// Taktkontrolle abloeschen → 复位触发(清空时序控制)
? ?ZUeGes ? ? ? : TIME:= T#10s;?// Timeout Schreib/Lesevorgang → 读写超时时间(默认10s)
? ?// TCP连接参数结构体
? ?VerbID : STRUCT ? ? ? ? ? ? ?//TCP-native Kopplung?
? ? ? ? ID : INT; ? ? ? ? ? ? ? ? ??//Verbindungsidentifikationsnummer → 连接ID
? ? ? ? DeviceID : INT; ? ? ? ? ? ??//2= CPU31x(F)-2PN/DP; 3=CPU319(F)-3PN/DP; 5= CPU41x(F)-xPN/DP → CPU型号
? ? ? ? IP : ARRAY[1?..?4?] OF INT;?//Partner IP Adresse → RFU设备IP(如[192,168,0,100])
? ? ? ? lokalPort : INT; ? ? ? ? ? ?//eigene Portnummer (2000 ... 5000) → 本地端口(2000-5000)
? ? ? ? remotePort : INT ; ? ? ? ? ?//nicht relevant → 远程端口(未使用)
? ? ? ? lokalTSAP : STRUCT ? ? ? ? ?//nicht relevant → 本地TSAP(未使用,TCP-native无需配置)
? ? ? ? ? ? ?Rack : BYTE ; ? ? ? ? ?
? ? ? ? ? ? ?Steckplatz : BYTE ; ? ?
? ? ? ? ? ? ?TSAP : STRING ?[14?]; ?
? ? ? ? END_STRUCT ; ? ?
? ? ? ? remoteTSAP : STRING ?[16?];?//nicht relevant → 远程TSAP(未使用)
? ?END_STRUCT ; ? ?
? ?// 配置位(WORD映射为BOOL,方便位操作)
? ?_Cfg : ? ?WORD;// X0:Lesen extern "Gate Off" ueber interne Zeit, X1:Lesen Wiederholung bei Fehler?
? ? ? ? Cfg AT _Cfg: STRUCT ? ? ? ?// WORD → BOOL
? ? ? ? ? ? X8~X15 : BOOL ; ? ? ? ?// 未使用
? ? ? ? ? ? X0 ?: BOOL ; ? ? ? ? ??// Lesen extern "Gate Off" ueber interne Zeit → 外部读取时用内部延时判断Gate Off
? ? ? ? ? ? X1 ?: BOOL ; ? ? ? ? ??// Lesen Wiederholung bei Fehler → 读取错误时自动重试
? ? ? ? ? ? X2~X7 : BOOL ; ? ? ? ??// 未使用
? ? ? ? END_STRUCT;
? ? VkStoe_In ? : BOOL; ? ? ? ? ? ??// Verkettete Stoerung → 外部连锁故障输入(如上游设备故障)
END_VAR
```
#### 解读
- **控制信号**:`Lesen`/`Schreiben`是外部触发读写的核心信号(需上升沿触发);`Ver_Lesen`/`Ver_Schreiben`是“权限开关”,锁定时无法触发读写。
- **TCP参数**:`VerbID`结构体定义RFU设备的IP、本地端口、CPU型号,是TCP连接的基础配置。
- **配置位**:`Cfg.X0`/`Cfg.X1`通过`AT`指令映射到`_Cfg`的位,实现“用WORD存储、按BOOL操作”,是西门子SCL的常用技巧。
### 2. 输出变量(VAR_OUTPUT):状态反馈与数据输出
#### 代码片段
```scl
VAR_OUTPUT
? ? oProz ? ? ? : BOOL; ? ? ? ? ? ? ? ? ? ??// Vorwahl Ohne Prozess → “无流程”模式状态输出
? ? Data_UII ? ?: ARRAY [1..30] OF BYTE ; ??// ARRAY UII → 读取到的标签UII(EPC码,30字节)
? ? FM_Lesen ? ?: BOOL; ? ? ? ? ? ? ? ? ? ??// Lesen abgeschlossen → 读取完成标志(TRUE=读取成功)
? ? FM_Schreiben: BOOL; ? ? ? ? ? ? ? ? ? ??// Schreiben abgeschlossen → 写入完成标志
? ? BUSY ? ? ? ?: BOOL; ? ? ? ? ? ? ? ? ? ??// Schreiben/Lesen aktiv → 读写忙状态(TRUE=正在执行)
? ? Stoe ? ? ? ?: BOOL; ? ? ? ? ? ? ? ? ? ??// Sammelstoerung → 汇总故障(任一故障触发为TRUE)
? ? VkStoe_Out ?: BOOL; ? ? ? ? ? ? ? ? ? ??// Verkettete Stoerung → 连锁故障输出(传递给下游设备)
END_VAR
```
#### 解读
- **数据输出**:`Data_UII`是核心输出,存储读取到的RFID标签UII(全球唯一标识)。
- **状态反馈**:`FM_Lesen`/`FM_Schreiben`告知外部“读写是否完成”,`BUSY`避免并行触发读写,`Stoe`汇总所有故障。
### 3. In-Out变量(VAR_IN_OUT):数据交互通道
#### 代码片段
```scl
VAR_IN_OUT
? ? ST_RFU_DATA : ST_RFU_DATA; ? ? ?// Empfangs- und Sendefach → RFU读写数据结构体(存储读写的标签数据)
? ? ST_BA ? ? ? : ST_Betriebsarten;?// Betriebsartenstruktur → 运行模式结构体(如设备使能、复位信号)
? ? ST_Meld ? ? : Meldungspuffer; ??// Meldestruktur → 报警缓冲区(存储故障信息)
END_VAR
```
#### 解读
- `ST_RFU_DATA`是“读写数据枢纽”:读取时从RFU接收的数据存入该结构体,写入时从该结构体取数据发送给RFU。
- `ST_BA`包含设备运行模式(如`ST_BA.SWE7`是复位使能),`ST_Meld`用于上报故障到系统报警模块。
### 4. 临时变量(VAR_Temp):逻辑计算临时存储
#### 代码片段
```scl
VAR_Temp
? ? TempPointerMsys ? ? ? ? ? ? ? ? :ANY;// Hilfsvariable → 报警指针(ANY类型兼容任意数据)
? ? TempPointerFlanken ? ? ? ? ? ? ?:ANY;// 边沿检测指针
? ? TempPointerMeldpuffer ? ? ? ? ? :ANY;// 报警缓冲区指针
? ? erg_SFC20 ? ? ? ? ? ? ? ? ? ? ? :INT;// Rueckgabewert SFC20 → SFC20(数据块复制)返回值
? ??// 指针结构体(用于访问报警数据的位/字节/字)
? ? pMeldFeld AT TempPointerMsys: STRUCT?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BYTE0 :BYTE; ?// Byte 0?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TYP ? :BYTE; ?// Daten/Parametertyp → 数据类型(1=BOOL,2=BYTE)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ANZ ? :WORD; ?// Laenge der Variablen → 变量长度
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DBNR ?:WORD; ?// DB-Nummer → 数据块编号
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? BZ ? ?:DWORD;?// Bereichszeiger → 地址指针
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?END_STRUCT;?
? ??// 循环变量与临时地址
? ? i ? ? ? ? ? : INT; ? ? ??// Loop variable → 循环计数器1
? ? j ? ? ? ? ? : INT; ? ? ??// Loop variable → 循环计数器2
? ? Address ? ? : INT; ? ? ??// Temporarily address → 临时地址
? ? Temp ? ? ? ?: INT; ? ? ??// Temporarily variable → 临时变量
? ? Start_Adr ? : INT; ? ? ??// Start address → 临时起始地址
? ? StartAdresse: INT; ? ? ??// 临时起始地址(写入用)
? ? EndAdresse ?: INT; ? ? ??// 临时结束地址(写入用)
?END_VAR
```
#### 解读
- **指针变量**:`TempPointerMsys`等`ANY`类型指针用于灵活访问报警数据块(如`pMeldFeld.BZ`指向报警数据的地址),是西门子PLC中“跨数据块访问”的常用方式。
- **循环变量**:`i`/`j`用于遍历数组(如清空缓冲区、复制标签数据)。
### 5. 静态变量(VAR):功能块内部状态保持
静态变量在功能块调用周期中保持值(不同于临时变量),核心是**TCP状态、读写流程、故障标志、可视化缓存**。
#### 代码片段(核心部分)
```scl
VAR
? ??// 可视化数据(HMI显示用)
? ? _dwVisuWerte1 : DWORD; ? ? ? ?// IP地址(如192.168.0.30 = DWORD#16#C0A8001E)
? ? _dwVisuWerte2 : DWORD; ? ? ? ?// 本地端口(低字)
? ? _dwVisuWerte3 : DWORD; ? ? ? ?// 错误号(低字)
? ? _dwVisuWerte4 : DWORD; ? ? ? ?// 状态双字(HMI显示读写状态)
? ? _dwVisuWerte5 : DWORD; ? ? ? ?// 起始地址/长度/结束地址(字节0=StartAdd,字节1=Laenge)
? ? strVisuText1 ?: STRING[64]; ??// UII读取区域(HMI显示)
? ? strVisuText2 ?: STRING[64]; ??// User数据读取区域(HMI显示)
? ??// TCP相关状态
? ? TCON_PARAM ? ? ? ? ?: TCON_PAR ;// TCP连接参数结构体(给TCON功能块用)
? ? iCommand ? ? ? ? ? ?: INT; ? ? ?// 命令选择(1=GateOn,2=GateOff,3=Write,4=Timer)
? ? STATUS_TRCV ? ? ? ? : WORD; ? ??// TRCV(接收)状态
? ? STATUS_TCON ? ? ? ? : WORD; ? ??// TCON(连接)状态
? ? xConnected ? ? ? ? ?: BOOL; ? ??// 连接建立标志(TRUE=TCP已连接)
? ? xConnReq ? ? ? ? ? ?: BOOL := True; ? ??// 连接请求(初始为TRUE,触发首次连接)
? ??// 读写流程状态
? ? xGateOn ? ? ? ? ? ? : BOOL; ? ??// Gate开启确认(RFU返回“GateOn”响应)
? ? xGateOff ? ? ? ? ? ?: BOOL; ? ??// Gate关闭确认
? ? xWrite ? ? ? ? ? ? ?: BOOL; ? ??// 写入成功确认
? ? xWriteNIO ? ? ? ? ? : BOOL; ? ??// 写入失败确认
? ? xRead ? ? ? ? ? ? ? : BOOL; ? ??// 读取成功确认(收到标签数据)
? ? xNoRead ? ? ? ? ? ? : BOOL; ? ??// 无标签确认(未读到标签)
? ? xCCError ? ? ? ? ? ?: BOOL; ? ??// CC错误(通信校验错误)
? ? xReadError ? ? ? ? ?: BOOL; ? ??// 读取错误(数据格式错误)
? ??// 故障标志
? ? F_TCPConnect ? ? ? ?: BOOL; ? ??// TCP连接故障
? ? F_TCPReceive ? ? ? ?: BOOL; ? ??// TCP接收故障
? ? F_TCPSend ? ? ? ? ? : BOOL; ? ??// TCP发送故障
? ? F_TimeOutL ? ? ? ? ?: BOOL; ? ??// 读取超时故障
? ? F_TimeOutS ? ? ? ? ?: BOOL; ? ??// 写入超时故障
? ? F_NoRead ? ? ? ? ? ?: BOOL; ? ??// 无标签故障
? ? F_CCError ? ? ? ? ? : BOOL; ? ??// CC错误故障
? ? F_ReadError ? ? ? ? : BOOL; ? ??// 读取错误故障
? ? F_Error ? ? ? ? ? ? : BOOL; ? ??// 数据接收故障
? ? F_Konfig ? ? ? ? ? ?: BOOL; ? ??// 配置错误(起始地址/长度非法)
? ??// 时序控制
? ? iSchritt ? ? ? ? ? ?: INT; ? ? ?// 流程步骤(状态机核心,0=空闲,1=GateOn,2=等待GateOn...)
? ??// 标准功能块实例
? ? R_TRIG_Lesen ? ? ? ?: R_Trig; ??// 读取触发上升沿检测
? ? F_TRIG_Lesen ? ? ? ?: F_Trig; ??// 读取触发下降沿检测
? ? TON_Timeout ? ? ? ? : TON; ? ? ?// 超时定时器(监控读写是否超时)
? ? TCP_Connect ? ? ? ? : TCON; ? ??// TCP连接功能块(西门子标准FB)
? ? TCP_Send ? ? ? ? ? ?: TSEND; ? ?// TCP发送功能块
? ? TCP_Receive ? ? ? ? : TRCV; ? ??// TCP接收功能块
? ??// 数据缓冲区
? ? TMP_Buffer ? ? ? ? ?: ARRAY [1..256] OF BYTE; ? ? ? ?// 临时缓冲区(存储标签数据)
? ? Buffer ? ? ? ? ? ? ?: ARRAY [1..256] OF BYTE; ? ? ? ?// TCP接收缓冲区(存储RFU返回的原始数据)
? ? DW_Buffer AT Buffer : ARRAY [1..64] OF DWORD; ? ? ? ?// 接收缓冲区的DWORD视图(方便4字节比对)
? ? Send_Buffer ? ? ? ? : ARRAY [1..256] OF BYTE; ? ? ? ?// TCP发送缓冲区(存储发给RFU的命令)
? ? Command AT SEND_Buffer: STRING; ? ? ? ? ? ? ? ? ? ? ?// 发送缓冲区的字符串视图(方便写命令)
END_VAR
```
三、核心逻辑(按功能模块拆解)
功能块逻辑按“可视化控制→TCP连接→标签读写→故障处理→可视化数据准备→报警上报”?顺序执行,覆盖从“触发”到“反馈”的全流程。
1.?可视化按键处理(HMI手动控制)
代码片段:
(**************************************************************************)
(************************ Visu Tasten auswerten?***************************)
(**************************************************************************)
If PC_AKTIV = DWORD#0 Then ?// PC_AKTIV=0 → HMI未激活,清空按键
? ? bVisuTasten ?:= Byte#0;
END_IF;
// 映射HMI按键(bVisuTasten=1→读取,2→无流程,3→写入,4→复位)
xS_Lesen ? ? ? ?:= (bVisuTasten = 1);
xS_oProz ? ? ? ?:= (bVisuTasten = 2);?
xS_Schreiben ? ?:= (bVisuTasten = 3);
xS_Reset ? ? ? ?:= (bVisuTasten = 4);
// 按键上升沿检测(避免长按重复触发)
R_TRIG_S_Lesen(CLK:= xS_Lesen);
R_TRIG_S_oProz(CLK:= xS_oProz); ? ? ? ? ? ??
R_TRIG_S_Schreiben(CLK:=xS_Schreiben);
R_TRIG_S_Reset(CLK:=xS_Reset);
(* An-/Abwahl ohne Prozess → “无流程”模式切换(HMI按键触发)?*)
IF R_TRIG_S_oProz.Q AND Frg_oProz THEN ?// 上升沿+外部允许
? ? xoProz := NOT xoProz; ?// 切换“无流程”状态(TRUE=激活,FALSE=取消)
? ? IF xoProz THEN ?// 激活“无流程”:清空所有数据缓冲区和可视化文本
? ? ? ? FOR i := 1 TO 256 BY 1 DO
? ? ? ? ? ? Buffer[i] := ? ?B#16#00;?
? ? ? ? END_FOR;
? ? ? ? FOR i:=1 TO 30 BY 1 DO
? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_UII[i] := B#0; ?// 清空UII数据
? ? ? ? END_FOR; ?
? ? ? ? FOR i:=1 TO 64 BY 1 DO
? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_USER[i] := B#0; ?// 清空User数据
? ? ? ? END_FOR;?
? ? ? ? // 清空HMI显示文本(UII读取区域)
? ? ? ? arrVisuText1[1] := ? B#64; ?// 字符串长度(64字节)
? ? ? ? arrVisuText1[2] := ? B#0; ? // 实际数据长度(0=空)
? ? ? ? arrVisuText3[1] := ? B#64; ?// UII写入区域
? ? ? ? arrVisuText3[2] := ? B#0;?
? ? ? ? FOR i := 1 TO 30 BY 1 DO
? ? ? ? ? ? arrVisuText1[i+2] := ? ?B#16#0;?
? ? ? ? ? ? arrVisuText3[i+2] := ? ?B#16#0; ? ? ? ? ?
? ? ? ? END_FOR;
? ? ? ? // 清空User数据显示区域
? ? ? ? arrVisuText2[1] := ? B#64; ?
? ? ? ? arrVisuText2[2] := ? B#0;?
? ? ? ? arrVisuText4[1] := ? B#64; ?
? ? ? ? arrVisuText4[2] := ? B#0;?
? ? ? ? FOR i := 1 TO 64 BY 1 DO
? ? ? ? ? ? arrVisuText2[i+2] := ? ?B#16#0;
? ? ? ? ? ? arrVisuText4[i+2] := ? ?B#16#0;
? ? ? ? END_FOR;
? ? END_IF;
END_IF;
oProz ? := xoProz; ?// “无流程”状态输出到外部
解读:
HMI按键映射:`bVisuTasten`是HMI发送的按键编码(1~4对应4个功能),通过上升沿检测(`R_TRIG_S_oProz`)避免长按重复触发。
无流程模式:激活后清空所有数据缓冲区,用于“手动调试”(无需外部触发`Lesen`/`Schreiben`,直接HMI操作)。
2.?TCP连接管理(建立/重连/状态监测)
代码片段
(**************************************************************************)
(*********************** Verbindung aufbauen ******************************)
// 复位时断开连接
IF R_TRIG_Reset.Q THEN
? ? xConnected := False;
END_IF;
// 复位结束后,触发连接请求
IF F_TRIG_Reset.Q THEN
? ? xConnReq ? ? := True;
END_IF;
// 未连接/接收故障/发送故障时,尝试建立/重连TCP
IF NOT xConnected OR F_TCPReceive OR F_TCPSend THEN?
? ??// 接收/发送故障时,按0.5Hz频率重试(避免频繁重试)
? ? IF F_TCPReceive OR F_TCPSend THEN
? ? ? ? xConnReq :=?"0,5Hz"; ?
? ? END_IF;
? ??// 配置TCON参数(TCP-native连接)
? ? TCON_PARAM.id ? ? ? ? ? ? ? := INT_TO_WORD(VerbID.ID); ? ?// 连接ID
? ? TCON_PARAM.active_est ? ? ? := True; ? ? ? ? ? ? ? ? ? ??// 主动建立连接
? ? TCON_PARAM.connection_type ?:= B#16#11; ? ? ? ? ? ? ? ? ?// 连接类型:TCP-native
? ? TCON_PARAM.local_device_id ?:= INT_TO_BYTE(VerbID.DeviceID); ?// CPU型号
? ??// 本地端口(高字节+低字节)
? ? TCON_PARAM.local_tsap_id[1] := WORD_TO_BYTE(SHR(IN:=INT_TO_WORD(VerbID.lokalPort),N:=8)); ? ?
? ? TCON_PARAM.local_tsap_id[2] := WORD_TO_BYTE(INT_TO_WORD(VerbID.lokalPort)); ? ?
? ? TCON_PARAM.rem_staddr_len ? := B#16#4; ? ? ? ? ? ? ? ? ??// 远程IP长度(4字节)
? ? TCON_PARAM.rem_tsap_id_len ?:= B#16#2; ? ? ? ? ? ? ? ? ??// 远程TSAP长度(2字节)
? ??// 远程IP(从VerbID.IP复制)
? ? TCON_PARAM.rem_staddr[1] ? ?:= INT_TO_BYTE(VerbID.IP[1]); ? ?
? ? TCON_PARAM.rem_staddr[2] ? ?:= INT_TO_BYTE(VerbID.IP[2]); ? ?
? ? TCON_PARAM.rem_staddr[3] ? ?:= INT_TO_BYTE(VerbID.IP[3]); ? ?
? ? TCON_PARAM.rem_staddr[4] ? ?:= INT_TO_BYTE(VerbID.IP[4]); ? ?
? ? TCON_PARAM.rem_tsap_id[1] ? := B#16#8; ? ? ? ? ? ? ? ? ??// 远程TSAP高字节(SICK RFU默认)
? ? TCON_PARAM.rem_tsap_id[2] ? := B#16#40; ? ? ? ? ? ? ? ? ?// 远程TSAP低字节(SICK RFU默认)
? ??// 调用TCON功能块,建立TCP连接
? ? TCP_Connect(
? ? ? ? REQ ? ? := xConnReq, ? ?// 连接请求
? ? ? ? ID ? ? ?:= TCON_PARAM.ID, ?// 连接ID
? ? ? ? CONNECT := TCON_PARAM); ??// 连接参数
? ??
? ??// 发送连接请求后,重置请求标志(避免重复发送)
? ? IF xConnReq THEN
? ? ? ? xConnReq := False;
? ? END_IF;
? ??// 连接完成/错误时,更新连接状态和故障标志
? ? IF TCP_Connect.DONE OR TCP_Connect.ERROR THEN
? ? ? ? xConnected ? := TCP_Connect.DONE; ?// DONE=TRUE→连接成功
? ? ? ? F_TCPConnect := TCP_Connect.ERROR;?// ERROR=TRUE→连接故障
? ? END_IF; ? ? ? ??
? ??// 特殊状态:STATUS=0x80A3(连接已存在),视为连接成功
? ? IF TCP_Connect.ERROR AND TCP_Connect.STATUS = W#16#80A3 THEN
? ? ? ? xConnected ? := True;
? ? ? ? F_TCPConnect := False;
? ? ? ? F_TCPReceive := False;
? ? ? ? F_TCPSend ? ?:= False;
? ? END_IF;
? ??// 记录连接状态(非0x7000=连接过程中)
? ? IF TCP_Connect.STATUS <> W#16#7000?THEN
? ? ? ? STATUS_TCON := TCP_Connect.STATUS;
? ? END_IF;
解读
连接逻辑:首次调用或连接故障时,通过`TCON`功能块主动建立TCP连接,参数`TCON_PARAM`严格匹配SICK RFU的TCP协议要求(如TSAP=0x840)。
自动重连:若`F_TCPReceive`/`F_TCPSend`(收发故障)触发,按0.5Hz频率重试,避免“死等”。
状态处理:`TCP_Connect.STATUS=0x80A3`表示“连接已存在”(重复调用TCON),此时视为连接成功,避免误判故障。
3.?标签读取流程(核心逻辑)
读取流程通过`iSchritt`(步骤)控制,顺序为:GateOn→延时→GateOff→接收标签数据→确认完成。
代码片段:
(**************************************************************************)
(************************** RECEIVE TELEGRAM ******************************)
ELSE ?// TCP已连接,执行读写逻辑
? ??// ... 省略TCP参数重配置(与连接阶段一致)…
? ? TCON_PARAM.id ? ? ? ? ? ? ? := INT_TO_WORD(VerbID.ID); ? ?//配置连接标识符(ID),将VerbID.ID(整数类型)转换为 WORD 类型,作为当前 TCP 连接的唯一标识。
? ? TCON_PARAM.active_est ? ? ? := True; ? ?//设置为 “主动建立连接” 模式(True表示主动发起连接,False表示被动等待连接)。
? ? TCON_PARAM.connection_type ?:= B#16#11; ?// 定义连接类型,B#16#11是西门子标准中 “TCP 协议” 的标识(固定值,用于指定使用 TCP 而非 UDP)。
? ? TCON_PARAM.local_device_id ?:= INT_TO_BYTE(VerbID.DeviceID); ?// CPU型号 ?
? ??// 本地端口(高字节+低字节)
? ? TCON_PARAM.local_tsap_id[1] := WORD_TO_BYTE(SHR(IN:=INT_TO_WORD(VerbID.lokalPort),N:=8)); ? ?
? ? TCON_PARAM.local_tsap_id[2] := WORD_TO_BYTE(INT_TO_WORD(VerbID.lokalPort)); ? ?
? ? TCON_PARAM.rem_staddr_len ? := B#16#4; ? ?// 远程IP长度(4字节)
? ? TCON_PARAM.rem_tsap_id_len ?:= B#16#2; ? ?// 远程TSAP长度(2字节)定义远程 TSAP 长度,B#16#2表示远程 TSAP 是 2 字节(16 位,通常对应远程端口号)。
? ?// 远程IP(从VerbID.IP复制)
? ? TCON_PARAM.rem_staddr[1] ? ?:= INT_TO_BYTE(VerbID.IP[1]); ? ?
? ? TCON_PARAM.rem_staddr[2] ? ?:= INT_TO_BYTE(VerbID.IP[2]); ? ?
? ? TCON_PARAM.rem_staddr[3] ? ?:= INT_TO_BYTE(VerbID.IP[3]); ? ?
? ? TCON_PARAM.rem_staddr[4] ? ?:= INT_TO_BYTE(VerbID.IP[4]); ? ?
? ? TCON_PARAM.rem_tsap_id[1] ? := B#16#8; ? ?// 远程TSAP高字节(SICK RFU默认)?
? ? TCON_PARAM.rem_tsap_id[2] ? := B#16#40; ? ??// 远程TSAP低字节(SICK RFU默认)
? ?//是 PLC 中用于接收 TCP 数据的函数块
? ? TCP_Receive(
? ? ? ? EN_R ? ?:= NOT F_TCPReceive, ??//接收使能信号,当F_TCPReceive(可能是 “接收故障标志”)为False时,使能接收功能(即EN_R为True时允许接收)。
? ? ? ? ID ? ? ?:= TCP_Connect.ID, ??//关联的连接 ID,与前面TCON_PARAM.id对应,指定从哪个 TCP 连接接收数据。
? ? ? ? LEN ? ? :=?0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??//接收数据长度,0表示接收缓冲区中所有可用的数据(不限制长度,直到缓冲区满)。
? ? ? ? DATA ? ?:= Buffer); ? ? ? ? ? ? ? ? ?//接收缓冲区,接收到的 TCP 数据将存储到Buffer变量中。
? ? ? ?//TCP_Receive.NDR:新数据就绪信号(True表示成功接收到新数据)。
? ? ? ?//TCP_Receive.ERROR:接收错误信号(True表示接收过程中发生错误)。
? ? ? ?//逻辑说明:当 “接收到新数据” 或 “接收出错” 时,更新F_TCPReceive标志位 —— 若接收出错,则F_TCPReceive设为True(表示故障);若成功接收,则F_TCPReceive设为False(清除故障标志)。
? ? ? ??
? ? IF TCP_Receive.NDR OR TCP_Receive.ERROR THEN
? ? ? ? F_TCPReceive := TCP_Receive.ERROR;
? ? END_IF;
? ??// 读取触发条件:外部触发/ HMI触发 + 读取使能 + 未写入 + 非“无流程”
? ? IF (R_TRIG_Lesen.Q OR R_TRIG_S_Lesen.Q OR xLesen) AND xFrgLesen AND NOT xSchreiben THEN
? ? ? ??// 步骤0:初始化(清空缓冲区、重置状态)
? ? ? ? IF iSchritt =?0?THEN
? ? ? ? ? ? xGateOn ? ? := False;
? ? ? ? ? ? xGateOff ? ?:= False;
? ? ? ? ? ? xWrite ? ? ?:= False;
? ? ? ? ? ? xWriteNIO ? := False;
? ? ? ? ? ? xRead ? ? ? := False;
? ? ? ? ? ? xNoRead ? ? := False;
? ? ? ? ? ? xCCError ? ?:= False;
? ? ? ? ? ? xReadError ?:= False;
? ? ? ? ? ? xTimer ? ? ?:= False;
? ? ? ? ? ? xFM_Lesen ? := False;
? ? ? ? ? ? FM_Lesen ? ?:= False;
? ? ? ? ? ? FM_Schreiben:= False;
? ? ? ? ? ? BUSY ? ? ? ?:= True; ?// 置位忙状态
? ? ? ? ? ? xLesen ? ? ?:= True; ?// 标记“正在读取”
? ? ? ? ? ? xLesenExt ? := R_TRIG_Lesen.Q AND NOT CFG.X0; ?// 外部读取标志
? ? ? ? ? ? iZaehler ? ?:=?0;
? ? ? ? ? ??// 清空缓冲区
? ? ? ? ? ? FOR i :=?1?TO?256?BY?1?DO
? ? ? ? ? ? ? ? ?Buffer[i] := ? ?B#16#00;
? ? ? ? ? ? ? ? ?TMP_Buffer[i] := B#16#00;?
? ? ? ? ? ? END_FOR;?
? ? ? ? ? ? FOR i:=1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_UII[i] := B#0;
? ? ? ? ? ? END_FOR; ?
? ? ? ? ? ? FOR i:=1?TO?64?BY?1?DO
? ? ? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_USER[i] := B#0;
? ? ? ? ? ? END_FOR;?
? ? ? ? ? ? iSchritt ? ?:=?1; ?// 进入步骤1:发送GateOn命令
? ? ? ? END_IF;
? ? ? ??
? ? ? ??// 步骤1:发送GateOn命令(开启RFU的读取门)
? ? ? ? IF iSchritt =?1?THEN
? ? ? ? ? ? iCommand ? ?:=?1; ? ? ? ? ??// 命令1=GateOn
? ? ? ? ? ? iSchritt ? ?:=?2; ? ? ? ? ??// 下一步:等待GateOn确认
? ? ? ? END_IF;
? ? ? ??
? ? ? ??// 步骤2:等待RFU返回“GateOn”确认
? ? ? ? IF iSchritt =?2?THEN
? ? ? ? ? ? IF xGateOn THEN ? ? ? ? ? ??// 收到GateOn确认
? ? ? ? ? ? ? ? iSchritt ? ?:=?3; ? ? ??// 下一步:启动延时
? ? ? ? ? ? ? ? xGateOn ? ? := False; ??// 重置确认标志
? ? ? ? ? ? END_IF;
? ? ? ? END_IF;
? ? ? ??
? ? ? ??// 步骤3:启动延时(等待标签进入读取范围)
? ? ? ? IF iSchritt =?3?THEN
? ? ? ? ? ? xTimer ? ? ?:= True; ? ? ? ?// 启动定时器
? ? ? ? ? ? iCommand ? ?:=?4; ? ? ? ? ??// 命令4=Timer
? ? ? ? ? ? iSchritt ? ?:=?4; ? ? ? ? ??// 下一步:等待延时结束
? ? ? ? END_IF;
? ? ? ??
? ? ? ??// 步骤4:等待延时结束(内部延时/Cfg.X0外部延时)
? ? ? ? IF iSchritt =?4?THEN
? ? ? ? ? ??// 条件:内部延时结束 或 外部读取触发下降沿+外部延时
? ? ? ? ? ? IF (NOT xLesenExt AND NOT xTimer) OR (xLesenExt AND NOT Lesen AND NOT xTimer) THEN ?
? ? ? ? ? ? ? ? iSchritt ? ?:=?5; ? ? ??// 下一步:发送GateOff命令
? ? ? ? ? ? END_IF;
? ? ? ? END_IF;
? ?
? ? ? ??// 步骤5:发送GateOff命令(关闭读取门,准备接收数据)
? ? ? ? IF iSchritt =?5?THEN
? ? ? ? ? ? iCommand ? ?:=?2; ? ? ? ? ??// 命令2=GateOff
? ? ? ? ? ? iSchritt ? ?:=?7; ? ? ? ? ??// 下一步:等待数据/故障确认
? ? ? ? END_IF;
? ? ? ??// 步骤7:等待RFU返回“数据/无标签/错误”
? ? ? ? IF iSchritt =?7?THEN
? ? ? ? ? ??// 收到数据/无标签/CC错误/读取错误,进入下一步
? ? ? ? ? ? IF xRead OR xNoRead OR xCCError OR xReadError THEN?
? ? ? ? ? ? ? ? iSchritt ? ?:=?6; ? ? ??
? ? ? ? ? ? END_IF;
? ? ? ? END_IF;
? ? ? ??
? ? ? ??// 步骤6:等待GateOff确认,完成读取
? ? ? ? IF iSchritt =?6?THEN
? ? ? ? ? ? IF xGateOff THEN?// 收到GateOff确认
? ? ? ? ? ? ? ? iSchritt ? ?:=?0; ? ? ??// 回到空闲步骤
? ? ? ? ? ? ? ??// 记录故障状态
? ? ? ? ? ? ? ? F_NoRead ? ?:= xNoRead;
? ? ? ? ? ? ? ? F_CCError ? := xCCError;
? ? ? ? ? ? ? ? F_ReadError := xReadError;
? ? ? ? ? ? ? ? xFM_Lesen ? := xRead; ??// 读取成功标志
? ? ? ? ? ? ? ? FM_Lesen ? ?:= xRead; ??// 输出读取完成(TRUE=成功)
? ? ? ? ? ? ? ? BUSY ? ? ? ?:= False; ??// 复位忙状态
? ? ? ? ? ? ? ??// 重置所有状态标志
? ? ? ? ? ? ? ? xGateOn ? ? := False;
? ? ? ? ? ? ? ? xGateOff ? ?:= False;
? ? ? ? ? ? ? ? xWrite ? ? ?:= False;
? ? ? ? ? ? ? ? xWriteNIO ? := False;
? ? ? ? ? ? ? ? xRead ? ? ? := False;
? ? ? ? ? ? ? ? xNoRead ? ? := False;
? ? ? ? ? ? ? ? xCCError ? ?:= False;
? ? ? ? ? ? ? ? xReadError ?:= False;
? ? ? ? ? ? ? ? xTimer ? ? ?:= False;
? ? ? ? ? ? ? ? xLesen ? ? ?:= False;?
? ? ? ? ? ? END_IF;
? ? ? ? END_IF;
? ? END_IF; ? ? ? ? ? ?
```
#### 关键命令与响应解析
| 步骤 | 命令(iCommand) | 发送内容(Send_Buffer) ? ? ? | RFU响应 ? ? ? ? ? ? ? ?| 处理逻辑 ? ? ? ? ? ? ? ? ? ? |
|------|------------------|--------------------------------|------------------------|------------------------------|
| 1 ? ?| 1(GateOn) ? ? ?| `[STX]sMN mTCgateon[ETX]` ? ? ?| `[STX]sAN mTCgate on?1[ETX]` | 置位`xGateOn`,确认门开启 ? ?|
| 5 ? ?| 2(GateOff) ? ? | `[STX]sMN mTCgateoff[ETX]` ? ? | `[STX]sAN mTCgate off?1[ETX]`| 置位`xGateOff`,确认门关闭 ? ?|
| 7 ? ?| - ? ? ? ? ? ? ? ?| - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| `[STX]RSS...[ETX]` ? ? | 置位`xRead`,解析UII/User数据|
| 7 ? ?| - ? ? ? ? ? ? ? ?| - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| `[STX]NoRead[CR]` ? ? ?| 置位`xNoRead`,无标签故障 ? ?|
- **Gate控制**:`GateOn`/`GateOff`是SICK RFU的专用命令,用于控制“读取门”(只有门开启时,RFU才会扫描标签)。
- **数据解析**:RFU返回`[STX]RSS...[ETX]`表示读取到标签数据,后续代码会将ASCII格式的UII/User数据转换为HEX格式,存入`ST_RFU_DATA.READ_TAG`。
4.?标签写入流程(核心逻辑)
写入流程顺序:发送写命令→等待写入确认→GateOn→延时→GateOff→确认完成,代码结构与读取类似,核心差异是“发送写数据”。
代码片段(关键步骤):
// 写入触发条件:外部触发/HMI触发 + 读取完成 + 写入使能 + 未读取
IF (((R_TRIG_Schreiben.Q OR R_TRIG_S_Schreiben.Q) AND FM_Lesen) OR xSchreiben) AND xFrgSchreiben AND NOT xLesen THEN
? ??
? ? ?// 步骤0:初始化(准备写入数据)
? ? IF iSchritt =?0? THEN
? ? ? ??// ... 省略状态重置(同读取步骤0)...
? ? ? ? BUSY ? ? ? ?:= True;
? ? ? ? xSchreiben ?:= True;?
? ? ? ? iSchritt ? ?:=?8; ? ? ? ?
? ? ? ??// HMI写入:从arrVisuText4(HMI输入)复制数据到arrTempText
? ? ? ? IF R_TRIG_S_Schreiben.Q THEN ? ? ? ? ? ?
? ? ? ? ? ? j :=?2;
? ? ? ? ? ? FOR i := StartAdd *2?TO iEndAdd *2?BY?1?DO
? ? ? ? ? ? ? ? arrTempText[j] := arrVisuText4[i+2];
? ? ? ? ? ? ? ? j := j+1;
? ? ? ? ? ? END_FOR;
? ? ? ? ? ? arrTempText[1] ?:= B#64; ?// 字符串长度
? ? ? ? ? ? arrTempText[2] ?:= INT_TO_BYTE(Laenge *2); ?// 数据长度(字节)
? ? ? ? END_IF;
? ? ? ??// 外部写入:从ST_RFU_DATA.Write_TAG(外部输入)复制数据
? ? ? ? IF R_TRIG_Schreiben.Q THEN ? ? ? ? ? ? ?
? ? ? ? ? ? j :=?2;
? ? ? ? ? ? FOR i := StartAdd *2?TO iEndAdd *2?BY?1?DO
? ? ? ? ? ? ? ? arrTempText[j] := ST_RFU_DATA.Write_TAG.DATA_USER[i];?
? ? ? ? ? ? ? ? j := j+1;
? ? ? ? ? ? END_FOR;
? ? ? ? ? ? arrTempText[1] ?:= B#64;
? ? ? ? ? ? arrTempText[2] ?:= INT_TO_BYTE(Laenge *2);
? ? ? ? END_IF;
? ? END_IF;
? ??// 步骤8:发送写入命令(TAwriteTagData)
? ? IF iSchritt =?8?THEN
? ? ? ? iCommand ? ?:=?3; ? ? ? ? ??// 命令3=Write
? ? ? ? iSchritt ? ?:=?9; ? ? ? ? ??// 下一步:等待写入确认
? ? END_IF;
? ??
? ??// 步骤9:等待写入成功/失败确认
? ? IF iSchritt =?9?THEN
? ? ? ? IF xWrite ?THEN ? ? ? ? ? ??// 写入成功
? ? ? ? ? ? iSchritt ? ?:=?10; ? ? ??
? ? ? ? ? ? xWrite ? ? ?:= False;?
? ? ? ? ELSIF xWriteNIO THEN ? ? ? ?// 写入失败
? ? ? ? ? ? iSchritt ? ?:=?0; ? ? ??
? ? ? ? ? ? xWriteNIO ? := False;?
? ? ? ? END_IF;
? ? END_IF;
? ??
? ? ? ?//这段代码是 FB_Sick_RFU 功能块中写入流程后续的标签验证读取逻辑,属于状态机控制的一部分(iSchritt为步骤变量,10-16 为验证读取的特定步骤)。
? ? ?//其核心作用是:在完成标签写入操作后,自动触发一次标签读取,验证写入的数据是否正确,形成 “写入→读取验证” 的闭环,确保数据写入可靠性。
? ??// 状态机步骤控制:标签写入后的验证读取流程(确保写入数据可被正确读取)
? ??// 核心逻辑:写入完成后自动执行"开读取门→等待→关读取门→验证数据"的闭环
? ??// 步骤10:启动读取门(为读取标签做准备)
? ? IF iSchritt =?10?THEN
? ? ? ? iCommand :=?1; ? ? ? ? ? ? ?// 触发GateOn命令(对应CASE iCommand中的1:开启RFU读取门)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// RFU收到该命令后,开始激活天线扫描标签
? ? ? ? iSchritt :=?11; ? ? ? ? ? ??// 步骤跳转至11,等待读取门开启的确认信号
? ? END_IF;
? ??// 步骤11:等待读取门开启确认(确保RFU已进入就绪状态)
? ? IF iSchritt =?11?THEN
? ? ? ? IF xGateOn THEN ? ? ? ? ? ??// xGateOn为TRUE:RFU已返回GateOn确认响应(响应解析逻辑设置)
? ? ? ? ? ? iSchritt ? ?:=?12; ? ? ?// 读取门已开启,跳转至步骤12(启动读取前延时)
? ? ? ? ? ? xGateOn ? ? := False; ??// 复位确认标志(避免因响应残留导致重复触发)
? ? ? ? END_IF;
? ? END_IF;
? ??// 步骤12:启动读取前延时(确保标签稳定进入读取范围)
? ? IF iSchritt =?12?THEN
? ? ? ? xTimer ? := True; ? ? ? ? ??// 置位定时器启动标志
? ? ? ? iCommand :=?4; ? ? ? ? ? ? ?// 触发Timer命令(对应CASE iCommand中的4:启动1秒延时)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 延时目的:生产线场景中,标签可能需要时间移动到RFU天线正下方
? ? ? ? iSchritt :=?13; ? ? ? ? ? ??// 跳转至步骤13,等待延时结束
? ? END_IF;
? ??// 步骤13:等待延时结束(确保标签处于最佳读取位置)
? ? IF iSchritt =?13?THEN
? ? ? ? IF NOT xTimer THEN ? ? ? ? ?// xTimer为FALSE:1秒延时已结束(由TON_Timer.Q触发复位)
? ? ? ? ? ? iSchritt ? ?:=?14; ? ? ?// 延时完成,跳转至步骤14(关闭读取门)
? ? ? ? END_IF;
? ? END_IF;
? ??// 步骤14:关闭读取门(停止扫描,准备返回已读取数据)
? ? IF iSchritt =?14?THEN
? ? ? ? iCommand :=?2; ? ? ? ? ? ? ?// 触发GateOff命令(对应CASE iCommand中的2:关闭RFU读取门)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 关闭后RFU停止扫描新标签,开始处理已捕获的标签数据
? ? ? ? iSchritt :=?16; ? ? ? ? ? ??// 跳转至步骤16,等待RFU返回读取结果
? ? END_IF;
? ??// 步骤16分支1:处理读取成功的情况(验证写入数据有效)
? ? IF iSchritt =?16?THEN
? ? ? ? IF xRead THEN ? ? ? ? ? ? ??// xRead为TRUE:RFU成功返回标签数据(UII+User区已解析)
? ? ? ? ? ? iSchritt ? ?:=?0; ? ? ??// 回归初始步骤(整个"写入→验证"流程结束)
? ? ? ? ? ? xRead ? ? ? := False; ??// 复位读取成功标志
? ? ? ? ? ? FM_Lesen ? ?:= True; ? ?// 置位读取完成标志(通知HMI/外部模块:验证读取成功)
? ? ? ? ? ? FM_Schreiben:= True; ? ?// 置位写入完成标志(通知外部:写入+验证全流程成功)
? ? ? ? ? ? BUSY ? ? ? ?:= False; ??// 复位忙状态(允许新的读写操作触发)
? ? ? ? ? ? xSchreiben ?:= False; ??// 复位写入触发标志(避免重复写入)
? ? ? ? END_IF;
? ? END_IF;
? ??// 步骤16分支2:处理所有可能的读取结果(成功/失败均需流转)
? ? IF iSchritt =?16?THEN
? ? ? ??// 覆盖所有读取结果:成功(xRead)、无标签(xNoRead)、校验错(xCCError)、数据错(xReadError)
? ? ? ? IF xRead OR xNoRead OR xCCError OR xReadError THEN?
? ? ? ? ? ? iSchritt ? ?:=?15; ? ? ?// 跳转至步骤15(异常处理步骤,代码中未展示,推测逻辑:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 1. 若xNoRead/xCCError/xReadError:置位对应故障标志(如F_NoRead)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 2. 触发重试机制或上报报警
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 3. 最终回归初始步骤iSchritt=0
? ? ? ? END_IF;
? ? END_IF;
? ??
? ??
? ??// 步骤15:写入完成,复位状态
? ? IF iSchritt =?15?THEN
? ? ? ? IF xGateOff THEN?
? ? ? ? ? ? iSchritt ? ?:=?0; ? ? ??
? ? ? ? ? ??// 记录故障
? ? ? ? ? ? F_NoRead ? ?:= xNoRead;
? ? ? ? ? ? F_CCError ? := xCCError;
? ? ? ? ? ? F_ReadError := xReadError;
? ? ? ? ? ? xFM_Lesen ? := xRead;
? ? ? ? ? ? FM_Lesen ? ?:= xRead; ? ? ??// 读取完成(写入后需确认数据)
? ? ? ? ? ? FM_Schreiben:= xRead; ? ? ??// 写入完成(TRUE=成功)
? ? ? ? ? ? BUSY ? ? ? ?:= False;
? ? ? ? ? ? xGateOn ? ? := False;
? ? ? ? ? ? xGateOff ? ?:= False;
? ? ? ? ? ? xWrite ? ? ?:= False;
? ? ? ? ? ? xWriteNIO ? := False;
? ? ? ? ? ? xRead ? ? ? := False;
? ? ? ? ? ? xNoRead ? ? := False;
? ? ? ? ? ? xCCError ? ?:= False;
? ? ? ? ? ? xReadError ?:= False;
? ? ? ? ? ? xTimer ? ? ?:= False;
? ? ? ? ? ? xSchreiben ?:= False;?
? ? ? ? END_IF;
? ? END_IF;
END_IF; ? ? ? ?
5、SICK RFU命令生成
(CASE?iCommand?OF)iCommand`是命令选择变量(1=GateOn、2=GateOff、3=Write、4=Timer),通过`CASE`语句生成对应TCP发送命令,遵循SICK RFU的**ASCII通信协议**(命令需包含`[STX]`(0x02)起始符和`[ETX]`(0x03)结束符)。
### 1. 命令1:GateOn(开启RFU读取门)
? ? ? ??#### 代码片段
? ? CASE iCommand OF
? ? ? ??
? ? ? ? ```scl
? ? ? ??// GateOn
? ? ? ??1: ?Command ? ? ?:=?'sMN mTCgateon'; ? ?// Trigger on command → SICK RFU开门命令
? ? ? ? ? ? Send_Buffer[1] ? ?:= B#16#00; ? ? ? // Overwrite string length → 覆盖字符串长度(用自定义长度)
? ? ? ? ? ? Send_Buffer[2] ? ?:= B#16#02; ? ? ? // Add [STX] → 添加起始符(ASCII码0x02,通信协议要求)
? ? ? ? ? ? Send_Buffer[16] ? := B#16#03; ? ? ? // Add [ETX] → 添加结束符(ASCII码0x03)
? ? ? ? ? ? TCP_SEND.LEN ? ? ?:=?16; ? ? ? ? ? ?// 发送数据长度(16字节:[STX]+命令+[ETX])
? ? ? ? ? ? xCreateCommand ? ?:= True; ? ? ? ? ?// 命令生成完成标志(触发后续TCP发送)
? ? ? ? ```
? ? ? ??#### 解读
? ? ? ? - **命令含义**:`sMN mTCgateon`是SICK RFU的标准命令,用于开启“读取门”(只有门开启时,RFU才会扫描标签)。
? ? ? ? - **格式细节**:
? ? ? ? ? - `Send_Buffer[1] =?0x00`:覆盖`Command`字符串默认的长度字节(PLC字符串前2字节为长度,此处用自定义长度);
? ? ? ? ? - `Send_Buffer[2] =?0x02`:插入`[STX]`,告知RFU“命令开始”;
? ? ? ? ? - `Send_Buffer[16] =?0x03`:插入`[ETX]`,告知RFU“命令结束”;
? ? ? ? ? - 总长度16字节:`[STX]`(1)+`sMN mTCgateon`(13)+`[ETX]`(1)+ 填充(1)=16字节。
? ? ? ? - **触发后续**:`xCreateCommand=True`触发下方`TCP_Send`执行发送。
? ? ? ??
? ? ? ??### 2. 命令2:GateOff(关闭RFU读取门)
? ? ? ??#### 代码片段
? ? ? ? ```scl
? ? ? ??// GateOff
? ? ? ??2: ?Command ? ? ?:=?'sMN mTCgateoff'; ??// Trigger on command → SICK RFU关门命令
? ? ? ? ? ? Send_Buffer[1] ? ?:= B#16#00; ? ? ? // Overwrite string length
? ? ? ? ? ? Send_Buffer[2] ? ?:= B#16#02; ? ? ? // Add [STX]
? ? ? ? ? ? Send_Buffer[17] ? := B#16#03; ? ? ? // Add [ETX]
? ? ? ? ? ? TCP_SEND.LEN ? ? ?:=?17; ? ? ? ? ? ?// 发送长度17字节(命令比GateOn多1个字符“f”)
? ? ? ? ? ? xCreateCommand ? ?:= True;
? ? ? ? ```
? ? ? ??#### 解读
? ? ? ? - **核心差异**:命令`mTCgateoff`比`mTCgateon`多1个字符(“f”),因此`[ETX]`位置从16移到17,总长度变为17字节。
? ? ? ? - **功能作用**:关闭读取门后,RFU停止扫描标签,准备返回已读取的标签数据(或“无标签”响应)。
? ? ? ??
? ? ? ??### 3. 命令3:Write(写入数据到RFID标签)
? ? ? ??#### 代码片段(核心复杂命令,分步骤解析)
? ? ? ? ```scl
? ? ? ??// Write
? ? ? ??3: ?Command :=?'sMN TAwriteTagData +64 '; ??// Write tag command → SICK RFU写标签命令(+64是标签地址前缀)
? ? ? ? ? ? Start_Adr :=?1;?// Set Start adress → 发送缓冲区起始地址(从1开始)
? ? ? ? ? ? j:=16;
? ? ? ??
? ? ? ? ? ??// 步骤1:从TMP_Buffer复制基础参数(标签地址相关)
? ? ? ? ? ? FOR i:=?25?TO ?88?DO
? ? ? ? ? ? ? ? Send_Buffer[Start_Adr+i] := TMP_Buffer[i-9];?// 复制标签地址参数到发送缓冲区
? ? ? ? ? ? END_FOR;
? ? ? ??
? ? ? ? ? ??// 步骤2:拼接写入配置参数(Bank、起始地址、长度、重试次数)
? ? ? ? ? ? Send_Buffer[Start_Adr+89] ? := B#16#20; ? ? ? ? // Add blank → 空格分隔
? ? ? ? ? ? Send_Buffer[Start_Adr+90] ? := B#16#33; ? ? ? ? // Add Bank = 3 → 写入区域:Bank3(用户数据区,SICK RFU固定)
? ? ? ? ? ? Send_Buffer[Start_Adr+91] ? := B#16#20; ? ? ? ? // Add blank
? ? ? ? ? ? Send_Buffer[Start_Adr+92] ? := arrStartAdd[3]; ?// Add Pointer(起始地址,如0)→ 从输入变量StartAdd转换的字符串
? ? ? ? ? ? Send_Buffer[Start_Adr+93] ? := arrStartAdd[4]; ?
? ? ? ? ? ? Send_Buffer[Start_Adr+94] ? := arrStartAdd[5]; ?
? ? ? ? ? ? Send_Buffer[Start_Adr+95] ? := arrStartAdd[6]; ?
? ? ? ? ? ? Send_Buffer[Start_Adr+96] ? := B#16#20; ? ? ? ? // Add blank
? ? ? ? ? ? Send_Buffer[Start_Adr+97] ? := arrLaenge[3]; ? ?// Add Wordcounter(写入长度,如32字)→ 从输入变量Laenge转换
? ? ? ? ? ? Send_Buffer[Start_Adr+98] ? := arrLaenge[4]; ? ?
? ? ? ? ? ? Send_Buffer[Start_Adr+99] ? := arrLaenge[5]; ? ?
? ? ? ? ? ? Send_Buffer[Start_Adr+100] ?:= arrLaenge[6]; ? ?
? ? ? ? ? ? Send_Buffer[Start_Adr+101] ?:= B#16#20; ? ? ? ? // Add blank?
? ? ? ? ? ? Send_Buffer[Start_Adr+102] ?:= B#16#33; ? ? ? ? // Add Retries(频率重试次数=3)
? ? ? ? ? ? Send_Buffer[Start_Adr+103] ?:= B#16#32; ? ? ? ? // Add Retries(信道跳变重试次数=2)
? ? ? ? ? ? Send_Buffer[Start_Adr+104] ?:= B#16#20; ? ? ? ? // Add blank?
? ? ? ? ? ? Send_Buffer[Start_Adr+105] ?:= arrDataL[3]; ? ??// Add Datalength(数据长度,如128字节)→ Laenge*4
? ? ? ? ? ? Send_Buffer[Start_Adr+106] ?:= arrDataL[4]; ? ??
? ? ? ? ? ? Send_Buffer[Start_Adr+107] ?:= arrDataL[5]; ? ??
? ? ? ? ? ? Send_Buffer[Start_Adr+108] ?:= arrDataL[6];
? ? ? ? ? ? ?
? ? ? ? ? ??// 步骤3:处理数据长度字符串的空格(避免格式错误)
? ? ? ? ? ? IF arrDataL[5] = BYTE#16#20 THEN ?// 数据长度字符串第5位是空格(短长度,如“ ?32”)
? ? ? ? ? ? ? ? Send_Buffer[Start_Adr+107] := B#16#20; ?// 填充空格
? ? ? ? ? ? ? ? Address :=109; ?// 数据起始地址设为109
? ? ? ? ? ? ELSIF arrDataL[6] = BYTE#16#20 THEN ?// 第6位是空格(中长度,如“ 128”)
? ? ? ? ? ? ? ? Send_Buffer[Start_Adr+108] := B#16#20;
? ? ? ? ? ? ? ? Address :=110;
? ? ? ? ? ? ELSE ?// 无空格(长长度,如“1024”)
? ? ? ? ? ? ? ? Send_Buffer[Start_Adr+109] ?:= B#16#20;
? ? ? ? ? ? ? ? Address :=?111;
? ? ? ? ? ? END_IF; ??
? ? ? ? ? ? ?
? ? ? ? ? ??// 步骤4:HEX→ASCII HEX转换(RFU仅接受ASCII格式的十六进制数据)
? ? ? ? ? ? StartAdresse :=?3; ?// 待转换数据的起始地址(arrTempText的3号位置)
? ? ? ? ? ? EndAdresse := (Laenge *?2) +?2; ?// 待转换数据的结束地址(长度×2:1字节=2个ASCII字符)
? ? ? ? ? ? FOR j:= StartAdresse TO EndAdresse DO
? ? ? ? ? ? ? ? Temp :=?BYTE_TO_INT(SHR(IN:=arrTempText[j],?N:=4)); ?// 取字节的高4位(如0x31→0x3)
? ? ? ? ? ? ? ? FOR i:=1?TO?2?DO ?// 转换高4位→ASCII,再转换低4位→ASCII
? ? ? ? ? ? ? ? ? ? IF Temp >?9?THEN ?// 高4位是A-F(10-15)→ ASCII码65-70(A-F)=10+55=65
? ? ? ? ? ? ? ? ? ? ? ? Temp := Temp +?55;
? ? ? ? ? ? ? ? ? ? ELSE ?// 高4位是0-9→ASCII码48-57(0-9)=0+48=48
? ? ? ? ? ? ? ? ? ? ? ? Temp := Temp +?48;
? ? ? ? ? ? ? ? ? ? END_IF;
? ? ? ? ? ? ? ? ? ? Send_Buffer[Address] :=?INT_TO_BYTE(Temp); ?// 存入发送缓冲区
? ? ? ? ? ? ? ? ? ? Address := Address+1;
? ? ? ? ? ? ? ? ? ? Temp ? ?:=?BYTE_TO_INT(arrTempText[j] AND B#16#0F); ?// 取字节的低4位(如0x31→0x1)
? ? ? ? ? ? ? ? END_FOR; ? ? ? ? ? ??
? ? ? ? ? ? END_FOR;
? ? ? ??
? ? ? ? ? ??// 步骤5:添加通信协议头/尾,设置发送长度
? ? ? ? ? ? Send_Buffer[1] ?:= B#16#00; ? ? // Overwrite string length
? ? ? ? ? ? Send_Buffer[2] ?:= B#16#02; ? ? // Add [STX]
? ? ? ? ? ? Send_Buffer[Address] := B#16#03; ? ?// Add [ETX] ??
? ? ? ? ? ? TCP_SEND.LEN ? ?:= Address +?1; ? ? ? ??// 总发送长度(地址+1:包含[ETX])
? ? ? ? ? ? xCreateCommand ?:= True;
? ? ? ? ```
? ? ? ??#### 解读(分核心要点)
? ? ? ? - **命令基础**:`sMN TAwriteTagData +64`是SICK RFU写标签的标准前缀,`+64`指定标签的基础地址段。
? ? ? ? - **关键参数含义**:
? ? ? ? ? - `Bank=3`:SICK RFU的标签存储分区(Bank3为**用户数据区**,可自定义写入数据;其他Bank为系统区,不可写);
? ? ? ? ? - `Pointer`:写入起始地址(对应输入变量`StartAdd`,0-31,单位:字);
? ? ? ? ? - `Wordcounter`:写入长度(对应输入变量`Laenge`,1-(32-StartAdd),单位:字);
? ? ? ? ? - `Datalength`:数据总字节数(`Laenge×4`,因1字=4字节);
? ? ? ? ? - `Retries=3/2`:发送失败时的重试策略(频率重试3次,信道跳变重试2次,提高写入成功率)。
? ? ? ? - **数据格式转换(核心)**:
? ? ? ? ? - RFU要求写入数据为**ASCII HEX格式**(如字节0x31需转为ASCII字符“3”和“1”);
? ? ? ? ? - 转换逻辑:1字节拆为“高4位+低4位”,分别转为ASCII(0-9→48-57,A-F→65-70);
? ? ? ? ? - 示例:字节`0x31`(十进制49)→ 高4位`0x3`(3→ASCII?51=“3”)、低4位`0x1`(1→ASCII?49=“1”)→ 最终发送“31”。
? ? ? ? - **长度动态计算**:`Address`随数据长度动态递增,最终`TCP_SEND.LEN=Address+1`(包含`[ETX]`),确保命令格式正确。
? ? ? ??
? ? ? ??### 4. 命令4:Timer(启动延时定时器)
? ? ? ??#### 代码片段
? ? ? ? ```scl
? ? ? ??// Timer
? ? ? ??4: ?xTimer ? ? ?:= True; ?// 启动延时标志
? ? ? ? ? ? iCommand ? ?:=?0; ? ??// 重置命令变量(避免重复执行)
? ? ? ? END_CASE;
? ? ? ??#### 解读
? ? ? ? - **功能作用**:用于“GateOn后等待标签进入读取范围”(如生产线物料移动需要时间),下方`TON_Timer`会以1秒延时(`PT:=t#1s`)触发`xTimer=False`,表示延时结束。
? ? ? ? - **与其他命令差异**:不生成TCP发送命令,仅控制内部定时器,是流程时序的“等待节点”。
6、TCP数据发送与状态处理(TCP_Send)
生成命令后,通过西门子标准FB `TCP_Send`将`Send_Buffer`中的命令发送给SICK RFU,并处理发送结果。
代码片段:
// 延时定时器(命令4触发,1秒延时)
? ? ? ? TON_Timer(IN:=xTimer, PT:= t#1s);
? ? ? ? IF TON_Timer.Q THEN
? ? ? ? ? ? xTimer := False; ?// 延时结束,重置延时标志
? ? ? ? END_IF;
? ? ? ??// 命令生成完成后,触发TCP发送
? ? ? ? IF xCreateCommand THEN ? ? ? ??
? ? ? ? ? ? xSendReq ? ? ? ?:= True; ?// 发送请求置位
? ? ? ? ? ? iCommand ? ? ? ?:=?0; ? ??// 重置命令变量
? ? ? ? ? ? xCreateCommand ?:= False;?// 重置命令生成标志
? ? ? ? END_IF;
? ? ? ??// 调用TCP_Send功能块,发送命令到RFU
? ? ? ? TCP_Send(
? ? ? ? ? ? REQ ? ? := xSendReq, ? ?// 发送请求(上升沿触发)
? ? ? ? ? ? ID ? ? ?:= TCON_PARAM.ID, ?// 连接ID(与TCP_Connect一致)
? ? ? ? ? ? DATA ? ?:= SEND_Buffer); ??// 发送缓冲区(存储生成的命令)
? ? ? ? ??
? ? ? ? Status_TRCV := TCP_Send.STATUS; ?// 记录发送状态(用于调试)
? ? ? ??
? ? ? ??// 发送请求触发后,立即重置(避免重复发送)
? ? ? ? IF xSendReq THEN
? ? ? ? ? ? xSendReq := False;
? ? ? ? END_IF; ?
? ? ? ??
? ? ? ??// 处理发送结果(成功/失败)
? ? ? ? IF TCP_Send.DONE OR TCP_Send.Error THEN
? ? ? ? ? ? F_TCPSend := TCP_Send.Error; ?// 发送失败则置位发送故障标志
? ? ? ? END_IF;
? ? ? ??```
? ? ? ? ### 解读
? ? ? ? - **发送触发逻辑**:`xCreateCommand=True`→`xSendReq=True`→`TCP_Send`执行发送,发送后`xSendReq=False`(避免因`xCreateCommand`持续为True导致重复发送)。
? ? ? ? - **状态处理**:
? ? ? ? ? - `TCP_Send.DONE=True`:发送成功,`F_TCPSend=False`(无故障);
? ? ? ? ? - `TCP_Send.Error=True`:发送失败(如网络中断),`F_TCPSend=True`(置位发送故障,触发后续TCP重连);
? ? ? ? ? - `Status_TRCV`:记录发送状态码(如0x0000=成功,0x8090=连接不存在),用于调试排查问题。
? ? ? ??
7、SICK RFU响应解析(TCP_Receive.NDR)
RFU接收命令后,会返回响应数据(如“GateOn确认”“标签数据”“无标签”),通过`TCP_Receive`接收后,解析响应内容并更新对应状态标志(如`xGateOn`、`xRead`)。
代码片段(核心响应分类解析):
// 接收缓冲区有新数据(NDR=True)且接收未忙时,解析响应
? ? ? ? IF (*BUSY AND*)NOT TCP_Receive.BUSY AND TCP_Receive.NDR THEN ? ??
? ? ? ? ? ??// 1. 响应1:GateOn确认(RFU已开启读取门)
? ? ? ? ? ? IF ?DW_Buffer[1] = DW#16#0273414E AND ??// '[STX]sAN'(sAN=命令确认前缀)
? ? ? ? ? ? ? ? DW_Buffer[2] = DW#16#206D5443 AND ??// ?' mTC'
? ? ? ? ? ? ? ? DW_Buffer[3] = DW#16#67617465?AND ??// ?'gate'
? ? ? ? ? ? ? ? DW_Buffer[4] = DW#16#6F6E2031 AND ??// ?'on 1'(1=成功)
? ? ? ? ? ? ? ? Buffer[17] ? = B#16#03? ? ? ? ? ? ??// ?'[ETX]'?
? ? ? ? ? ? ? ? THEN
? ? ? ? ? ? ? ? xGateOn := True; ?// 置位GateOn确认标志,触发下一步流程
? ? ? ? ? ??
? ? ? ? ? ??// 2. 响应2:GateOff确认(RFU已关闭读取门)
? ? ? ? ? ? ELSIF DW_Buffer[1] = DW#16#0273414E AND?// '[STX]sAN'
? ? ? ? ? ? ? ? ? DW_Buffer[2] = DW#16#206D5443 AND?// ?' mTC'
? ? ? ? ? ? ? ? ? DW_Buffer[3] = DW#16#67617465?AND?// ?'gate'
? ? ? ? ? ? ? ? ? DW_Buffer[4] = DW#16#6F666620 AND?// ?'off '
? ? ? ? ? ? ? ? ? Buffer[17] ? = B#16#31? ? ? ? AND?// ?'1'(成功)
? ? ? ? ? ? ? ? ? Buffer[18] ? = B#16#03? ? ? ? ? ??// ?'[ETX]'
? ? ? ? ? ? ? ? ? THEN
? ? ? ? ? ? ? ? xGateOff := True; ?// 置位GateOff确认标志
? ? ? ? ? ??
? ? ? ? ? ??// 3. 响应3:Write成功确认(数据已写入标签)
? ? ? ? ? ? ELSIF DW_Buffer[1] = DW#16#0273414e AND?// '[STX]sAN'
? ? ? ? ? ? ? ? ? DW_Buffer[2] = DW#16#20544177?AND?// ?' TAw'(TAwriteTagData前缀)
? ? ? ? ? ? ? ? ? DW_Buffer[3] = DW#16#72697465?AND?// ?'rite'
? ? ? ? ? ? ? ? ? DW_Buffer[4] = DW#16#54616744?AND?// ?'TagD'
? ? ? ? ? ? ? ? ? DW_Buffer[5] = DW#16#61746120?AND?// ?'ata '
? ? ? ? ? ? ? ? ? Buffer[21] ? = B#16#31? ? ? ? ? ??// ?'1'(成功)
? ? ? ? ? ? ? ? ? THEN
? ? ? ? ? ? ? ? xWrite := True; ?// 置位写入成功标志
? ? ? ? ? ??
? ? ? ? ? ??// 4. 响应4:Write失败确认(数据未写入)
? ? ? ? ? ? ELSIF DW_Buffer[1] = DW#16#0273414e AND?// '[STX]sAN'
? ? ? ? ? ? ? ? ? DW_Buffer[2] = DW#16#20544177?AND?// ?' TAw'
? ? ? ? ? ? ? ? ? DW_Buffer[3] = DW#16#72697465?AND?// ?'rite'
? ? ? ? ? ? ? ? ? DW_Buffer[4] = DW#16#54616744?AND?// ?'TagD'
? ? ? ? ? ? ? ? ? DW_Buffer[5] = DW#16#61746120?AND?// ?'ata '
? ? ? ? ? ? ? ? ? Buffer[21] ? = B#16#30? ? ? ? ? ??// ?'0'(失败)
? ? ? ? ? ? ? ? ? THEN
? ? ? ? ? ? ? ? xWriteNIO := True; ?// 置位写入失败标志
? ? ? ??
? ? ? ? ? ??// 5. 响应5:Read成功(接收到标签数据)
? ? ? ? ? ? ELSIF DW_Buffer[1] = DW#16#02525353? ? ?// '[STX]RSS'(RSS=数据响应前缀)
? ? ? ? ? ? ? ? ? THEN
? ? ? ??
? ? ? ? ? ? ? ??// 步骤1:解析UII数据(30字节,标签唯一标识)
? ? ? ? ? ? ? ? (*Convert ASCII HEX --> HEX , UII Area*)?
? ? ? ? ? ? ? ? FOR j:=1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? ? ? IF j =?1?THEN?
? ? ? ? ? ? ? ? ? ? ? ? Address :=?20; ?// UII数据在Buffer中的起始地址(偏移20)
? ? ? ? ? ? ? ? ? ? END_IF;
? ? ? ? ? ? ? ? ? ??// 1个UII字节对应Buffer中2个ASCII字符(ASCII HEX→HEX)
? ? ? ? ? ? ? ? ? ? FOR i:=1?TO?2?BY?1?DO
? ? ? ? ? ? ? ? ? ? ? ? Temp :=BYTE_TO_INT(Buffer[Address])-48; ?// ASCII→数值(如“3”→51-48=3)
? ? ? ? ? ? ? ? ? ? ? ? IF Temp >?9?THEN
? ? ? ? ? ? ? ? ? ? ? ? ? ? Temp := Temp-7; ?// A-F→ASCII 65-70→数值10-15(65-48-7=10)
? ? ? ? ? ? ? ? ? ? ? ? END_IF;
? ? ? ? ? ? ? ? ? ? ? ??// 拼接高4位+低4位→1字节(如“3”→3<<4=48,“1”→1→48+1=49=0x31)
? ? ? ? ? ? ? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_UII[j] := SHL(IN:=ST_RFU_DATA.READ_TAG.DATA_UII[j], N:=4) OR INT_TO_BYTE(Temp);
? ? ? ? ? ? ? ? ? ? ? ? Address ? ?:= Address +?1;
? ? ? ? ? ? ? ? ? ? END_FOR; ?
? ? ? ? ? ? ? ? END_FOR;
? ? ? ??
? ? ? ? ? ? ? ??// 步骤2:解析User数据(64字节,用户自定义数据)
? ? ? ? ? ? ? ? (*Convert ASCII HEX --> HEX , USER Area*)?
? ? ? ? ? ? ? ? FOR j:=1?TO?64?BY?1?DO
? ? ? ? ? ? ? ? ? ? IF j =?1?THEN?
? ? ? ? ? ? ? ? ? ? ? ? Address :=?86; ?// User数据在Buffer中的起始地址(偏移86)
? ? ? ? ? ? ? ? ? ? END_IF;
? ? ? ? ? ? ? ? ? ??// 转换逻辑同UII(ASCII HEX→HEX)
? ? ? ? ? ? ? ? ? ? FOR i:=1?TO?2?BY?1?DO
? ? ? ? ? ? ? ? ? ? ? ? Temp :=BYTE_TO_INT(Buffer[Address])-48;
? ? ? ? ? ? ? ? ? ? ? ? IF Temp >?9?THEN
? ? ? ? ? ? ? ? ? ? ? ? ? ? Temp := Temp-7;
? ? ? ? ? ? ? ? ? ? ? ? END_IF;
? ? ? ? ? ? ? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_USER[j] := SHL(IN:=ST_RFU_DATA.READ_TAG.DATA_USER[j], N:=4) OR INT_TO_BYTE(Temp);
? ? ? ? ? ? ? ? ? ? ? ? Address ? ?:= Address +?1;
? ? ? ? ? ? ? ? ? ? END_FOR; ?
? ? ? ? ? ? ? ? END_FOR;
? ? ? ? ? ? ? ? TMP_BUFFER := BUFFER; ?// 备份接收数据(用于后续调试)
? ? ? ? ? ? ? ? xRead := True; ?// 置位读取成功标志
? ? ? ??
? ? ? ? ? ??// 6. 响应6:No Read(未检测到标签)
? ? ? ? ? ? ELSIF DW_Buffer[1] = DW#16#024E6F52 AND?// '[STX]NoR'(NoRead前缀)
? ? ? ? ? ? ? ? ? DW_Buffer[2] = DW#16#6561640D ? ??// 'ead[CR]'([CR]=回车符,结束标志)
? ? ? ? ? ? ? ? ? THEN
? ? ? ? ? ? ? ? xNoRead := True; ?// 置位无标签标志
? ? ? ? ? ? ? ? iF_NoRead := iF_NoRead +1; ? ??// 测试用:统计无标签次数
? ? ? ??
? ? ? ? ? ??// 7. 响应7:CCError(通信校验错误,标签与RFU通信异常)
? ? ? ? ? ? ELSIF DW_Buffer[1] = DW#16#02434345?AND?// '[STX]CCE'(CCError前缀)
? ? ? ? ? ? ? ? ? DW_Buffer[2] = DW#16#72726F72 ? ??// 'rror'
? ? ? ? ? ? ? ? ? THEN
? ? ? ? ? ? ? ? xCCError := True; ?// 置位校验错误标志
? ? ? ? ? ? ? ? iF_CCError := iF_CCError +1; ?// 测试用:统计校验错误次数
? ? ? ??
? ? ? ? ? ??// 8. 响应8:ReadError(读取数据格式错误,无法解析)
? ? ? ? ? ? ELSIF DW_Buffer[1] = DW#16#02526561?AND?// '[STX]Rea'(ReadError前缀)
? ? ? ? ? ? ? ? ? DW_Buffer[2] = DW#16#64457272? ? ?// 'dErr'
? ? ? ? ? ? ? ? ? THEN
? ? ? ? ? ? ? ? xReadError := True; ?// 置位读取错误标志
? ? ? ? ? ? ? ? iF_ReadError := iF_ReadError +1; ?// 测试用:统计读取错误次数
? ? ? ? ? ??// ELSE: 未定义响应(可扩展F_Error=True,此处预留)
? ? ? ? ? ? END_IF;
? ? ? ??// 测试用:统计接收命令次数(超过4次则计数,用于调试流程异常)
? ? ? ? iZaehler := iZaehler +?1; ? ?
? ? ? ? END_IF;
? ? ? ??// 测试用:接收命令超过4次,置位异常计数(排查是否有重复响应)
? ? ? ? IF iZaehler >=?4?THEN ? ? ? ?
? ? ? ? ? ? iF_3Befehle := iF_3Befehle +?1;
? ? ? ? ? ? iZaehler :=?0;
? ? ? ? END_IF;
? ? ? ??```
? ? ? ? ### 解读(核心响应解析逻辑)
? ? ? ? - **响应前缀识别**:RFU响应以固定前缀区分类型(如`[STX]sAN`=命令确认、`[STX]RSS`=数据响应、`[STX]NoR`=无标签),通过`DW_Buffer`(`Buffer`的DWORD视图)快速匹配前缀(4字节比对,效率高于逐字节判断)。
? ? ? ? - **数据解析(Read响应核心)**:
? ? ? ? ? - UII数据:从`Buffer[20]`开始,30字节(标签唯一标识),ASCII HEX→HEX转换(逻辑同写入,反向操作);
? ? ? ? ? - User数据:从`Buffer[86]`开始,64字节(用户自定义数据),转换逻辑同UII;
? ? ? ? ? - 结果存储:解析后的数据存入`ST_RFU_DATA.READ_TAG`(后续输出到`Data_UII`供外部使用)。
? ? ? ? - **故障响应处理**:
? ? ? ? ? - `xNoRead`:无标签(如物料未到位);
? ? ? ? ? - `xCCError`:标签与RFU通信校验失败(如标签损坏、距离过远);
? ? ? ? ? - `xReadError`:数据格式错误(如RFU返回异常数据);
? ? ? ? ? - 以上响应均会触发后续故障标志(如`F_NoRead=True`),最终汇总到`Stoe`(汇总故障)。
? ? ? ? - **测试用代码**:`iZaehler`/`iF_3Befehle`/`iF_NoRead`等变量用于调试(统计响应次数、故障次数),现场运行时可保留或注释,不影响核心功能。
? ? ? ??
8、故障检测与复位(保障系统稳定性)
涵盖**配置错误检测、故障复位、超时处理、读取重试**四大功能,确保在异常场景下系统可恢复、故障可定位。
代码片段:
### 1. 配置错误检测(F_Konfig)
? ? ? ??#### 代码片段
? ? ? ? ```scl
? ? ? ??// 检测写入起始地址/长度是否合法(3.9.05版本新增,避免越界写入)
? ? ? ??IF?((StartAdd <?0)?OR?(StartAdd >?31)?OR?(Laenge <?1)?OR?(Laenge >?32- StartAdd)) AND ST_BA.En_Stoe THEN
? ? ? ? ? ? F_Konfig :=?TRUE; ?// 配置错误标志置位(如StartAdd=32、Laenge=33均非法)
? ? ? ? ELSE
? ? ? ? ? ? F_Konfig :=?FALSE;
? ? ? ? END_IF;
? ? ? ? ```
? ? ? ??#### 解读
? ? ? ? - **合法范围**:
? ? ? ? ? - `StartAdd`:0-31(标签用户区最大地址31,字单位);
? ? ? ? ? - `Laenge`:1-(32-StartAdd)(避免写入地址超过31,如StartAdd=30时Laenge最大2);
? ? ? ? - **触发条件**:配置非法且设备使能(`ST_BA.En_Stoe=True`),`F_Konfig=True`会汇总到`Stoe`,触发报警。
? ? ? ??
? ? ? ??### 2. 故障复位(清空故障与状态)
? ? ? ??#### 代码片段
? ? ? ? ```scl
? ? ? ??// 故障复位触发条件:报警确认/设备未使能/外部复位
? ? ? ??IF?((ST_BA.Quit OR NOT ST_BA.En_Stoe) AND Stoe AND NOT xoProz AND NOT ST_BA.K92_PoT ) OR xReset THEN
? ? ? ? ? ??// 清空所有故障标志
? ? ? ? ? ? F_TimeOutL ? ? ?:= False;?
? ? ? ? ? ? F_TimeOutS ? ? ?:= False;?
? ? ? ? ? ? F_NoRead ? ? ? ?:= False;
? ? ? ? ? ? F_CCError ? ? ? := False;
? ? ? ? ? ? F_ReadError ? ? := False;
? ? ? ? ? ? F_Error ? ? ? ? := False;
? ? ? ? ? ? F_Konfig ? ? ? ?:= False;
? ? ? ? ? ??// 重置流程状态
? ? ? ? ? ? iCommand ? ? ? ?:=?0;?
? ? ? ? ? ? iSchritt ? ? ? ?:=?0;?
? ? ? ? ? ? BUSY ? ? ? ? ? ?:= False;
? ? ? ? ? ? xLesen ? ? ? ? ?:= False;
? ? ? ? ? ? xSchreiben ? ? ?:= False;
? ? ? ? END_IF;
? ? ? ??// 外部复位(如HMI复位、系统重启):额外清空缓冲区和可视化数据
? ? ? ? IF xReset THEN
? ? ? ? ? ??// 清空TCP接收缓冲区
? ? ? ? ? ? F_TCPConnect ? ?:= False;
? ? ? ? ? ? F_TCPReceive ? ?:= False;
? ? ? ? ? ? F_TCPSend ? ? ? := False;
? ? ? ? ? ? FOR i :=?1?TO?256?BY?1?DO
? ? ? ? ? ? ? ? Buffer[i] := ? ?B#16#0;?
? ? ? ? ? ? END_FOR;
? ? ? ? ? ??// 清空标签数据缓冲区
? ? ? ? ? ? FOR i :=?1?TO?256?BY?1?DO
? ? ? ? ? ? ? ? Buffer[i] := ? ?B#16#00;?
? ? ? ? ? ? END_FOR;
? ? ? ? ? ? FOR i:=1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_UII[i] := B#0;
? ? ? ? ? ? END_FOR; ?
? ? ? ? ? ? FOR i:=1?TO?64?BY?1?DO
? ? ? ? ? ? ? ? ST_RFU_DATA.READ_TAG.DATA_USER[i] := B#0;
? ? ? ? ? ? END_FOR;?
? ? ? ? ? ??// 清空可视化文本(HMI显示)
? ? ? ? ? ? arrVisuText1[1] := ? B#64; ? ? ? ? ? ? ?// UII读取区域
? ? ? ? ? ? arrVisuText1[2] := ? B#0;?
? ? ? ? ? ? arrVisuText3[1] := ? B#64; ? ? ? ? ? ? ?// UII写入区域
? ? ? ? ? ? arrVisuText3[2] := ? B#0;?
? ? ? ? ? ? FOR i :=?1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? arrVisuText1[i+2] := ? ?B#16#0;?
? ? ? ? ? ? ? ? arrVisuText3[i+2] := ? ?B#16#0; ? ? ? ? ?
? ? ? ? ? ? END_FOR;
? ? ? ? ? ? arrVisuText2[1] := ? B#64; ? ? ? ? ? ? ?// User读取区域
? ? ? ? ? ? arrVisuText2[2] := ? B#0;?
? ? ? ? ? ? arrVisuText4[1] := ? B#64; ? ? ? ? ? ? ?// User写入区域
? ? ? ? ? ? arrVisuText4[2] := ? B#0;?
? ? ? ? ? ? FOR i :=?1?TO?64?BY?1?DO
? ? ? ? ? ? ? ? arrVisuText2[i+2] := ? ?B#16#0;
? ? ? ? ? ? ? ? arrVisuText4[i+2] := ? ?B#16#0;
? ? ? ? ? ? END_FOR;
? ? ? ? ? ??// 重置读写完成标志
? ? ? ? ? ? FM_Lesen ? ? ? ?:= False;
? ? ? ? ? ? FM_Schreiben ? ?:= False;
? ? ? ? END_IF;
? ? ? ? ```
? ? ? ??#### 解读
? ? ? ? - **复位分级**:
? ? ? ? ? - 普通复位(`ST_BA.Quit`=报警确认、`NOT ST_BA.En_Stoe`=设备未使能):仅清空故障标志和流程状态,保留缓冲区数据;
? ? ? ? ? - 外部复位(`xReset`=HMI复位/系统重启):彻底清空故障标志、缓冲区、可视化数据,恢复到初始状态,适用于“严重故障后重新启动”。
? ? ? ? - **复位保护**:`NOT xoProz`(非无流程模式)、`NOT ST_BA.K92_PoT`(非工艺暂停状态)确保复位仅在正常运行模式下执行,避免流程混乱。
? ? ? ??
? ? ? ??### 3. 超时处理(TON_Timeout)
? ? ? ??#### 代码片段
? ? ? ? ```scl
? ? ? ??// 读取超时:超时定时器触发且处于读取流程后期(iSchritt>5,避免刚启动就误判)
? ? ? ? IF TON_Timeout.Q AND xLesen AND iSchritt >?5?THEN
? ? ? ? ? ? BUSY ? ? ? ? ? ?:=?FALSE; ?// 重置忙状态
? ? ? ? ? ? F_TimeOutL ? ? ?:= True; ??// 置位读取超时故障
? ? ? ? ? ? iF_TimeOut := iF_TimeOut +1; ?// 测试用:统计超时次数
? ? ? ??// 写入超时:超时定时器触发且处于写入流程
? ? ? ? ELSIF TON_Timeout.Q AND xSchreiben THEN
? ? ? ? ? ? BUSY ? ? ? ? ? ?:=?FALSE;
? ? ? ? ? ? F_TimeOutS ? ? ?:= True; ??// 置位写入超时故障
? ? ? ? END_IF;
? ? ? ??// 超时定时器:仅在忙状态且无超时故障时运行(PT=ZUeGes,输入变量,默认10s)
? ? ? ??TON_Timeout(IN:=BUSY AND NOT F_TimeOutL AND NOT F_TimeOutS,?PT:=ZUeGes);?
? ? ? ? ```
? ? ? ??#### 解读
? ? ? ? - **超时触发条件**:
? ? ? ? ? - 读取超时:`xLesen=True`(正在读取)、`iSchritt>5`(已发送GateOff,等待响应)、`TON_Timeout.Q=True`(超时时间到);
? ? ? ? ? - 写入超时:`xSchreiben=True`(正在写入)、`TON_Timeout.Q=True`;
? ? ? ? - **超时时间**:由输入变量`ZUeGes`配置(默认10s),可根据现场通信速度调整(如网络差时设为20s);
? ? ? ? - **故障处理**:超时后`BUSY=False`(允许重新触发)、`F_TimeOutL/S=True`(汇总到`Stoe`),避免“无限等待”导致流程卡死。
? ? ? ??
? ? ? ??### 4. 读取重试(自动恢复机制)
? ? ? ??#### 代码片段
? ? ? ? ```scl
? ? ? ??// 读取重试定时器:读取故障时启动(500ms延时,避免频繁重试)
? ? ? ??TON_Timer1(IN:=(F_TimeOutL OR F_NoRead OR F_CCError OR F_ReadError) AND NOT xWiederholen AND xLesenExt AND Cfg.X1, PT:= t#500ms);
? ? ? ? IF TON_Timer1.Q THEN
? ? ? ? ? ??// 清空故障标志
? ? ? ? ? ? F_TimeOutL ? ? ?:= False;?
? ? ? ? ? ? F_NoRead ? ? ? ?:= False;
? ? ? ? ? ? F_CCError ? ? ? := False;
? ? ? ? ? ? F_ReadError ? ? := False;
? ? ? ? ? ??// 重置流程,重新触发读取
? ? ? ? ? ? iCommand ? ? ? ?:=?0;?
? ? ? ? ? ? iSchritt ? ? ? ?:=?0;?
? ? ? ? ? ? BUSY ? ? ? ? ? ?:= False;
? ? ? ? ? ? xLesen ? ? ? ? ?:= True; ?// 重新触发读取流程
? ? ? ? ? ? xWiederholen ? ?:= True; ?// 置位重试标志(避免重复触发)
? ? ? ? ? ? iF_Wiederh ? ? ?:= iF_Wiederh +1; ?// 测试用:统计重试次数
? ? ? ? END_IF;
? ? ? ? ```
? ? ? ??#### 解读
? ? ? ? - **重试触发条件**:
? ? ? ? ? - 故障类型:读取超时(`F_TimeOutL`)、无标签(`F_NoRead`)、校验错误(`F_CCError`)、读取错误(`F_ReadError`);
? ? ? ? ? - 使能条件:`xLesenExt=True`(外部读取触发)、`Cfg.X1=True`(配置允许重试,输入变量`_Cfg`的位1);
? ? ? ? ? - 防重复:`NOT xWiederholen`(避免单次故障多次重试)。
? ? ? ? - **重试逻辑**:500ms延时后,清空故障标志、重置流程、`xLesen=True`重新触发读取,提高读取成功率(如临时网络波动、标签位置偏移场景)。
? ? ? ??
? ? ? ??### 5. 汇总故障与连锁故障(Stoe/VkStoe_Out)
? ? ? ??#### 代码片段
? ? ? ? ```scl
? ? ? ??// 汇总故障:所有故障标志逻辑或(任一故障则Stoe=True)
? ? ? ? Stoe := F_TCPConnect
? ? ? ? ? ? ?OR F_TCPReceive?
? ? ? ? ? ? ?OR F_TCPSend
? ? ? ? ? ? ?OR F_TimeOutL?
? ? ? ? ? ? ?OR F_TimeOutS?
? ? ? ? ? ? ?OR F_NoRead
? ? ? ? ? ? ?OR F_CCError
? ? ? ? ? ? ?OR F_ReadError
? ? ? ? ? ? ?OR F_Error
? ? ? ? ? ? ?OR F_Konfig;
? ? ? ??// 连锁故障:本模块故障 + 外部连锁故障(传递给下游设备,如生产线停机)
? ? ? ? VkStoe_Out:= VkStoe_In OR Stoe;
? ? ? ? ```
? ? ? ??#### 解读
? ? ? ? - **汇总故障(Stoe)**:是功能块的“故障总开关”,`Stoe=True`时会:
? ? ? ? ? - 禁止新的读写触发(`xFrgLesen/xFrgSchreiben=False`);
? ? ? ? ? - 触发HMI报警显示;
? ? ? ? ? - 传递给`VkStoe_Out`;
? ? ? ? - **连锁故障(VkStoe_Out)**:将“本模块故障”与“上游设备故障(VkStoe_In)”结合,传递给下游设备(如生产线PLC),实现“一处故障,全线暂停”的安全逻辑,避免物料损坏或数据丢失。
? ? ? ??
9、核心数据输出(Data_UII)
将解析后的标签UII数据输出到功能块外部,供其他模块(如物料追溯系统)使用。
代码片段:
(**************************************************************************)
? ? ? ? (******************** Array Data_UII schreiben ****************************)
? ? ? ? (**************************************************************************)
? ? ? ? FOR i:=1?TO?30?BY?1?DO
? ? ? ? ? ? Data_UII[i] := ST_RFU_DATA.READ_TAG.DATA_UII[i]; ?// 复制UII数据到输出变量
? ? ? ? END_FOR;
? ? ? ??```
? ? ? ? #### 解读
? ? ? ? - **数据来源**:`ST_RFU_DATA.READ_TAG.DATA_UII`(解析RFU响应得到的标签UII,30字节);
? ? ? ? - **输出作用**:`Data_UII`是功能块的核心数据输出,外部系统可通过该变量获取标签唯一标识(如汽车VIN码、物料批次号),实现全流程追溯。
? ? ? ??
? ? ? ? ## 六、可视化数据准备(HMI交互)
? ? ? ? 将设备状态、标签数据、故障信息转换为HMI可显示的格式(DWORD/STRING),实现“人机交互”与状态监控。
? ? ? ? ### 1. 设备信息显示(IP地址、端口)
? ? ? ? #### 代码片段
? ? ? ? ```scl
? ? ? ??// IP地址转换:VerbID.IP[1..4](如[192,168,0,100])→ DWORD(0xC0A80064),HMI可反向解析为IP
? ? ? ? dwVisuWerte1.B0 := INT_TO_BYTE(VerbID.IP[4]); ?// IP第4段(100→0x64)
? ? ? ? dwVisuWerte1.B1 := INT_TO_BYTE(VerbID.IP[3]); ?// IP第3段(0→0x00)
? ? ? ? dwVisuWerte1.B2 := INT_TO_BYTE(VerbID.IP[2]); ?// IP第2段(168→0xA8)
? ? ? ? dwVisuWerte1.B3 := INT_TO_BYTE(VerbID.IP[1]); ?// IP第1段(192→0xC0)
? ? ? ??// 本地端口显示:存入dwVisuWerte2的低字(如2000→0x07D0)
? ? ? ? dwVisuWerte2.W0 := VerbID.lokalPort;
? ? ? ??```
? ? ? ? #### 解读
? ? ? ? - **IP转换逻辑**:IPv4地址的4个段(如192.168.0.100)对应DWORD的4个字节(B3.B2.B1.B0=0xC0.0xA8.0x00.0x64),HMI通过“DWORD转IP”控件即可显示为“192.168.0.100”;
? ? ? ? - **端口显示**:本地端口(如2000)直接存入`dwVisuWerte2.W0`,HMI可直接显示十进制数值。
? ? ? ??
? ? ? ? ### 2. 标签数据与故障文本显示(strVisuText1~5)
? ? ? ? #### 代码片段
? ? ? ? ```scl
? ? ? ??// 读取到数据时,显示数据质量信息(strVisuText5)
? ? ? ? IF (DW_Buffer[1] = DW#16#02525353) THEN?// [STX]RSS(读取到数据)
? ? ? ? ? ? arrVisuText5[1] := B#64; ?// 字符串长度(64字节)
? ? ? ? ? ? arrVisuText5[2] := B#4; ??// 实际数据长度(4字节质量信息)
? ? ? ? ? ? arrVisuText5[3] := Buffer[7]; ?// 质量信息1(如信号强度)
? ? ? ? ? ? arrVisuText5[4] := Buffer[8]; ?// 质量信息2(如校验结果)
? ? ? ? ? ? arrVisuText5[5] := Buffer[9]; ?// 质量信息3(如读取次数)
? ? ? ? ? ? arrVisuText5[6] := Buffer[10];?// 质量信息4(如标签状态)
? ? ? ? END_IF;
? ? ? ??// 无标签故障时,HMI显示“No Read”
? ? ? ? IF F_NoRead THEN ?// [STX]NoRead[CR]
? ? ? ? ? ? strVisuText1 :=?'No Read'; ?// UII读取区域显示
? ? ? ? ? ? strVisuText2 :=?'No Read'; ?// User读取区域显示
? ? ? ? ? ? strVisuText3 :=?' '; ? ? ? ?// UII写入区域清空
? ? ? ? ? ? strVisuText4 :=?' '; ? ? ? ?// User写入区域清空
? ? ? ? END_IF;
? ? ? ??// CCError故障时,HMI显示“CCError >=2”
? ? ? ? IF F_CCError THEN ?// [STX]CCError
? ? ? ? ? ? strVisuText1 :=?'CCError >=2';
? ? ? ? ? ? strVisuText2 :=?'CCError >=2';
? ? ? ? ? ? strVisuText3 :=?' ';
? ? ? ? ? ? strVisuText4 :=?' ';
? ? ? ? END_IF;
? ? ? ??// ReadError故障时,HMI显示“Read Error”
? ? ? ? IF F_ReadError THEN ?// [STX]ReaError
? ? ? ? ? ? strVisuText1 :=?'Read Error';
? ? ? ? ? ? strVisuText2 :=?'Read Error';
? ? ? ? ? ? strVisuText3 :=?' ';
? ? ? ? ? ? strVisuText4 :=?' ';
? ? ? ? END_IF;
? ? ? ??// 读取完成后,更新HMI显示的UII/User数据(处理NULL字符)
? ? ? ? IF R_TRIG_FM_Lesen.Q THEN
? ? ? ? ? ??// UII读取区域显示(30字节)
? ? ? ? ? ? arrVisuText1[1] := ? B#64; ? ? ? ? ? ? ?
? ? ? ? ? ? arrVisuText1[2] := ? B#64; ?// 数据长度64字节
? ? ? ? ? ? FOR i :=?1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? arrVisuText1[i+2] := ? ?ST_RFU_DATA.READ_TAG.DATA_UII[i];
? ? ? ? ? ? END_FOR;
? ? ? ? ? ??// User读取区域显示(64字节,NULL字符替换为空格)
? ? ? ? ? ? arrVisuText2[1] := ? B#64; ? ? ? ? ? ? ?
? ? ? ? ? ? arrVisuText2[2] := ? B#64; ?
? ? ? ? ? ? FOR i :=?1?TO?64?BY?1?DO
? ? ? ? ? ? ? ??// 避免HMI显示乱码:将NULL(0x00)替换为空格(0x20)
? ? ? ? ? ? ? ? IF ST_RFU_DATA.READ_TAG.DATA_USER[i] = Byte#16#00?THEN
? ? ? ? ? ? ? ? ? ? arrVisuText2[i+2] := ? ?Byte#16#20;
? ? ? ? ? ? ? ? ELSE
? ? ? ? ? ? ? ? ? ? arrVisuText2[i+2] := ? ?ST_RFU_DATA.READ_TAG.DATA_USER[i];?
? ? ? ? ? ? ? ? END_IF; ? ? ? ?
? ? ? ? ? ? ?END_FOR;
? ? ? ? ? ??// UII/User写入区域显示(同读取区域,方便用户确认)
? ? ? ? ? ? IF xFM_Lesen THEN
? ? ? ? ? ? ? ? arrVisuText3[1] := ? B#64; ? ? ? ? ?
? ? ? ? ? ? ? ? arrVisuText3[2] := ? B#64; ?
? ? ? ? ? ? ? ? FOR i :=?1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? ? ? arrVisuText3[i+2] := ? ?ST_RFU_DATA.READ_TAG.DATA_UII[i];?
? ? ? ? ? ? ? ? END_FOR;
? ? ? ? ? ? ? ? arrVisuText4[1] := ? B#64; ? ? ? ? ?
? ? ? ? ? ? ? ? arrVisuText4[2] := ? B#64; ?
? ? ? ? ? ? ? ? FOR i :=?1?TO?64?BY?1?DO
? ? ? ? ? ? ? ? ? ? IF ST_RFU_DATA.READ_TAG.DATA_USER[i] = Byte#16#00?THEN
? ? ? ? ? ? ? ? ? ? ? ? arrVisuText4[i+2] := ? ?Byte#16#20;
? ? ? ? ? ? ? ? ? ? ELSE
? ? ? ? ? ? ? ? ? ? ? ? arrVisuText4[i+2] := ? ?ST_RFU_DATA.READ_TAG.DATA_USER[i];?
? ? ? ? ? ? ? ? ? ? END_IF;
? ? ? ? ? ? ? ? END_FOR;
? ? ? ? ? ? xFM_Lesen := False;
? ? ? ? ? ? END_IF;
? ? ? ? END_IF;
? ? ? ??// 无读写操作时,清空HMI显示
? ? ? ? IF NOT FM_Lesen AND NOT xLesen AND NOT xSchreiben THEN
? ? ? ? ? ? arrVisuText1[1] := ? B#64; ? ? ? ? ? ? ?
? ? ? ? ? ? arrVisuText1[2] := ? B#0; ?// 数据长度0(空)
? ? ? ? ? ? FOR i :=?1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? arrVisuText1[i+2] := ? ?B#16#0;?
? ? ? ? ? ? END_FOR;
? ? ? ? ? ??
? ? ? ? ? ? arrVisuText2[1] := ? B#64; ? ? ? ? ? ? ?
? ? ? ? ? ? arrVisuText2[2] := ? B#0;?
? ? ? ? ? ? FOR i :=?1?TO?64?BY?1?DO
? ? ? ? ? ? ? ? arrVisuText2[i+2] := ? ?B#16#0;
? ? ? ? ? ? END_FOR;
? ? ? ? ? ? arrVisuText3[1] := ? B#64; ? ? ? ? ? ? ?
? ? ? ? ? ? arrVisuText3[2] := ? B#0;?
? ? ? ? ? ? FOR i :=?1?TO?30?BY?1?DO
? ? ? ? ? ? ? ? arrVisuText3[i+2] := ? ?B#16#0;?
? ? ? ? ? ? END_FOR;
? ? ? ? ? ? arrVisuText4[1] := ? B#64; ? ? ? ? ? ? ?
? ? ? ? ? ? arrVisuText4[2] := ? B#0;?
? ? ? ? ? ? FOR i :=?1?TO?64?BY?1?DO
? ? ? ? ? ? ? ? arrVisuText4[i+2] := ? ?B#16#0;
? ? ? ? ? ? END_FOR;
? ? ? ? END_IF;
? ? ? ??```
? ? ? ? #### 解读
? ? ? ? - **文本格式**:HMI显示的字符串遵循“前2字节为长度”的PLC字符串格式(如`arrVisuText1[1]=64`表示字符串最大长度64字节,`arrVisuText1[2]=64`表示实际数据长度64字节);
? ? ? ? - **NULL字符处理**:标签数据中的`0x00`(NULL)会导致HMI显示乱码,因此替换为`0x20`(空格),确保显示正常;
? ? ? ? - **故障文本**:故障时`strVisuText1~2`显示故障原因(如“No Read”“CCError >=2”),方便操作人员快速定位问题;
? ? ? ? - **读写同步显示**:读取完成后,`arrVisuText3~4`(写入区域)同步显示读取数据,方便用户确认“写入数据是否与读取一致”。
? ? ? ??
? ? ? ? ### 3. 状态指示灯控制(dwVisuWerte4/5)
? ? ? ? #### 代码片段
? ? ? ? ```scl
? ? ? ??// dwVisuWerte4:HMI指示灯状态(位映射,1位对应1个指示灯)
? ? ? ? dwVisuWerte4.X0 := xLesen AND NOT FM_Lesen AND NOT Stoe; ? ?// 读取中(绿灯闪烁)
? ? ? ? dwVisuWerte4.X1 := FM_Lesen; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 读取完成(绿灯常亮)
? ? ? ? dwVisuWerte4.X2 := xFrgLesen; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 读取使能(绿灯常亮)
? ? ? ? dwVisuWerte4.X3 := xSchreiben AND NOT FM_Schreiben AND NOT Stoe;?// 写入中(绿灯闪烁)
? ? ? ? dwVisuWerte4.X4 := FM_Schreiben; ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 写入完成(绿灯常亮)
? ? ? ? dwVisuWerte4.X5 := xFrgSchreiben AND FM_Lesen; ? ? ? ? ? ? ?// 写入使能(绿灯常亮)
? ? ? ? dwVisuWerte4.X6 := xoProz; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 无流程模式(黄灯常亮)
? ? ? ? dwVisuWerte4.X7 := Frg_oProz; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 无流程允许(绿灯常亮)
? ? ? ? dwVisuWerte4.X8 := False; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// K100功能(未使用,灯灭)
? ? ? ? dwVisuWerte4.X9 := ST_BA.K100K_LSP; ? ? ? ? ? ? ? ? ? ? ? ??// K100使能(绿灯常亮)
? ? ? ? dwVisuWerte4.X10 := F_TimeOutL ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 读取故障(红灯常亮)
? ? ? ? ? ? ? ? ? ? ? ? ?OR ((F_NoRead OR F_CCError OR F_ReadError OR F_Error)AND xLesen);
? ? ? ? dwVisuWerte4.X11 := F_TimeOutS ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 写入故障(红灯常亮)
? ? ? ? ? ? ? ? ? ? ? ? ?OR ((F_NoRead OR F_CCError OR F_ReadError OR F_Error)AND xSchreiben);
? ? ? ? dwVisuWerte4.X12 := xReset; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 复位中(绿灯常亮)
? ? ? ? dwVisuWerte4.X13 := ST_BA.SWE7; ? ? ? ? ? ? ? ? ? ? ? ? ? ??// 复位允许(绿灯常亮)
? ? ? ??// dwVisuWerte4的字节视图:步骤状态显示(dwVisuWerteSB4是dwVisuWerte4的BYTE视图)
? ? ? ? dwVisuWerteSB4.B2 := INT_TO_BYTE(iSchritt); ? ? ? ? ? ? ? ??// 显示当前流程步骤(如1=GateOn,5=GateOff)
? ? ? ??// dwVisuWerte5:写入地址/长度信息(HMI显示)
? ? ? ? dwVisuWerte5.B0 ? := INT_TO_BYTE(StartAdd *2); ? ? ? ? ? ? ?// 起始地址(字节单位,方便HMI显示)
? ? ? ? dwVisuWerte5.B1 ? := INT_TO_BYTE(Laenge *2); ? ? ? ? ? ? ? ?// 长度(字节单位)
? ? ? ? dwVisuWerte5.B2 ? := INT_TO_BYTE(iEndAdd *2); ? ? ? ? ? ? ??// 结束地址(字节单位)
? ? ? ??// HMI报警颜色控制(故障=红色,警告=黄色,维护=蓝色,正常=无色)
? ? ? ? IF xStoe THEN ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? dwVisuWerteSB4.B3 := Byte#3; ? ? ? ? ? ? ? ? ? (* Rot → 故障(红色)*)
? ? ? ? ELSIF xWARN THEN
? ? ? ? ? ? dwVisuWerteSB4.B3 := Byte#6; ? ? ? ? ? ? ? ? ? (* Gelb → 警告(黄色)*)
? ? ? ? ELSIF xWart THEN
? ? ? ? ? ? dwVisuWerteSB4.B3 := Byte#2; ? ? ? ? ? ? ? ? ? (* Blau → 维护(蓝色)*)
? ? ? ? ELSE
? ? ? ? ? ? dwVisuWerteSB4.B3 := Byte#0; ? ? ? ? ? ? ? ? ? (* 无色 → 正常 *)
? ? ? ? END_IF; ? ?
? ? ? ??```
? ? ? ? #### 解读
? ? ? ? - **指示灯位映射**:`dwVisuWerte4`的每一位对应HMI的一个指示灯,通过“逻辑与/或”判断状态(如`X0=读取中`、`X1=读取完成`),HMI可根据位状态控制灯的“亮/灭/闪烁”;
? ? ? ? - **颜色控制**:`dwVisuWerteSB4.B3`定义报警颜色(3=红色、6=黄色、2=蓝色),HMI根据该字节值显示对应颜色,实现“视觉化故障分级”;
? ? ? ? - **地址信息**:`dwVisuWerte5`将`StartAdd`/`Laenge`/`iEndAdd`转换为字节单位(×2),方便HMI以“字节”为单位显示(符合用户习惯)。
? ? ? ??
10、系统报警集成(MeldSend)
将功能块内部故障转换为系统级报警,通过`MeldSend`函数块上报到工厂报警系统,实现“故障集中监控”。
代码片段:
(****************************************************************************************)
? ? ? ? (*******************Anfang Signalaufbereitung Meldesystem *******************************)
? ? ? ? (***********************AUFRUF am Ende im SCL-BAUSTEINS**********************************)
? ? ? ? // 配置报警指针(指向MSYS报警数据、MSYS.Flanken边沿数据、ST_Meld报警缓冲区)
? ? ? ? TempPointerMsys := Msys;
? ? ? ? TempPointerFlanken := Msys.Flanken;
? ? ? ? TempPointerMeldpuffer := ST_Meld;
? ? ? ? // 调整地址指针:将数据块(DB)地址转换为背景数据块(DI)地址(避免地址冲突)
? ? ? ? pMeldFeld.BZ := pMeldFeld.BZ OR 16#1000000; // 0x1000000是DB→DI的地址偏移
? ? ? ? pMeldAbb.BZ ?:= pMeldAbb.BZ ?OR 16#1000000;
? ? ? ? // 配置报警参数(MeldSend函数块要求)
? ? ? ? pMeldFeld.TYP := 1;// Typ:=1 → 报警数据类型为BOOL(1位对应1个报警)
? ? ? ? pMeldFeld.ANZ := 16;// 报警数量:16个BOOL(需是8的倍数)
? ? ? ? pMeldAbb.TYP := 2;// Typ:=2 → 边沿数据类型为BYTE(1字节对应8个边沿)
? ? ? ? pMeldAbb.ANZ := 2;// 边沿数据数量:2个BYTE(对应16个报警的边沿)
? ? ? ? // 映射故障到MSYS.xF_1~xF_16(16个报警位,1位对应1个故障/警告)
? ? ? ? MSYS.xF_1 ? := F_TCPConnect; ? ? ? ? ? ? ? ?// STE/TCP连接故障
? ? ? ? MSYS.xF_2 ? := F_TCPReceive; ? ? ? ? ? ? ? ?// STE/TCP接收故障
? ? ? ? MSYS.xF_3 ? := F_TCPSend; ? ? ? ? ? ? ? ? ? // STE/TCP发送故障
? ? ? ? MSYS.xF_4 ? := F_TimeOutL; ? ? ? ? ? ? ? ? ?// STE/读取超时故障
? ? ? ? MSYS.xF_5 ? := F_TimeOutS; ? ? ? ? ? ? ? ? ?// STE/写入超时故障
? ? ? ? MSYS.xF_6 ? := F_NoRead; ? ? ? ? ? ? ? ? ? ?// STE/无标签故障
? ? ? ? MSYS.xF_7 ? := F_CCError; ? ? ? ? ? ? ? ? ? // STE/通信校验错误
? ? ? ? MSYS.xF_8 ? := F_Error; ? ? ? ? ? ? ? ? ? ? // STE/数据接收错误
? ? ? ? MSYS.xF_9 ? := F_ReadError; ? ? ? ? ? ? ? ? // STE/读取数据错误
? ? ? ? MSYS.xF_10 ?:= xWiederholen; ? ? ? ? ? ? ? ?// MT0/读取重试(警告)
? ? ? ? MSYS.xF_11 ?:= xoProz; ? ? ? ? ? ? ? ? ? ? ?// MT0/无流程模式(警告)
? ? ? ? MSYS.xF_12 ?:= F_Konfig; ? ? ? ? ? ? ? ? ? ?// STE/配置错误(起始地址/长度非法)
? ? ? ? MSYS.xF_13 ?:= False; ? ? ? ? ? ? ? ? ? ? ? // 未使用(预留)
? ? ? ? MSYS.xF_14 ?:= False; ? ? ? ? ? ? ? ? ? ? ? // 未使用(预留)
? ? ? ? MSYS.xF_15 ?:= False; ? ? ? ? ? ? ? ? ? ? ? // 未使用(预留)
? ? ? ? MSYS.xF_16 ?:= False; ? ? ? ? ? ? ? ? ? ? ? // 未使用(预留)
? ? ? ? // 调用MeldSend函数块,上报报警到系统
? ? ? ? MeldSend(AufrufNr := ?1 ? ? ? ? ? ? ? ? ? ? // 调用编号(唯一标识本功能块的报警)
? ? ? ? ? ? ? ? ?,Anz_Meld := ?16 ? ? ? ? ? ? ? ? ? ?// 报警数量(16个)
? ? ? ? ? ? ? ? ?,pMeldFeld := ?pMeldFeld.BZ ? ? ? ? // 报警数据指针(指向MSYS.xF_1~xF_16)
? ? ? ? ? ? ? ? ?,pMeldAbb := ?pMeldAbb.BZ ? ? ? ? ?// 边沿数据指针(指向MSYS.Flanken)
? ? ? ? ? ? ? ? ?,MeldDB := ?WORD_TO_INT(pMeldDBNR.DBNR) // 报警数据块编号(存储报警信息的DB)
? ? ? ? ? ? ? ? ?,AenderungsID := ?AenderungsID ? ? ?// 版本标识(用于追溯报警数据版本)
? ? ? ? ? ? ? ? ?,Integritaet := ?Integritaet ? ? ? ?// 数据完整性标志(确保报警数据未篡改)
? ? ? ? ? ? ? ? ?,Neustart := ?Neustart ? ? ? ? ? ? ?// 系统重启标志(重启后清空报警)
? ? ? ? ? ? ? ? ?); // VOID(无返回值)
? ? ? ? // 报警分类:故障(Stoe)、警告(Warn)、维护(Wart),更新到全局可视化状态
? ? ? ? // 1. 故障(Stoe):触发红色报警
? ? ? ? xStoe := Msys.xF_1 ? ?
? ? ? ? ? ? ? OR Msys.xF_2 ? ?
? ? ? ? ? ? ? OR Msys.xF_3 ? ?
? ? ? ? ? ? ? OR Msys.xF_4 ? ?
? ? ? ? ? ? ? OR Msys.xF_5 ? ?
? ? ? ? ? ? ? OR Msys.xF_6 ? ?
? ? ? ? ? ? ? OR Msys.xF_7 ? ?
? ? ? ? ? ? ? OR Msys.xF_8 ? ?
? ? ? ? ? ? ? OR Msys.xF_9 ? ??
? ? ? ? ? ? ? OR Msys.xF_12 ??
? ? ? ? ? ? ? OR Msys.xF_13 ??
? ? ? ? ? ? ? OR Msys.xF_14 ??
? ? ? ? ? ? ? OR Msys.xF_15 ??
? ? ? ? ? ? ? OR Msys.xF_16;
? ? ? ? DB_ARG.VisuSS.Station_Stoer:= DB_ARG.VisuSS.Station_Stoer OR xStoe; ?// 全局故障状态
? ? ? ? // 2. 警告(Warn):触发黄色报警(非故障,仅提示)
? ? ? ? xWARN := Msys.xF_10
? ? ? ? ? ? ? OR Msys.xF_11;
? ? ? ? DB_ARG.VisuSS.Station_Warn := DB_ARG.VisuSS.Station_Warn OR xWARN; ?// 全局警告状态
? ? ? ? // 3. 维护(Wart):触发蓝色报警(预留,如定期维护提醒)
? ? ? ? xWART := FALSE;
? ? ? ? DB_ARG.VisuSS.Station_Wart := DB_ARG.VisuSS.Station_Wart OR xWART; ?// 全局维护状态
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? (****************************************************************************************)
? ? ? ? (*************Ende Signalaufbereitung fuer das Meldesystem?*******************************)
? ? ? ? (****************************************************************************************)
? ? ? ? ```
? ? ? ? ### 解读(核心报警逻辑)
? ? ? ? -?**报警数据映射**:`MSYS.xF_1~xF_16`是“报警信号枢纽”,将功能块内部的12个故障/警告(如`F_TCPConnect`→`xF_1`、`xoProz`→`xF_11`)映射到16个BOOL位,预留4个位用于后续扩展;
? ? ? ? -?**指针地址调整**:`pMeldFeld.BZ OR 0x1000000`将数据块(DB)地址转换为背景数据块(DI)地址,避免与其他模块的DB地址冲突(西门子PLC中DI是独立的背景数据区);
? ? ? ? -?**MeldSend函数块作用**:
? ? ? ? ? - 是项目自定义的系统级报警FB,负责将`MSYS`中的报警数据写入`ST_Meld`(报警缓冲区);
? ? ? ? ? - 支持报警编号(`AufrufNr=1`)、数据完整性校验(`Integritaet`)、系统重启清空(`Neustart`),确保报警数据可靠;
? ? ? ? -?**报警分类与全局同步**:
? ? ? ? ? - 故障(`xStoe`):影响功能执行的严重异常(如TCP连接故障、读取错误),触发红色报警,更新`DB_ARG.VisuSS.Station_Stoer`(全局故障状态);
? ? ? ? ? - 警告(`xWARN`):不影响功能但需关注的提示(如读取重试、无流程模式),触发黄色报警,更新`DB_ARG.VisuSS.Station_Warn`(全局警告状态);
? ? ? ? ? - 维护(`xWART`):预留用于定期维护提醒(如RFU设备清洁、天线校准),触发蓝色报警;
? ? ? ? -?**系统集成价值**:通过`MeldSend`将分散的故障信号集中上报,工厂操作员可在中央监控系统查看所有设备的报警状态,实现“远程监控、快速排查”。
? ? ? ??
八、整体逻辑闭环总结
本次解读的代码是FB_Sick_RFU的**执行核心**,与前期的“变量声明、TCP连接”共同构成完整的RFID通信控制逻辑,其闭环流程如下:
1.**配置触发**:外部输入`Lesen`/`Schreiben`触发读写,`VerbID`配置TCP参数;
2.**命令生成**:`iCommand`选择命令类型(GateOn/GateOff/Write/Timer),生成符合SICK RFU协议的TCP命令;
3.**TCP发送**:`TCP_Send`发送命令,处理发送结果(`F_TCPSend`);
4.**响应解析**:`TCP_Receive.NDR`触发响应解析,更新`xGateOn`/`xRead`/`xNoRead`等状态;
5.**故障处理**:检测配置错误、超时、通信故障,触发重试或复位,汇总到`Stoe`;
6.**数据输出**:解析的UII数据通过`Data_UII`输出,供外部系统使用;
7.**可视化与报警**:HMI显示状态/数据/故障,`MeldSend`上报系统报警,实现人机交互与集中监控。
该逻辑覆盖了“从触发到反馈”的全流程,兼顾了**功能性(读写标签)、可靠性(重试/复位)、可维护性(报警/可视化)**?,是工业RFID应用的典型实现方案。