2. Netty使用JSerialComm进行串口读取(二)
前面提到了有两个问题:
第一个问题:串口读取超时异常一路传播到最后也没有处理。
第二个问题:即使处理了该异常,程序仍然退出,这不能满足一些实际使用要求需求。该问题与设置很长的读取超时时间没关系,无论多长到时间后总会退出。
现在开始这些问题的处理。
2.1.问题1:串口读取超时异常一路传播到最后也没有处理
先解决较容易处理的问题。
测试程序中使用如下语句设置读取超时时间:
config.setReadTimeout(10000);
即使设置读取超时时间长一点,如10秒,但如果十秒后没数据,也会出现超时异常,并提示
警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
com.fazecast.jSerialComm**.SerialPortTimeoutException**: The read operation timed out before any data was returned.
这个提示得意思是:异常一路传播到最后也没有处理,这通常意味着管道中的最后一个处理程序没有处理异常。
问题解决方法:
定义一个异常处理类ExceptionCaughtHandler,放在pipeline的最后用来处理各类异常。
package io.netty.example.jserialcomm;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.jsc.JSerialCommReadTimeoutException;
import com.fazecast.jSerialComm.SerialPortTimeoutException;
/**
* 定义一个异常处理类,放在pipeline的最后,处理各类异常
*/
public class ExceptionCaughtHandler extends ChannelInboundHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//System.out.println("Test Exception");
if (cause instanceof SerialPortTimeoutException) {
System.out.println("串口读取超时");
//超时后,定义业务需要的处理,例如可重新读取数据
}
else {
super.exceptionCaught(ctx, cause);
}
}
}
把异常处理器添加到pipeline最后面。
pipeline.addLast(new ExceptionCaughtHandler());
测试运行结果:
成功监测到该异常。
2.2. 问题2:即使处理了该异常,程序仍然退出
问题现象
从测试结果看,即使处理了该异常,程序仍然退出,这不能满足一些实际使用要求需求。
例如希望有数据时能正常读取数据,没有数据时给出读取超时提示并继续等待数据到来,这样就可以把串口的状态提示给用户或记录到日志系统。
同时该问题与设置很长的读取超时时间没关系,无论多长到时间后总会退出。
问题分析
直接查看Netty源代码并调试代码,发现JSerialCommChannel类是继承得 OioByteStreamChannel类。程序运行到OioByteStreamChannel类doReadBytes函数的buf.writeBytes语句行时,如果串口没有数据可读取,将抛出com.fazecast.jSerialComm.SerialPortTimeoutException异常导致程序退出。
@Override
protected int doReadBytes(ByteBuf buf) throws Exception {
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.attemptedBytesRead(Math.max(1, Math.min(available(), buf.maxWritableBytes())));
return buf.writeBytes(is, allocHandle.attemptedBytesRead());
}
解决方法
在JSerialCommChannel类中重载doReadBytes函数,并处理异常,抛出自定义异常JSerialCommReadTimeoutException在串口应用测试程序中处理该异常。
在重载代码中很方便查看抛出的具体是什么异常?(SerialPortTimeoutException)
com.fazecast.jSerialComm.SerialPortTimeoutException: The read operation timed out before any data was returned.
在netty-transport-jserialcomm自定义异常JSerialCommReadTimeoutException类。
package io.netty.channel.jsc;
public class JSerialCommReadTimeoutException extends RuntimeException{
public JSerialCommReadTimeoutException(){
}
public JSerialCommReadTimeoutException(String msg){
super(msg);
}
}
在JSerialCommChannel类重载函数doReadBytes中抛出自定义异常JSerialCommReadTimeoutException。
try {
return buf.writeBytes(is, i);
}
catch (IOException io)
{
//串口在给定时间内没数据读取
//抛出自定义串口读取超时异常处理异常JSerialCommReadTimeoutException
throw new JSerialCommReadTimeoutException("The Serial read operation timed out before any data was returned");
}
然后在串口应用测试序中修改ExceptionCaughtHandler类的exceptionCaught函数。将SerialPortTimeoutException异常修改为为JSerialCommReadTimeoutException异常。
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//System.out.println("Test Exception");
//if (cause instanceof SerialPortTimeoutException) {
if (cause instanceof JSerialCommReadTimeoutException) {
System.out.println("串口读取超时");
//超时后,克定义业务需要的处理,例如可重新读取数据
}
else {
super.exceptionCaught(ctx, cause);
}
}
运行测试程序进行测试,测试结果如下图:
修改后的程序可以提示读取超时信息,同时也不退出程序,还可以正常接收串口数据,达到预期效果。
最终修改好的库与测试代码下载地址为:《Netty使用JSerialComm进行串口读取的更新库源代码及问题修正》
介绍如下:
Netty-Transport-jSerialComm升级版本(v2.0.0)
更新如下:
1)依赖的Netty从 4.1.13.Final版本升级到 4.1.82.Final版本
2)依赖的jSerialComm 从1.3.11版本升级到2.9.2版本
3)处理两个问题:
第一个问题是:串口读取超时异常一路传播到最后也没有处理。
第二个问题是:即使处理了该异常,测试程序仍然退出,这不能满足一些实际使用要求需求。该问题与设置很长的读取超时时间没关系,无论多长到时间后总会退出。
4)打包生成 netty-transport-jserialcomm-2.0.0.jar、netty-transport-jserialcomm-2.0.0-javadoc.jar、netty-transport-jserialcomm-2.0.0-sources.jar三个包