JAVA版DLT645解析

该文介绍了使用JAVA和Netty框架实现DLT645-2007电能表通信协议的TCP服务器,服务器监听端口接收DTU设备通过232或485连接仪表的数据,进行解析并处理。主要涉及心跳帧处理、数据帧解析、设备注册管理以及数据处理。代码中包含了DTL645协议解析类ReadData2007和SendData2007,用于构建和解析报文。
摘要由CSDN通过智能技术生成

JAVA版DLT645解析

前言

几年前一个项目需要用到,研究过一段时间,现在也忘得差不多了,所以主要贴代码为主,想到哪些说哪些,见谅;
开发参照DLT645-2007多功能电能表通信协议(2015)

代码结构

在这里插入图片描述
一个简单得netty框架加上645协议解析,

大致说明

DTU设置好IP和端口波特率,再现场通过232或者485连接仪表,然后dtu和服务端软件建立tcp连接;
所以我们做的就只是起一个netty服务监听一个端口,等dtu设备和咱们建立tcp连接

首先是netty的配置

@Slf4j
public class DTL645DiscardServer implements Runnable{

    private final int port;

    public DTL645DiscardServer(int port) {
        this.port = port;
    }

    @Override
    public void run() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup(100);
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new DTL645ChannelInitializer());
            ChannelFuture f = b.bind(port);
            log.info("==== server start ====");
//            Channel channel = b.bind(port).sync().channel();
//            channel.closeFuture().sync();
            f.channel().closeFuture().sync();
            log.info("==== server end ====");
        } catch (Exception e){
            log.error("启动数据采集监听出错 ",e);
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
//        new CommandFetcherTest().run();
        new DTL645DiscardServer(61026).run();

    }
}

服务启动监听端口

@Component
@Slf4j
public class ApplicationRunnerImpl implements ApplicationRunner {


    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("启动监听端口61026");
        DTL645DiscardServer discardServer = new DTL645DiscardServer(61026);
        Thread thread = new Thread(discardServer);
        thread.start();
    }
}

model目录

这个目录下定义有构建dlt645发送报文和解析报文的两个对象,还有两个全局map
ChannelMap ( 设备信息:通道号)
CtxMap (通道号:通道对象)
ReadData2007 (解析报文)
SendData2007 (发送报文)
后者是根据报文结构构建好对象方便使用,关于dlt645数据帧结构可以看看网上文章了解,这里没别人写的好,不做讲解了;
前两个是处理注册后的连接,这里注册就是首次会把设备id发过来,设备id带上固定得校验,ok得放入map里,不ok就给他关掉
ChannelMap :

public class ChannelMap {

    private static ConcurrentHashMap<String, Object> MAP = null;

    public ChannelMap() {
    }

    public static ConcurrentHashMap<String, Object> getMap() {
        if (MAP == null){
            synchronized (CtxMap.class){
                if(MAP == null){
                    MAP = new ConcurrentHashMap<String, Object>();
                }
            }
        }
        return MAP;
    }
}

CtxMap :

public class CtxMap {

    private static ConcurrentHashMap<String, ChannelHandlerContext> MAP = null;

    public CtxMap() {
    }

    public static ConcurrentHashMap<String, ChannelHandlerContext> getMap() {
        if (MAP == null){
            synchronized (CtxMap.class){
                if(MAP == null){
                    MAP = new ConcurrentHashMap<String, ChannelHandlerContext>();
                }
            }
        }
        return MAP;
    }
}

ReadData2007

public class ReadData2007 {

    private int[] effectiveData;      //有效数据
    private int controlCode;         //控制码
    private Integer dot;              //确定小数点
    private String deviceAddress;     //表地址
    private byte[] dataType;     //数据标识


