JAVA版DLT645解析

2024-03-09 12:20
文章标签 java 解析 dlt645

本文主要是介绍JAVA版DLT645解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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;}@Overridepublic 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 {@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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";@Overrideprotected 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();}}@Overridepublic 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;}@Overridepublic 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) { //检查数据里面是否包含特殊字节 0xfdif (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 { //不是特殊字节 0xfdtmpEd[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 powerA_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

这篇关于JAVA版DLT645解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/790656

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2