您遇到的数据顺序错乱问题是由于**TCP协议本身的无边界性和PLC缓冲区处理机制**共同导致的。以下是具体原因和解决方案:
---
### **问题根源分析**
1. **TCP流式协议特性**
TCP是字节流协议,不保证应用层消息的边界。若客户端快速连续发送多组数据,PLC可能将多次发送的数据合并到同一接收缓冲区(如网络延迟或PLC处理速度慢时)。
2. **PLC缓冲区覆盖机制**
S7-1200的`TRCV`指令默认采用**循环缓冲区**(FIFO机制)。当新数据到达时,会从缓冲区的当前指针位置开始覆盖旧数据,导致看似"乱序"。
3. **字节对齐差异**
如果客户端发送时未显式指定字节序(如大端/小端),而PLC默认按大端序解析,可能引发分段错误(但您的案例中实际是整体偏移,非字节反转)。
---
### **解决方案**
#### **(1) 显式定义应用层协议**
在数据包中加入**帧头**和**帧尾**标识,例如:
```plaintext
[头标识:0xAA][数据长度:1字节][数据:N字节][校验和:1字节][尾标识:0x55]
```
PLC侧通过循环检查缓冲区匹配完整帧后再处理。
#### **(2) 修改TRCV参数配置**
```pascal
TRCV(
REQ := TRUE, // 持续使能
CONT := TRUE, // 保持连接
DATA := #ReceiveBuf, // 接收缓冲区
LEN := 20, // 预期接收长度
RCVD_LEN=> #ActualLen, // 实际接收长度输出
BUSY => #BusyFlag);
```
关键点:
- 设置`LEN`与发送方严格一致(您已设为20字节)。
- 监控`RCVD_LEN`确认每次接收的实际字节数。
#### **(3) 强制单次接收同步**
在客户端每次发送后增加延迟(如100ms),或通过PLC程序控制:
```pascal
IF #FirstReceive THEN
TRCV(REQ := TRUE); // 首次触发
#FirstReceive := FALSE;
ELSE
TRCV(REQ := NOT #BusyFlag); // 上次接收完成后再请求
END_IF;
```
#### **(4) 使用`TRCV_C`替代`TRCV`**
`TRCV_C`集成连接管理,可减少缓冲区冲突:
```pascal
TRCV_C(
CONT := TRUE,
LEN := 20,
DATA := #ReceiveBuf,
RCVD_LEN=> #ActualLen);
```
---
### **您的数据案例分析**
- **原始发送数据**(16进制):
```plaintext
15 3E 14 24 54 26 4E 28 51 29 53 AA 56 B0 2D 2D 5C 75 2F C8
```
- **PLC接收数据**:
```plaintext
2D 2D 5C 75 2F C8 15 3E 14 24 54 26 4E 28 51 29 53 AA 56 B0
```
**现象解释**:
数据被**环形偏移6字节**,说明PLC缓冲区中残留了之前未及时读取的6字节数据,新数据从缓冲区第7字节开始写入,形成"拼接"效果。
---
### **终极调试建议**
1. **清空缓冲区**
在建立连接后、首次接收前,调用`TRCV`连续读取直到`BUSY`=FALSE,丢弃旧数据。
2. **Wireshark抓包验证**
过滤PLC的IP和端口,确认客户端是否真正按预期顺序发送(排除调试助手本身问题)。
3. **PLC侧诊断**
在线监视`TRCV`的`RCVD_LEN`和`STATUS`,正常时应返回`16#7000`(无错误)。
4. **代码示例(清空缓冲区)**
```pascal
// 初始化阶段清空缓冲区
#TempBuffer := ARRAY[1..100] OF BYTE; // 临时缓冲区
WHILE TRUE DO
TRCV(
REQ := TRUE,
DATA := #TempBuffer,
LEN := 100,
BUSY => #IsBusy);
IF NOT #IsBusy THEN EXIT; END_IF;
END_WHILE;
```
---
通过以上方法,您应该能解决数据错序问题。如果仍有异常,请检查:
- 客户端是否启用了Nagle算法(建议禁用)
- PLC的OB1循环时间是否过短(建议≥50ms)
- 是否有多余的`TRCV`调用覆盖了缓冲区