    public ReadData2007(byte[] bytes) {
        /**
         * 起始符  地址域  起始符  控制码  数据域长度              数据域                校验码   结束符
         *  0       1~6      7      8         9                   9+n
         * 1字节   6字节   1字节   1字节     1字节         n字节(4字节数据标识+数据)      1字节   1字节
         */
        this.deviceAddress = CheckUtil.GetBCDAddress(bytes);
        boolean check = CheckUtil.checkData(bytes);
        if (!check) {
            System.out.println("check error");
            System.out.print("error msg: ");
            for (byte b : bytes) {
                System.out.print(b + " ");
            }
            return;
        } else {
            int[] read_ints = ConvertUtil.bytesToInts(bytes);
            this.controlCode = read_ints[8];  //控制码
            int lengthOfData = read_ints[9];  //数据域长度

            if (read_ints.length>12&&lengthOfData==1){//对于更改速率的应答,数据域中无标识符,只有一个字节;从站异常应答也是如此
                this.effectiveData = new int[]{read_ints[10]};
            }

            if (read_ints.length>16&&lengthOfData>=4){//在有数据域的情况下
                byte[] type = new byte[4];   // 4字节的数据标识
                int[] data = new int[lengthOfData - 4]; //除去两字节的数据标识剩下的数据长度

                for (int t = 0; t < 4; t++) {
                    type[t] = (byte) (read_ints[10 + t] - 0x33);  //构建数据标识 DI0 DI1
                }
                if (lengthOfData>4){
                    for (int d = 0; d < lengthOfData - 4; d++) {
                        data[d] = read_ints[14 + d] - 0x33;                  //获取真正数据域
                        //  2007

                        //data[d] = Integer.parseInt(ConvertUtil.intToHex(data[d]));//会报异常:NumberFormatException.forInputString
                    }
                }
                this.effectiveData = data;
                this.dataType = type;
                DataIdentify2007 dataIdentify2007 = new DataIdentify2007();
                this.dot = dataIdentify2007.doc.get(Arrays.toString(type));
            }
        }
    }

    public int[] getEffectiveData() {
        return effectiveData;
    }

    public void setEffectiveData(int[] effectiveData) {
        this.effectiveData = effectiveData;
    }

    public int getControlCode() {
        return controlCode;
    }

    public void setControlCode(int controlCode) {
        this.controlCode = controlCode;
    }

    public Integer getDot() {
        return dot;
    }

    public void setDot(Integer dot) {
        this.dot = dot;
    }

    public String getDeviceAddress() {
        return deviceAddress;
    }

    public void setDeviceAddress(String deviceAddress) {
        this.deviceAddress = deviceAddress;
    }

    public byte[] getDataType() {
        return dataType;
    }

    public void setDataType(byte[] dataType) {
        this.dataType = dataType;
    }


    @Override
    public String toString() {
        return "ReadData1997{" +
                "effectiveData=" + Arrays.toString(effectiveData) +
                ", controlCode=" + controlCode +
                ", dot=" + dot +
                ", deviceAddress='" + deviceAddress + '\'' +
                ", dataType=" + Arrays.toString(dataType) +
                '}';
    }

}
public class SendData2007 {
    private int[] address; //地址域
    private int control; //控制码
    private int length; //数据长度
    private int[] data; //数据,包括数据标识
    int cs; //校验码

    public  SendData2007(int[] address, int control, int[] data){
        this.address = address;
        this.control = control;
        this.data = data;
        this.length=data.length;
    }

    //构建完整的帧,发送前调用
    public byte[] send() {
        int[] temp = new int[12+data.length];
        temp[0] = Constants.START;//帧起始符
        temp[7] = temp[0];//帧结束符
        for (int i = 1; i <= address.length; i++) {//地址域
            temp[i] =  address[i-1];
        }
        temp[8] =  control;//控制码
        temp[9] = length;//数据长度
        if (data.length>0){
            for (int i = 0; i <data.length ; i++) {//数据
                temp[10+i] =  data[i] + 0x33;
            }
        }
        //得到校验码
        cs =0;
        for(int i=0;i<temp.length-2;i++) {
            cs += (temp[i] & 0xff) % 256;
        }
        temp[temp.length-2] =  cs; //校验码
        temp[temp.length-1]= Constants.ENDING;//结束符
        byte[] msg = new byte[temp.length];
        //转化为字节
        for (int i = 0; i < temp.length; i++) {
            msg[i] = (byte) temp[i];
        }
        return msg;
    }

    @Override
    public String toString() {
        return "SendData1997{" +
                "address=" + Arrays.toString(address) +
                ", control=" + control +
                ", length=" + length +
                ", data=" + Arrays.toString(data) +
                ", cs=" + cs +
                '}';
    }

}

nio

nio目录下是一个通用netty框架运用

    protected void initChannel(Channel ch) throws Exception {

        ch.pipeline().addLast(new IdleStateHandler(600,0,0));
//        ch.pipeline().addLast(new StringEncoder());
//        ch.pipeline().addLast(new DTLHandler());
//        ch.pipeline().addLast(new StringDecoder());
        ch.pipeline().addLast(new Encoder());
        ch.pipeline().addLast(new DTL645DiscardServerHandler());
    }

主要贴一下DTL645ChannelInitializer 和 DTL645Analysis
大致是DTL645DiscardServerHandler处理报文数据,排除心跳帧后调用DTL645Analysis做数据得处理,具体见注释
DTL645DiscardServerHandler:

@Slf4j
public class DTL645DiscardServerHandler extends SimpleChannelInboundHandler<Object> {

    private String status = "init";

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        int length = byteBuf.readableBytes();
        byte[] tmps = new byte[length];
        byteBuf.readBytes(tmps);
        String s = bytesToHexString(tmps);
        log.info("数据接收:"+s);


        // fe 为心跳包
        if (tmps.length != 1){
            DTL645Analysis dtl645Analysis = new DTL645Analysis(tmps,ctx);
            Thread thread = new Thread(dtl645Analysis);
            thread.start();
        }

    }



    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("连接断开");
        // 通道号:通道对象
        ConcurrentHashMap<String,ChannelHandlerContext> ctxMap = CtxMap.getMap();
        // 设备信息:通道号
        ConcurrentHashMap<String,Object> channelMap = ChannelMap.getMap();
        if (ctxMap.contains(ctx)){
            ctxMap.remove(ctx);
            channelMap.remove(ctx.channel().id().toString());
            System.out.println("剔除已经断开通道");
        }
    }

    public String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if ((src == null) || (src.length <= 0)) {
            return null;
        }
        for (byte aSrc : src) {
            int v = aSrc & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv).append(" ");
        }
        return stringBuilder.toString();
    }

}

DTL645Analysis :

public class DTL645Analysis implements Runnable{

    private byte[] sourcesBytes;
    private ChannelHandlerContext ctx;

    public DTL645Analysis(byte[] sourcesBytes, ChannelHandlerContext ctx) {
        this.sourcesBytes = sourcesBytes;
        this.ctx = ctx;
    }

    @Override
    public void run() {

        sourcesBytes = transferred(transferred(sourcesBytes, 0xed), 0xee); //转义
        // 通道号:通道对象
        ConcurrentHashMap<String,ChannelHandlerContext> ctxMap = CtxMap.getMap();
        // 设备信息:通道号
        ConcurrentHashMap<String,Object> channelMap = ChannelMap.getMap();
        // Rtu设备编号
        // 第一次接入
        if (channelMap.get(ctx.channel().id().toString()) == null){
            String rtuName = new String(conByte(sourcesBytes, 4, sourcesBytes.length - 7));
            System.out.println("rtuName"+rtuName);
            // 注册
            if (rtuName.contains("87775236")){
                System.out.println("首次注册"+rtuName);
                channelMap.put(ctx.channel().id().toString(), rtuName);
                ctxMap.put(rtuName, ctx);

                // 测试一下发送
//                SendHelper.readData(ctx,"810000218497", CommandType.A_PHASE_VOLTAGE);
            }else {
                ctx.close();
            }

        }else {
            int num = 0;
            for (int i = 0; i < sourcesBytes.length; i++) {
                if (sourcesBytes[i] == 104){
                    num = i;
                    break;
                }
            }

            // 发送的命令 反馈的数据
            byte[] newSourcesBytes = new byte[sourcesBytes.length-num];
            arraycopy(sourcesBytes, num, newSourcesBytes, 0, sourcesBytes.length-num);
            System.out.println(bytesToHexString(newSourcesBytes));

            System.out.println("处理前"+bytesToHexString(sourcesBytes));
            System.out.println("处理后"+bytesToHexString(newSourcesBytes));

            ReadData2007 readData2007 = new ReadData2007(newSourcesBytes);
            System.out.println(readData2007.toString());

            int controlCode = readData2007.getControlCode();
            switch (controlCode){
                case 145:    // 0x91 主站请求读数据应答后 -- 无后续数据帧情况
                    System.out.println("数据:"+getDataResult(readData2007));
                    break;
                case 177:   // 0xb1  有后续数据帧
                    System.out.println("有后续数据帧");
                    break;
                case 209:
                    System.out.println("异常");
                    break;
                default:
                    System.out.println("返回数据出错");
                    break;
            }




        }

    }

    private double getDataResult(ReadData2007 readData) {
        int[] data = readData.getEffectiveData();
        StringBuilder sb = new StringBuilder();
        for (int i = data.length - 1; i >= 0; i--) {
            sb.append(Integer.toHexString(data[i]).length()<2?"0"+Integer.toHexString(data[i]):Integer.toHexString(data[i]));
        }
        sb.insert(sb.length() - readData.getDot(), ".");
        return Double.valueOf(sb.toString());
    }


    private static byte[] transferred(byte[] paramsBytes, int params) { //字节转义
        byte[] tmpEd = new byte[paramsBytes.length];
        int countFd = 0;
        for (int i = 0; i < paramsBytes.length; i++) {
            if ((paramsBytes[i] & 0xff) == 0xfd) { //检查数据里面是否包含特殊字节 0xfd
                if (i + 1 < paramsBytes.length) {
                    if ((paramsBytes[i + 1] & 0xff) == params) {
                        switch (params) {
                            case 0xed:
                                tmpEd[i - countFd] = (byte) 0xfd;
                                break;
                            case 0xee:
                                tmpEd[i - countFd] = (byte) 0xfe;
                                break;
                        }
                        i++;
                        countFd++;
                    } else {
                        tmpEd[i - countFd] = paramsBytes[i];
                    }
                } else {
                    tmpEd[i - countFd] = paramsBytes[i];
                }
            } else { //不是特殊字节 0xfd
                tmpEd[i - countFd] = paramsBytes[i];
            }
        }
        byte[] resultBytes = conByte(tmpEd, 0, tmpEd.length - 1 - countFd);
        return resultBytes;
    }

    private static byte[] conByte(byte[] tmp, int start, int end) {
        byte[] b = new byte[end - start + 1];
        int j = 0;
        for (int i = start; i <= end; i++) {
            b[j] = tmp[i];
            j++;
        }
        return b;
    }

    public String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if ((src == null) || (src.length <= 0)) {
            return null;
        }
        for (byte aSrc : src) {
            int v = aSrc & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv).append(" ");
        }
        return stringBuilder.toString();
    }
}

其他一些重要得配置说明

DataIdentify2007:等同于电能量标志编码表,定义了数据标志,格式,长度,单位等等,我这里好像只用到了数据格式,也就是xxxx.xx 确定小数点在哪里,其他的没写那么细,在params也定义了些,需求上简单,不需要干弄那么多

