‘liusuyi’
2023-06-27 3be4440f6800e10efd8db51b957d17a6cc3b39df
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
package com.ard.utils.tcp;
 
import com.alibaba.fastjson2.JSON;
import com.ard.alarm.radar.domain.ArdEquipRadar;
import com.ard.alarm.radar.domain.RadarAlarmInfo;
import com.ard.hiksdk.domain.alarmEventInfo;
import com.ard.utils.LonlatConver;
import com.ard.utils.SpringTool;
import com.ard.utils.mqtt.MqttConsumer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GlobalCoordinates;
import sun.nio.cs.ext.GBK;
 
import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
 
/**
 * @Description: tcp客户端处理
 * @ClassName: NettyTcpClientHandler
 * @Author: 刘苏义
 * @Date: 2023年06月25日17:02
 * @Version: 1.0
 **/
@Slf4j(topic = "radar")
public class NettyTcpClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
 
    private String host;
    private Integer port;
    private Double longitude;
    private Double lagitude;
    private String name;
 
    public NettyTcpClientHandler(ArdEquipRadar ardEquipRadar) {
        this.host = ardEquipRadar.getIp();
        this.port = ardEquipRadar.getPort();
        this.longitude = ardEquipRadar.getLongitude();
        this.lagitude = ardEquipRadar.getLatitude();
        this.name=ardEquipRadar.getName();
    }
 
    private ChannelHandlerContext context;
    private ScheduledFuture<?> heartbeatTask;
 
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
        // 处理接收到的消息
        byte[] byteArray = new byte[msg.readableBytes()];
        msg.getBytes(msg.readerIndex(), byteArray);
        byte[] bytes = receiveCompletePacket(byteArray);
        if (bytes != null) {
          //  String hexString = DatatypeConverter.printHexBinary(bytes);
          //  log.info(hexString);
            processData(bytes);
        }
    }
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        context = ctx;
        startHeartbeatTask();//开始发送心跳
    }
 
    /**
     * 开始心跳任务
     */
    private void startHeartbeatTask() {
        heartbeatTask = context.executor().scheduleAtFixedRate(() -> {
            // 发送心跳消息
            ByteBuf message = context.alloc().buffer();
            byte[] heart = {0x01, 0x02, 0x01, 0x10, 0x00, 0x00, 0x00, (byte) 0x83, (byte) 0x88, 0x5d, 0x71, 0x01, 0x02, 0x00};
            String hexString = DatatypeConverter.printHexBinary(heart);
            log.info("发送心跳:" + hexString);
            message.writeBytes(heart);
            context.writeAndFlush(message);
 
        }, 0, 5, TimeUnit.SECONDS);
    }
 
    /**
     * 停止心跳任务
     */
    private void stopHeartbeatTask() {
        if (heartbeatTask != null) {
            heartbeatTask.cancel(false);
            heartbeatTask = null;
        }
    }
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.info("tcp客户端连接异常");
        // 发生异常时的处理
        cause.printStackTrace();
        ctx.close();
        stopHeartbeatTask();//停止心跳发送
    }
 
    /**
     * 解析报警数据
     */
    public void processData(byte[] data) {
        try {
            data = transferData(data);//去掉包头和包尾、校验及转义
 
            byte[] type = Arrays.copyOfRange(data, 0, 1);//命令类型
            log.info("命令类型:" + DatatypeConverter.printHexBinary(type));
 
            byte[] cmdId = Arrays.copyOfRange(data, 1, 2);//命令ID
            log.info("命令ID:" + DatatypeConverter.printHexBinary(cmdId));
 
            byte[] payloadSize = Arrays.copyOfRange(data, 2, 4);//有效负载大小
            payloadSize = toLittleEndian(payloadSize);
            int payloadSizeToDecimal = byteArrayToDecimal(payloadSize);
            log.info("有效负载大小(转整型):" + payloadSizeToDecimal);
 
            if (Arrays.equals(cmdId, new byte[]{0x01})) {
                byte[] dwTim = Arrays.copyOfRange(data, 4, 8);
                dwTim = toLittleEndian(dwTim);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                long l = byteArrayToDecimal(dwTim);
                String alarmTime = sdf.format(l * 1000);
                log.info("周视图像的出现时间(转date):" + alarmTime);
 
                byte[] wTargetNum = Arrays.copyOfRange(data, 8, 10);
                wTargetNum = toLittleEndian(wTargetNum);
                int targetNum = byteArrayToDecimal(wTargetNum);
                log.info("目标总点数(转整型):" + targetNum);
 
                //解析NET_TARGET_UNIT(64是NET_TARGET_HEAD的字节数)
                int uintSize = (payloadSizeToDecimal - 64) / targetNum;
                log.info("单条报警大小:" + uintSize);
                for (int i = 0; i < targetNum; i++) {
 
                    Integer index = 68 + uintSize * i;
                    byte[] dwID = Arrays.copyOfRange(data, index, index + 4);
                    dwID = toLittleEndian(dwID);
                    int id = byteArrayToDecimal(dwID);
                    // log.info("目标ID号:" + id);
 
                    byte[] iDistance = Arrays.copyOfRange(data, index + 8, index + 12);
                    iDistance = toLittleEndian(iDistance);
                    int Distance = byteArrayToDecimal(iDistance);
                    // log.info("目标当前距离(m):" + Distance);
                    //region 不需要的字段
//                    byte[] dwGSum = Arrays.copyOfRange(data, index + 4, index + 8);
//                    dwGSum = toLittleEndian(dwGSum);
//                    int GSum = byteArrayToDecimal(dwGSum);
//                    log.info("目标当前像素灰度和:" + GSum);
//                    byte[] iTw = Arrays.copyOfRange(data, index + 12, index + 16);
//                    iTw = toLittleEndian(iTw);
//                    int Tw = byteArrayToDecimal(iTw);
//                    log.info("目标当前的像素宽度:" + Tw);
//
//                    byte[] iTh = Arrays.copyOfRange(data, index + 16, index + 20);
//                    iTh = toLittleEndian(iTh);
//                    int Th = byteArrayToDecimal(iTh);
//                    log.info("目标当前的像素高度:" + Th);
//
//                    byte[] wPxlArea = Arrays.copyOfRange(data, index + 20, index + 22);
//                    wPxlArea = toLittleEndian(wPxlArea);
//                    int PxlArea = byteArrayToDecimal(wPxlArea);
//                    log.info("目标当前像素面积:" + PxlArea);
//
//                    byte[] cTrkNum = Arrays.copyOfRange(data, index + 22, index + 23);
//                    cTrkNum = toLittleEndian(cTrkNum);
//                    int TrkNum = byteArrayToDecimal(cTrkNum);
//                    log.info("轨迹点数:" + TrkNum);
//
//                    byte[] cStat = Arrays.copyOfRange(data, index + 23, index + 24);
//                    cStat = toLittleEndian(cStat);
//                    int Stat = byteArrayToDecimal(cStat);
//                    log.info("目标当前状态:" + Stat);
//
//                    byte[] sVx = Arrays.copyOfRange(data, index + 24, index + 26);
//                    sVx = toLittleEndian(sVx);
//                    int Vx = byteArrayToDecimal(sVx);
//                    log.info("目标当前速度矢量(像素距离)X:" + Vx);
//
//                    byte[] sVy = Arrays.copyOfRange(data, index + 26, index + 28);
//                    sVy = toLittleEndian(sVy);
//                    int Vy = byteArrayToDecimal(sVy);
//                    log.info("目标当前速度矢量(像素距离)Y:" + Vy);
//
//                    byte[] sAreaNo = Arrays.copyOfRange(data, index + 28, index + 30);
//                    sAreaNo = toLittleEndian(sAreaNo);
//                    int AreaNo = byteArrayToDecimal(sAreaNo);
//                    log.info("目标归属的告警区域号:" + AreaNo);
//
//                    byte[] cGrp = Arrays.copyOfRange(data, index + 30, index + 31);
//                    cGrp = toLittleEndian(cGrp);
//                    int Grp = byteArrayToDecimal(cGrp);
//                    log.info("所属组:" + Grp);
                    //endregion
                    byte[] szName = Arrays.copyOfRange(data, index + 64, index + 96);
                    int position = findIndexOfDoubleZero(szName);
                    String alarmPointName = "";
                    if (position != -1) {
                        byte[] result = new byte[position];
                        System.arraycopy(szName, 0, result, 0, position);
                        alarmPointName = new String(result, "GBK");
                    } else {
                        alarmPointName = new String(szName, "GBK");
                    }
                    // log.info("所属告警区域名称:" + alarmPointName);
                    byte[] afTx = Arrays.copyOfRange(data, index + 96, index + 100);
                    afTx = toLittleEndian(afTx);
                    float fTx = bytesToFloat(afTx);
                    //  log.info("水平角度:" + fTx);
                    byte[] afTy = Arrays.copyOfRange(data, index + 112, index + 116);
                    afTy = toLittleEndian(afTy);
                    float fTy = bytesToFloat(afTy);
                    //  log.info("垂直角度:" + fTy);
                    log.info("雷达信息:" + host + "【port】" + port + "【X】" + longitude + "【Y】" + lagitude);
                    Double[] radarXY = {longitude, lagitude};
                    Double[] alarmXY = CalculateCoordinates(radarXY, Distance, (double) fTx);
                    log.info("报警信息:" + "【id】" + id + "【name】" + alarmPointName + "【alarmTime】" + alarmTime + "【distance】" + Distance + "【P】" + fTx + "【T】" + fTy + "【X】" + alarmXY[0] + "【Y】" + alarmXY[1]);
                    RadarAlarmInfo alarmInfo=new RadarAlarmInfo();
                    alarmInfo.setId(id);
                    alarmInfo.setAlarmTime(alarmTime);
                    alarmInfo.setName(alarmPointName+"("+name+")");
                    alarmInfo.setLongitude(alarmXY[0]);
                    alarmInfo.setLagitude(alarmXY[1]);
                    MqttConsumer.publish(2, false, "radar", JSON.toJSONString(alarmInfo));
                }
            }
        } catch (Exception ex) {
            log.error(ex.getMessage());
        }
    }
    /**
     * byte数组转float
     */
    private float bytesToFloat(byte[] bytes) {
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        return buffer.getFloat();
    }
 
 
    /**
     * byte数组转整型
     */
    public int byteArrayToDecimal(byte[] byteArray) {
        int decimalValue = 0;
 
        for (int i = 0; i < byteArray.length; i++) {
            decimalValue = (decimalValue << 8) | (byteArray[i] & 0xFF);
        }
 
        return decimalValue;
    }
 
    /**
     * 大端转小端
     */
    public byte[] toLittleEndian(byte[] bigEndianBytes) {
        byte[] littleEndianBytes = new byte[bigEndianBytes.length];
 
        for (int i = 0; i < bigEndianBytes.length; i++) {
            int j = bigEndianBytes.length - i - 1;
            littleEndianBytes[i] = bigEndianBytes[j];
        }
 
        return littleEndianBytes;
    }
 
 
    // 创建缓冲区列表
    List<Byte> buffer = new ArrayList<>();
 
    /**
     * 接收完整包
     */
    public byte[] receiveCompletePacket(byte[] receivedData) {
        // 定义包尾字节序列
        byte[] packetEnd = {0x01, 0x02, 0x00};
        // 添加已接收的数据到缓冲区
        for (byte data : receivedData) {
            buffer.add(data);
        }
        // 检查缓冲区中的数据是否包含完整的包
        while (buffer.size() >= packetEnd.length) {
            int endIndex = findPacketEndIndex(buffer, packetEnd);
            if (endIndex != -1) {
                // 找到完整的包
                byte[] packet = extractPacketFromBuffer(buffer, endIndex + packetEnd.length);
                return packet;
            } else {
                // 未找到包尾,继续接收数据
                break;
            }
        }
 
        // 未找到完整的包
        return null;
    }
 
    /**
     * 获取包结束索引
     */
    public int findPacketEndIndex(List<Byte> buffer, byte[] packetEnd) {
        for (int i = 0; i <= buffer.size() - packetEnd.length; i++) {
            boolean isMatch = true;
            for (int j = 0; j < packetEnd.length; j++) {
                if (buffer.get(i + j) != packetEnd[j]) {
                    isMatch = false;
                    break;
                }
            }
            if (isMatch) {
                return i;
            }
        }
        return -1;
    }
 
    /**
     * 从缓冲区提取数据包
     */
    public byte[] extractPacketFromBuffer(List<Byte> buffer, int endIndex) {
        byte[] packet = new byte[endIndex];
        for (int i = 0; i < endIndex; i++) {
            packet[i] = buffer.get(i);
        }
        buffer.subList(0, endIndex).clear();
        return packet;
    }
 
    /**
     * 去掉包头和包尾校验及转义
     */
    public byte[] transferData(byte[] data) {
        data = Arrays.copyOfRange(data, 3, data.length);
        data = Arrays.copyOfRange(data, 0, data.length - 7);
        String dataStr = DatatypeConverter.printHexBinary(data);
        if (dataStr.contains("01020201")) {//转义01020201
            dataStr = dataStr.replaceAll("01020201", "010201");
        }
        if (dataStr.contains("01020200")) {//转义01020200
            dataStr = dataStr.replaceAll("01020200", "010200");
        }
        if (dataStr.contains("01020202")) {//转义01020202
            dataStr = dataStr.replaceAll("01020202", "010202");
        }
        data = DatatypeConverter.parseHexBinary(dataStr);
        return data;
    }
 
    /**
     * 找到00的索引位置
     */
    private int findIndexOfDoubleZero(byte[] bytes) {
        for (int i = 0; i < bytes.length - 1; i++) {
            if (bytes[i] == 0x00) {
                return i;
            }
        }
        return -1;
    }
 
    /**
     * 通过A点坐标,长度和Y轴角度计算B点坐标
     */
    public Double[] CalculateCoordinates(Double[] radarCoordinates, Integer distance, Double angle) {
        double[] to_wgs84 = LonlatConver.gcj02_To_Wgs84(radarCoordinates[0], radarCoordinates[1]);
        double Ax = to_wgs84[0]; // A 点的 X 坐标
        double Ay = to_wgs84[1]; // A 点的 Y 坐标
        double AB = distance; // AB 的长度
        double angleWithYAxisDegrees = angle; // AB 与 Y 轴的角度(以度数表示)
        GeodeticCalculator calculator = new GeodeticCalculator();
        GlobalCoordinates globalCoordinates = new GlobalCoordinates(Ay, Ax);
        GlobalCoordinates globalCoordinates1 = calculator.calculateEndingGlobalCoordinates(Ellipsoid.WGS84, globalCoordinates, angleWithYAxisDegrees, AB);
        return new Double[]{globalCoordinates1.getLongitude(), globalCoordinates1.getLatitude()};
    }
 
}