本文主要是介绍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解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!