public class DataIdentify2007 {
    public List<byte[]> dataIdent = new ArrayList<>();
    public Map<String,String> identifyname=new HashMap<String,String>();
    public Map<String,Integer> doc=new HashMap<String,Integer>();
    public int length;
    public DataIdentify2007() {
        dataIdent.add(0,new byte[]{(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00});//有功总电能

        dataIdent.add(1,new byte[]{(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x00});//正向有功总电能
        dataIdent.add(2,new byte[]{(byte)0x00,(byte)0x00,(byte)0x02,(byte)0x00});//反向有功总电能

        dataIdent.add(3,new byte[]{(byte)0x00,(byte)0x01,(byte)0x01,(byte)0x02});//A相电压
        dataIdent.add(4,new byte[]{(byte)0x00,(byte)0x02,(byte)0x01,(byte)0x02});//B相电压
        dataIdent.add(5,new byte[]{(byte)0x00,(byte)0x03,(byte)0x01,(byte)0x02});//C相电压

        dataIdent.add(6,new byte[]{(byte)0x00,(byte)0x01,(byte)0x02,(byte)0x02});//A相电流
        dataIdent.add(7,new byte[]{(byte)0x00,(byte)0x02,(byte)0x02,(byte)0x02});//B相电流
        dataIdent.add(8,new byte[]{(byte)0x00,(byte)0x03,(byte)0x02,(byte)0x02});//C相电流

        dataIdent.add(9,new byte[]{(byte)0x00,(byte)0x00,(byte)0x03,(byte)0x02});//瞬时有功功率
        dataIdent.add(10,new byte[]{(byte)0x00,(byte)0x01,(byte)0x03,(byte)0x02});//瞬时A相有功功率
        dataIdent.add(11,new byte[]{(byte)0x00,(byte)0x02,(byte)0x03,(byte)0x02});//瞬时B相有功功率
        dataIdent.add(12,new byte[]{(byte)0x00,(byte)0x03,(byte)0x03,(byte)0x02});//瞬时C相有功功率

        dataIdent.add(13,new byte[]{(byte)0x00,(byte)0x00,(byte)0x04,(byte)0x02});//瞬时无功功率
        dataIdent.add(14,new byte[]{(byte)0x00,(byte)0x01,(byte)0x04,(byte)0x02});//瞬时A相总无功功率
        dataIdent.add(15,new byte[]{(byte)0x00,(byte)0x02,(byte)0x04,(byte)0x02});//瞬时B相总无功功率
        dataIdent.add(16,new byte[]{(byte)0x00,(byte)0x03,(byte)0x04,(byte)0x02});//瞬时C相总无功功率

        dataIdent.add(17,new byte[]{(byte)0x00,(byte)0x00,(byte)0x05,(byte)0x02});//瞬时视在功率
        dataIdent.add(18,new byte[]{(byte)0x00,(byte)0x01,(byte)0x05,(byte)0x02});//A相视在功率
        dataIdent.add(19,new byte[]{(byte)0x00,(byte)0x02,(byte)0x05,(byte)0x02});//B相视在功率
        dataIdent.add(20,new byte[]{(byte)0x00,(byte)0x03,(byte)0x05,(byte)0x02});//C相视在功率

        dataIdent.add(21,new byte[]{(byte)0x00,(byte)0x00,(byte)0x06,(byte)0x02});//总功率因数
        dataIdent.add(22,new byte[]{(byte)0x00,(byte)0x01,(byte)0x06,(byte)0x02});//A相功率因数
        dataIdent.add(23,new byte[]{(byte)0x00,(byte)0x02,(byte)0x06,(byte)0x02});//B相功率因数
        dataIdent.add(24,new byte[]{(byte)0x00,(byte)0x03,(byte)0x06,(byte)0x02});//C相功率因数

        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00}),"total_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x00}),"pos_positive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x02,(byte)0x00}),"neg_positive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x01,(byte)0x02}),"a_voltage");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x01,(byte)0x02}),"b_voltage");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x01,(byte)0x02}),"c_voltage");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x02,(byte)0x02}),"a_current");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x02,(byte)0x02}),"b_current");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x02,(byte)0x02}),"c_current");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x03,(byte)0x02}),"positive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x03,(byte)0x02}),"a_positive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x03,(byte)0x02}),"b_positive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x03,(byte)0x02}),"c_positive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x04,(byte)0x02}),"reactive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x04,(byte)0x02}),"a_reactive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x04,(byte)0x02}),"b_reactive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x04,(byte)0x02}),"c_reactive_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x05,(byte)0x02}),"apparent_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x05,(byte)0x02}),"a_apparent_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x05,(byte)0x02}),"b_apparent_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x05,(byte)0x02}),"c_apparent_power");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x06,(byte)0x02}),"influence");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x06,(byte)0x02}),"a_influence");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x06,(byte)0x02}),"b_influence");
        identifyname.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x06,(byte)0x02}),"c_influence");

        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x01,(byte)0x00}),2);  // 当前正向有功费率1电能
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x00,(byte)0x00}),2);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x00}),2);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x02,(byte)0x00}),2);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x01,(byte)0x02}),1);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x01,(byte)0x02}),1);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x01,(byte)0x02}),1);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x02,(byte)0x02}),3);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x02,(byte)0x02}),3);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x02,(byte)0x02}),3);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x03,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x03,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x03,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x03,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x04,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x04,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x04,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x04,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x05,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x05,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x05,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x05,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x00,(byte)0x06,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x01,(byte)0x06,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x02,(byte)0x06,(byte)0x02}),4);
        doc.put(Arrays.toString(new byte[]{(byte)0x00,(byte)0x03,(byte)0x06,(byte)0x02}),4);
    }
    public int  getLength()
    {
        return dataIdent.size();
    }
}

CommandType:

public enum CommandType {
    NONE(""),//无命令
    //读数据的命令,统一以0开头
    CUR_POSITIVE_ACTIVE_POWER("04000402"),//读表号  --phase voltage  positive active power
    A_PHASE_VOLTAGE("02010100"),
    Z_POSITIVE_ACTIVE_POWER("00010100"),  // 正向有功总电能
    Z_POSITIVE_ACTIVE_POWER1("00010000")  // 正向有功总电能
    ;


    private String value;
    CommandType(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
/**
 * 提取器 此处获取地址和连接 向从机请求数据
 * @Author: yele
 * @Date: 2021/1/14 14:43
 */
@Slf4j
@Component
public class CommandFetcher {


    @Scheduled(cron = "*/15 * * * * ?")
    public void send(){
        System.out.println("定时任务发送");
        // 通道号:通道对象
        ConcurrentHashMap<String, ChannelHandlerContext> ctxMap = CtxMap.getMap();
        if (ctxMap.size() > 0){
            System.out.println("发送测试");
            ChannelHandlerContext ctx = ctxMap.get("02887775236");
            ChannelHandlerContext ctx1 = ctxMap.get("02787775236");
            ChannelHandlerContext ctx2 = ctxMap.get("03087775236");
//            SendHelper.readData(ctx,"810000218497", CommandType.Z_POSITIVE_ACTIVE_POWER);
//            SendHelper.readData(ctx,"000000111111", CommandType.Z_POSITIVE_ACTIVE_POWER);
//            SendHelper.readData(ctx,"810000218497", CommandType.A_PHASE_VOLTAGE);

//            String strShunshi = "FE FE FE FE 68 97 84 21 00 00 81 68 11 04 33 34 34 33 6E 16"; //读取瞬时流量
//            String[] strShunshiArray = strShunshi.split(" ");
//            byte[] tmpShunshi = new byte[strShunshiArray.length];
//            for (int i = 0; i < tmpShunshi.length; i++) {
//                tmpShunshi[i] = (byte) Integer.parseInt(strShunshiArray[i], 16);
//            }
//            ctx.writeAndFlush(tmpShunshi);//发送读取数据命令
            if (ctx1 != null){
                SendHelper.readData(ctx1,"810000218495", CommandType.Z_POSITIVE_ACTIVE_POWER1);
            }
//
//            if (ctx2 != null){
//                SendHelper.readData(ctx2,"000002062701", CommandType.Z_POSITIVE_ACTIVE_POWER1);
//            }

//            String strShunshi1 = "68 97 84 21 00 00 81 68 11 04 33 34 34 33 70 16"; //读取瞬时流量
//            String[] strShunshiArray1 = strShunshi1.split(" ");
//            byte[] tmpShunshi1 = new byte[strShunshiArray1.length];
//            for (int i = 0; i < tmpShunshi1.length; i++) {
//                tmpShunshi1[i] = (byte) Integer.parseInt(strShunshiArray1[i], 16);
//            }
//            ctx.writeAndFlush(tmpShunshi1);//发送读取数据命令
//
//            System.out.println("发送"+strShunshi1);
        }
    }

}

总结

写的比较乱,代码也没贴全,后面我把代码也放上来,这里再次说明下处理的思路;
首先设备注册进来,长连接就建立了,两个全局的map存储的就是设备对应的通道,设备不会主动发数据过来,需要做一个请求读的操作。举个例子获取设备id为02787775236的正向有功总电能

ChannelHandlerContext ctx1 = ctxMap.get("02787775236");
            if (ctx1 != null){
                SendHelper.readData(ctx1,"810000218495", CommandType.Z_POSITIVE_ACTIVE_POWER);
            }

获得应答处理

            int controlCode = readData2007.getControlCode();
            switch (controlCode){
                case 145:    // 0x91 主站请求读数据应答后 -- 无后续数据帧情况
                    System.out.println("数据:"+getDataResult(readData2007));
                    break;
                case 177:   // 0xb1  有后续数据帧
                    System.out.println("有后续数据帧");
                    break;
                case 209:
                    System.out.println("异常");
                    break;
                default:
                    System.out.println("返回数据出错");
                    break;
            }

https://download.csdn.net/download/Gyele/87689835

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值