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

相关文章

SpringBoot中四种AOP实战应用场景及代码实现

《SpringBoot中四种AOP实战应用场景及代码实现》面向切面编程(AOP)是Spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护,在SpringBoot应用中,AO... 目录引言场景一:日志记录与性能监控业务需求实现方案使用示例扩展:MDC实现请求跟踪场景二:权限控制与

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

Java注解之超越Javadoc的元数据利器详解

《Java注解之超越Javadoc的元数据利器详解》本文将深入探讨Java注解的定义、类型、内置注解、自定义注解、保留策略、实际应用场景及最佳实践,无论是初学者还是资深开发者,都能通过本文了解如何利用... 目录什么是注解?注解的类型内置注编程解自定义注解注解的保留策略实际用例最佳实践总结在 Java 编程

Java 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows