liusuyi
2026-05-30 f4f4fc53260eb67483dce406a963628273786a61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.ard.work.sdk.fjr.netty;
 
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.net.InetSocketAddress;
import java.util.List;
 
/**
 * FJR 协议编码器
 * 负责将 ThermalCommand 对象转换为符合 FJR 协议的 UDP 数据包
 */
public class FjrProtocolEncoder extends MessageToMessageEncoder<ThermalCommand> {
 
    @Override
    protected void encode(ChannelHandlerContext ctx, ThermalCommand msg, List<Object> out) throws Exception {
        // 1. 构建原始数据体:地址 + 指令ID + 数据
        // 注意:cameraId 是业务字段,不包含在协议包中
        byte[] rawData = new byte[1 + 1 + (msg.getData() != null ? msg.getData().length : 0)];
        rawData[0] = ThermalCommand.DEVICE_ADDR;  // 0x26
        rawData[1] = msg.getCmdId();
        if (msg.getData() != null) {
            System.arraycopy(msg.getData(), 0, rawData, 2, msg.getData().length);
        }
 
        // 2. 计算校验和 (基于原始数据)
        // 规则:所有数据字节相加,取低8位
        int sum = 0;
        for (byte b : rawData) {
            sum += (b & 0xFF);
        }
        byte checkSum = (byte) (sum & 0xFF);
 
        // 3. 数据区转义处理
        // 规则:
        // F0 -> F5 00
        // FF -> F5 0F
        // F5 -> F5 05
        ByteBuf escapedBuf = ctx.alloc().buffer();
        for (byte b : rawData) {
            if (b == (byte) 0xF0) {
                escapedBuf.writeByte(0xF5);
                escapedBuf.writeByte(0x00);
            } else if (b == (byte) 0xFF) {
                escapedBuf.writeByte(0xF5);
                escapedBuf.writeByte(0x0F);
            } else if (b == (byte) 0xF5) {
                escapedBuf.writeByte(0xF5);
                escapedBuf.writeByte(0x05);
            } else {
                escapedBuf.writeByte(b);
            }
        }
 
        // 4. 组装最终数据包
        // 结构:F0(1) + Length(1) + Data(Escaped) + CheckSum(1) + FF(1)
        ByteBuf packetBuf = ctx.alloc().buffer();
        packetBuf.writeByte(0xF0);          // 起始标志
        packetBuf.writeByte(rawData.length); // 数据长度 (注意:这里是原始数据的长度,不是转义后的长度)
        packetBuf.writeBytes(escapedBuf);   // 转义后的数据
        packetBuf.writeByte(checkSum);      // 校验和
        packetBuf.writeByte(0xFF);          // 结束标志
 
        // 释放中间 buffer
        escapedBuf.release();
 
        // 5. 包装成 UDP DatagramPacket
        // 获取当前 Channel 绑定的远程地址
        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        DatagramPacket packet = new DatagramPacket(packetBuf, remoteAddress);
 
        out.add(packet);
 
        // 可选:打印调试日志
        // System.out.println(">>> 发送指令: " + msg.getCmdId());
    }
}