‘liusuyi’
2023-06-26 05e5da7edd1368a2e8f216b3efc6e87eb8d375a2
解析雷达报警报文
已添加1个文件
已修改5个文件
415 ■■■■■ 文件已修改
src/main/java/com/ard/alarm/tube/service/TubeAlarmService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ard/utils/SpringTool.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ard/utils/tcp/NettyTcpClient.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ard/utils/tcp/NettyTcpClientHandler.java 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ard/utils/tcp/index.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/logback-spring.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ard/alarm/tube/service/TubeAlarmService.java
@@ -42,9 +42,9 @@
            return;
        }
        nettyUdpServer.init(udpPort);
        log.info("UDP服务已经启动");
        nettyTcpClient.init("112.98.126.2",1200);
        log.info("TCP客户端已连接");
        log.info("UDP服务已启动");
        nettyTcpClient.init("127.0.0.1",1200);
        log.info("TCP客户端已启动");
    }
    @Async("alarm")
src/main/java/com/ard/utils/SpringTool.java
@@ -32,4 +32,21 @@
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }
    /**
     * Byte字节转Hex
     * @param b å­—节
     * @return Hex
     */
    public static String byteToHex(byte b)
    {
        String hexString = Integer.toHexString(b & 0xFF);
        //由于十六进制是由0~9、A~F来表示1~16,所以如果Byte转换成Hex后如果是<16,就会是一个字符(比如A=10),通常是使用两个字符来表示16进制位的,
        //假如一个字符的话,遇到字符串11,这到底是1个字节,还是1和1两个字节,容易混淆,如果是补0,那么1和1补充后就是0101,11就表示纯粹的11
        if (hexString.length() < 2)
        {
            hexString = new StringBuilder(String.valueOf(0)).append(hexString).toString();
        }
        return hexString.toUpperCase();
    }
}
src/main/java/com/ard/utils/tcp/NettyTcpClient.java
@@ -2,6 +2,7 @@
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
@@ -36,6 +37,14 @@
                        }
                    });
            ChannelFuture future = bootstrap.connect(host, port).sync();
            // æ·»åŠ è¿žæŽ¥æˆåŠŸçš„ç›‘å¬å™¨
            future.addListener((ChannelFutureListener) future1 -> {
                if (future1.isSuccess()) {
                    log.info("tcp连接成功"+host+":"+port);
                } else {
                    log.info("tcp连接失败"+host+":"+port);
                }
            });
            future.channel().closeFuture().sync();
        } catch (Exception ex) {
src/main/java/com/ard/utils/tcp/NettyTcpClientHandler.java
@@ -1,11 +1,17 @@
package com.ard.utils.tcp;
import com.ard.utils.SpringTool;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import sun.nio.cs.ext.GBK;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
/**
 * @Description: tcp客户端处理
@@ -14,30 +20,328 @@
 * @Date: 2023å¹´06月25日17:02
 * @Version: 1.0
 **/
@Slf4j
@Slf4j(topic = "radar")
public class NettyTcpClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg){
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
        // å¤„理接收到的消息
        byte[] byteArray = new byte[msg.readableBytes()];
        msg.getBytes(msg.readerIndex(), byteArray);
        String hexString = DatatypeConverter.printHexBinary(byteArray);
        log.info("Received: " + hexString);
        byte[] bytes = receiveCompletePacket(byteArray);
        if (bytes != null) {
            String hexString = DatatypeConverter.printHexBinary(bytes);
            log.info(hexString);
            processData(bytes);
        }
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    public void channelActive(ChannelHandlerContext ctx) {
        // å½“客户端连接成功后,发送消息给服务器
        ByteBuf message = ctx.alloc().buffer();
        message.writeBytes("Hello, Server!".getBytes());
        ctx.writeAndFlush(message);
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                ByteBuf message = ctx.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);
                ctx.writeAndFlush(message);
            }
        };
        Timer timer = new Timer();
        // timer.schedule(timerTask, 0, 6000);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("连接异常");
        // å‘生异常时的处理
        cause.printStackTrace();
        ctx.close();
    }
    public static void processData(byte[] data) {
        try {
            data = transferData(data);//去掉包头和包尾及转义
            String s = DatatypeConverter.printHexBinary(data);
            log.info(s);
            byte[] type = Arrays.copyOfRange(data, index.type[0], index.type[1]);//命令类型
            log.info("命令类型:" + DatatypeConverter.printHexBinary(type));
            byte[] cmdId = Arrays.copyOfRange(data, index.funcc[0], index.funcc[1]);//命令ID
            log.info("命令ID:" + DatatypeConverter.printHexBinary(cmdId));
            byte[] payloadSize = Arrays.copyOfRange(data, index.payloadSize[0], index.payloadSize[1]);//有效负载大小
            //  log.info("有效负载大小:" + DatatypeConverter.printHexBinary(payloadSize));
            payloadSize = toLittleEndian(payloadSize);
            // log.info("有效负载大小(转小端):" + DatatypeConverter.printHexBinary(payloadSize));
            int payloadSizeToDecimal = byteArrayToDecimal(payloadSize);
            log.info("有效负载大小(转整型):" + payloadSizeToDecimal);
            if (Arrays.equals(cmdId, new byte[]{0x01})) {
                byte[] dwTim = Arrays.copyOfRange(data, index.dwTim[0], index.dwTim[1]);
                // log.info("周视图像的出现时间:" + DatatypeConverter.printHexBinary(dwTim));
                dwTim = toLittleEndian(dwTim);
                // log.info("周视图像的出现时间(转小端):" + DatatypeConverter.printHexBinary(dwTim));
                // log.info("周视图像的出现时间(转整型):" + byteArrayToDecimal(dwTim));
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                long l = byteArrayToDecimal(dwTim);
                String format = sdf.format(l * 1000);
                log.info("周视图像的出现时间(转date):" + format);
                byte[] wTargetNum = Arrays.copyOfRange(data, index.wTargetNum[0], index.wTargetNum[1]);
                wTargetNum = toLittleEndian(wTargetNum);
                // log.info("目标总点数(转小端):" + DatatypeConverter.printHexBinary(wTargetNum));
                int targetNum = byteArrayToDecimal(wTargetNum);
                log.info("目标总点数(转整型):" + targetNum);
                //解析NET_TARGET_UNIT(64是NET_TARGET_HEAD的字节数)
                int unitIndex = index.acRes[1];
                log.info("有效负载无头起始位:" + unitIndex);
//                byte datum = data[unitIndex];
//                log.info("无头起始字节:" + SpringTool.byteToHex(datum));
                int UNITNum = (payloadSizeToDecimal - 64) / targetNum;
                log.info("单个Unit大小:" + UNITNum);
                for (int i = 0; i < targetNum; i++) {
                    Integer index = unitIndex + UNITNum * i;
                    byte[] dwID = Arrays.copyOfRange(data, index, index + 4);
                    dwID = toLittleEndian(dwID);
                    // log.info("dwID:" + DatatypeConverter.printHexBinary(dwID));
                    int id = byteArrayToDecimal(dwID);
                    log.info("目标ID号:" + id);
                    byte[] dwGSum = Arrays.copyOfRange(data, index + 4, index + 8);
                    dwGSum = toLittleEndian(dwGSum);
                    int GSum = byteArrayToDecimal(dwGSum);
                    log.info("目标当前像素灰度和:" + GSum);
                    byte[] iDistance = Arrays.copyOfRange(data, index + 8, index + 12);
                    iDistance = toLittleEndian(iDistance);
                    int Distance = byteArrayToDecimal(iDistance);
                    log.info("目标当前距离(m):" + Distance);
                    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);
                    byte[] szName = Arrays.copyOfRange(data, index + 64, index + 96);
                    String str = new String(szName, "GBK");
                    log.info("所属告警区域名称:" + str);
                }
            }
        } catch (Exception ex) {
            log.error(ex.getMessage());
        }
    }
    public static int byteArrayToDecimal(byte[] byteArray) {
        int decimalValue = 0;
        for (int i = 0; i < byteArray.length; i++) {
            decimalValue = (decimalValue << 8) | (byteArray[i] & 0xFF);
        }
        return decimalValue;
    }
    public static 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;
    }
    public static Integer processPayloadSize(String payloadSize) {//解析有效负载大小
        Integer payloadSizeInfo = 0;
        String[] payloadSizeArr = payloadSize.split("");
        for (int i = 0; i <= payloadSizeArr.length - 1; i++) {
            Integer num = null;
            switch (payloadSizeArr[i]) {
                case "0":
                    num = 0;
                    break;
                case "1":
                    num = 1;
                    break;
                case "2":
                    num = 2;
                    break;
                case "3":
                    num = 3;
                    break;
                case "4":
                    num = 4;
                    break;
                case "5":
                    num = 5;
                    break;
                case "6":
                    num = 6;
                    break;
                case "7":
                    num = 7;
                    break;
                case "8":
                    num = 8;
                    break;
                case "9":
                    num = 9;
                    break;
                case "a":
                    num = 10;
                    break;
                case "b":
                    num = 11;
                    break;
                case "c":
                    num = 12;
                    break;
                case "d":
                    num = 13;
                    break;
                case "e":
                    num = 14;
                    break;
                case "f":
                    num = 15;
                    break;
                default:
                    break;
            }
            if (i % 2 == 0) {
                payloadSizeInfo = payloadSizeInfo + num * 16;
            } else {
                payloadSizeInfo = payloadSizeInfo + num * 256;
            }
        }
        return payloadSizeInfo;
    }
    public static Boolean checkPayloadSize(Integer payloadSizeInfo, String data) {//校验有效负载大小
        Integer payloadSize = (data.length() - 8 - 8) / 2;//去除包头8位和校验8位
        if (payloadSize.equals(payloadSizeInfo)) {
            return true;
        } else {
            return false;
        }
    }
    // åˆ›å»ºç¼“冲区列表
    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 static 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 static 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 static 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;
    }
}
src/main/java/com/ard/utils/tcp/index.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ard.utils.tcp;
/**
 * @Description:
 * @ClassName: domain
 * @Author: åˆ˜è‹ä¹‰
 * @Date: 2023å¹´06月26日15:21
 * @Version: 1.0
 **/
public class index {
    //header
    public static Integer[] type = {0,1};
    public static Integer[] funcc = {1,2};
    public static Integer[] payloadSize = {2,4};
    //NET_TARGET_HEAD
    public static Integer[] dwTim = {4,8};
    public static Integer[] wTargetNum = {8,10};
    public static Integer[] acRes = {10,68};
    //NET_TARGET_UNIT
//    public static Integer[] dwID = {68,72};
//    public static Integer[] dwGSum = {};
//    public static Integer[] iDistance = 2;
//    public static Integer[] cTrkNum = 1;
//    public static Integer[] cStat = 1;
//    public static Integer[] sVx = 2;
//    public static Integer[] sVy = 2;
//    public static Integer[] sAreaNo = 2;
//    public static Integer[] cGrp = 1;
//    public static Integer[] acRes1 = 33;
//    public static Integer[] szName = 32;
//    public static Integer[] afTx = 16;
//    public static Integer[] afTy = 16;
}
src/main/resources/logback-spring.xml
@@ -76,6 +76,28 @@
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!--雷达报警日志输出-->
    <appender name="radar" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/radar.log</file>
        <!--循环政策:基于时间创建日志文件-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件名格式-->
            <fileNamePattern>${log.path}/radar.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--日志最大的历史60天-->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--过滤的级别-->
            <level>INFO</level>
            <!--匹配时的操作:接收(记录)-->
            <onMatch>ACCEPT</onMatch>
            <!--不匹配时的操作:拒绝(不记录)-->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console"/>
    </root>
@@ -91,5 +113,9 @@
    <root level="INFO">
        <appender-ref ref="camera"/>
    </root>
    <!--雷达报警日志-->
    <root level="INFO">
        <appender-ref ref="radar"/>
    </root>
</configuration>