springboot+netty框架实现PLC通信的那点坑

    今天分享一下用netty框架实现与PLC实现数据通讯。期间碰到了一些坑,留作自己的笔迹,同步分享。

    本案例是在spring boot框架 搭建了netty框架。与PLC对接最大的坑就是16进制的转换。PLC中的数据基本都是16进展形式,对于服务器端获取到数据后,需要将16进制的数据转换成字符格式。下面我描述一下整个读写过程。

    首先,获取PLC寄存器地址数据。要获取PLC寄存器地址,netty服务端需要发送读指令给PLC,PLC收到读指令同步返回相应寄存器地址值。PLC读指令格式如下:0103112E0032A12A。这个时候就涉及到16进制问题。在java代码中 0103112E0032A12A以字符串形式保存,但是PLC收到的需要16进制的同样的字符。同理,PLC返回的是16进制的数据,如果直接用字符串去接,那就变乱码了,无法很好的去根据数据计算最终的数字。

    目前采用的方式是在netty下自定义通信数据的加码、解码器。如下代码:

步骤一:Encoder加码器

public class Encoder extends MessageToMessageEncoder {
    private final Charset charset;
    public Encoder() {
         this(Charset.defaultCharset());
    }
    public Encoder(Charset charset) {
        if(charset == null) {
                throw new NullPointerException("charset");
        } else {
           this.charset = charset;
        }

    }

    @Override

   protected void encode(ChannelHandlerContext channelHandlerContext, CharSequence charSequence, List     list) throws Exception {
         if(charSequence.length() != 0) {
             list.add(unreleasableBuffer(directBuffer(charSequence.length()).writeBytes(toBytes(charSequence.toString()))));
        }

    }

    public static byte[] toBytes(String str) {
        if(str == null || str.trim().equals("")) {
            return new byte[0];
        }
        byte[] bytes = new byte[str.length() / 2];
        for(int i = 0; i < str.length() / 2; i++) {
            String subStr = str.substring(i * 2, i * 2 + 2);
            bytes[i] = (byte) Integer.parseInt(subStr, 16);
        }
        return bytes;
    }

}

步骤二:Decode解码器:

public class Decoder extends MessageToMessageDecoder {
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception {
        byte[] req = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(req);
        list.add(bytesToHexFun1(req));

      }

public static String bytesToHexFun1(byte[] bytes) {

// 一个byte为8位,可用两个十六进制位标识

  char[] buf = new char[bytes.length * 2];
  int a = 0;
  int index = 0;
  for(byte b : bytes) { // 使用除与取余进行转换
       if(b < 0) {
            a = 256 + b;
       } else {
            a = b;
   }
   buf[index++] = HEX_CHAR[a / 16];
   buf[index++] = HEX_CHAR[a % 16];
 }
 return new String(buf);

}

步骤三:在netty服务启动时,加载encode和decode

new Thread(new Runnable() {
@Override

 public void run() {

 logger.info("Netty server starting ......");

 //final ByteBuf delimiter = Unpooled.copiedBuffer("FFFFFFFF".getBytes());
 final ByteBuf delimiter = Unpooled.copiedBuffer(toBytes("ffffffff"));//将fffffff转换成16进制的
 bossGroup = new NioEventLoopGroup();
 workerGroup = new NioEventLoopGroup(workerCore);
 ServerBootstrap server = new ServerBootstrap();
 server.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, backlog)
.childOption(ChannelOption.SO_KEEPALIVE,true).childOption(ChannelOption.TCP_NODELAY, true)
                .childHandler(new ChannelInitializer() {
 @Override
 protected void initChannel(SocketChannel ch) throws Exception {
 ChannelPipeline line = ch.pipeline();
 // TODO 增加粘包、分包解码器(使用netty自带的)
 //line.addLast("lineDecoder", new LineBasedFrameDecoder(1024));
 line.addLast("framer", new DelimiterBasedFrameDecoder(2048,delimiter));
 // TODO 换成自己的编解码器
 line.addLast("decoder", new Decoder());
 line.addLast("encoder", new Encoder());
                 //line.addLast("decoder", new StringDecoder(Charset.forName("utf-8")));
 //line.addLast("encoder", new StringEncoder(Charset.forName("utf-8")));
 // TODO 换成自己的处理器
 line.addLast("handler", new ServerHandler());
}
});

        try {
channel = server.bind(host, port).sync().channel();
} catch (InterruptedException e) {
e.printStackTrace();
}


logger.info("Netty server started on port " + port + " ......");
}
}).start();

    

你可能感兴趣的:(netty)