今天分享一下用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<CharSequence> {
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<Object> 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<ByteBuf> {
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<Object> 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<SocketChannel>() {
@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框架与PLC设备进行数据通信,并详细解析16进制数据转换的方法。通过自定义编码器和解码器解决数据格式问题。
2334

被折叠的 条评论
为什么被折叠?



