netty可以传输报文,为了解决数据粘连,自己费力气写了缓存
private void addCmd(byte[] bytes, byte len1, byte len2, List list) {
log.debug("Jc5000Service Jc5000Decoder decode jcBuffer.limit memory ={}",jcBuffer.limit());
Jc5000Response jc5000Response = new Jc5000Response();
byte cmdCode = (byte) (bytes[CMD_POS] & 0xff);
if (log.isDebugEnabled()) {
log.debug("Jc5000Service Jc5000Decoder decode command is cmdCode=" + cmdCode + "," + Jc5000CommandEnum.getJc5000CommandEnumKeyByCode(cmdCode));
}
jc5000Response.setCmdCode(cmdCode);
jc5000Response.setCommand(Jc5000CommandEnum.getJc5000CommandEnumByCode(cmdCode));
jc5000Response.setLEN(new byte[]{len1, len2});
jc5000Response.setBCC(bytes[bytes.length - 2]);
if (log.isDebugEnabled()) {
log.debug("Jc5000Service Jc5000Decoder success parse addcmd {}", bytes2HexString(bytes));
}
jc5000Response.setDATA(bytes);
byte reponseFlag = bytes[SUCCESS_POS];
jc5000Response.setReponseFlag(reponseFlag);
if (reponseFlag == Constants.SUCCESS) {
jc5000Response.setIS_SUCCEEDED(true);
log.info("Jc5000Service Jc5000Decoder cmd execute success");
TvmRuntimeCoreDeviceStatusManager.getInstance().setJc5000ReaderStatus(DeviceStatusEnum.NORMAL.getCode());
TvmRuntimeCoreDeviceStatusManager.getInstance().setJc5000ReaderrCode(StringUtils.EMPTY);
} else if (reponseFlag == Constants.FAIL) {
byte[] errorCode = Arrays.copyOfRange(bytes, 5, bytes.length);
jc5000Response.setErrorcode(errorCode);
if (errorCode != null && errorCode.length >= 2) {
jc5000Response.setErrCode(String.format("%02dH", Integer.valueOf(errorCode[1])));
jc5000Response.setErrUniqueCode(bytes2HexString(new byte[]{errorCode[0],errorCode[1]}));
}
log.error("Jc5000Service Jc5000Decoder execute failure , {}",bytes2HexString(bytes));
}else{
log.error("Jc5000Service Jc5000Decoder execute ,other failure, result={}", reponseFlag);
}
list.add(jc5000Response);
}
/**
* 读取到缓存里所有数据
*/
public byte[] conver(ByteBuffer byteBuffer) {
byteBuffer.flip();
byteBuffer.rewind();
int len = byteBuffer.limit() - byteBuffer.position();
byte[] bytes = new byte[len];
if (byteBuffer.isReadOnly()) {
return null;
} else {
byteBuffer.get(bytes);
}
return bytes;
}
/**
* 处理一个数据包
*/
private int dealPacket(byte[] tmp, List list) {
if (tmp.length >= PACKET_LEN && (tmp[0] == START_DATA[0] && tmp[1] == START_DATA[1] && tmp[2] == START_DATA[2])
&& (tmp[3] == tmp[5] && tmp[4] == tmp[6]) && tmp.length > (HEAD_LEN + bytes2Int(tmp[3], tmp[4]) + 1)
&& tmp[HEAD_LEN + bytes2Int(tmp[3], tmp[4]) + 1] == START_DATA[0]) {
int i = bytes2Int(tmp[3], tmp[4]);
byte[] bytes = Arrays.copyOfRange(tmp, HEAD_LEN, HEAD_LEN + i);
if (ByteArrayUtils.sumBCC(bytes) == tmp[HEAD_LEN + i]) {
addCmd(bytes, tmp[3], tmp[4], list);
return HEAD_LEN + bytes2Int(tmp[3], tmp[4]) + 2;
}
}
return 0;
}
/**
* 处理缓存数据
*/
private void dealbuf(List list) {
byte[] tmp = conver(jcBuffer);
jcBuffer.compact();
if (tmp == null) {
return;
}
byte[] acks = Arrays.copyOfRange(tmp, 0, ACK_LEN);
if (tmp.length > ACK_LEN) {
int k = 0;
//剔除ack 长度大于ack,前面数据等于ack或nack 并且长度不等于长度校验
if ((Arrays.equals(ACK, acks) || Arrays.equals(NACK, acks)) && (acks[ACK_LEN - 1] != acks[3] || tmp[ACK_LEN] != acks[4])) {
k = ACK_LEN + dealPacket(Arrays.copyOfRange(tmp, ACK_LEN, tmp.length), list);
} else {
k = dealPacket(tmp, list);
}
if (k < tmp.length) {
jcBuffer.put(Arrays.copyOfRange(tmp, k, tmp.length));
}
return;
} else if (tmp.length == ACK_LEN && (Arrays.equals(ACK, acks) || Arrays.equals(NACK, acks))) {
log.info("Jc5000Service Jc5000Decoder decode ack tmp : {}", tmp);
//ack或nack 直接丢弃不用特别处理
return;
}
//不完整的数据放回缓冲区
jcBuffer.put(tmp);
}
后来跟底层沟通去掉了不含长度的应答帧ack,那就只有一种格式的信息帧了,改进下用netty的插件
LengthFieldBasedFrameDecoder,这下代码非常精简了
开启时增加开关是否启用这个插件
public Boolean enableFrameDecoder = false;
/**
* 创建连接channel 分两步执行
* 1.设置bootstrap 信息
* 2.创建channel
*
*/
@Override
public boolean init() {
ready = false;
setBootstrap();
return connect();
}
/**
* 设置bootstrap
* @return
*/
protected boolean connect() {
createSerialChannel();
return ready;
}
public void setBootstrap() {
if (bootstrap.config().group() == null) {
//标识不限制连接数
int maxChannels = 0;
//标识不限制连接数
EventLoopGroup group = new OioEventLoopGroup(maxChannels, new SerialDeviceThreadFactory(getModulePrefix()));
bootstrap.group(group);
}
if (bootstrap.config().channelFactory() == null) {
bootstrap.channel(PureJavaCommChannel.class)
.option(PureJavaCommChannelOption.BAUD_RATE, getSerialDeviceConfig().getBaudRate())
.option(PureJavaCommChannelOption.READ_TIMEOUT, 5)
.option(PureJavaCommChannelOption.WAIT_TIME, 5);
}
bootstrap.handler(new ChannelInitializer<PureJavaCommChannel>() {
@Override
public void initChannel(PureJavaCommChannel ch) throws Exception {
try {
ChannelPipeline channelPipeline = ch.pipeline();
//channelPipeline.addLast("logging", new LoggingHandler(LogLevel.DEBUG))
if(enableFrameDecoder) {
channelPipeline.addFirst(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 5, 2, 2, 5));
}
channelPipeline.addLast(getDecoder());
ChannelHandler channelHandler = getExtendChannelHandler();
if (channelHandler != null) {
channelPipeline.addLast(channelHandler);
}
channelPipeline.addLast(getEncoder());
channelPipeline.addLast(getHandler());
} catch (Exception e) {
ready = false;
log.error("Init Channel failed.", e);
}
}
});
}
/**
* 创建串口创建方法
*/
public boolean createSerialChannel() {
try {
//用于修复init失败的问题
//java.lang.IllegalStateException: group not set
if (bootstrap.config().group() == null) {
this.setBootstrap();
}
if (channel == null || !channel.isActive()) {
ChannelFuture f = bootstrap.connect(new PureJavaCommDeviceAddress(getSerialDeviceConfig().getCom()));
channel = f.channel();
f.addListener((ChannelFutureListener) future -> {
if (!future.isSuccess()) {
future.channel().close();
ready = false;
channel = null;
log.error("Serial port {} open failed.", getSerialDeviceConfig().getCom());
} else {
ready = true;
channel = future.channel();
log.debug("Serial port {} open success.", getSerialDeviceConfig().getCom());
}
}).sync();
}
if (channel != null && channel.isActive()) {
ready = true;
} else {
ready = false;
}
} catch (Exception e) {
ready = false;
log.error("Connect serial port failed.", e);
}
return ready;
}
/**
* @param key
* @param message
* @return
*/
public synchronized SyncFuture<Object> send(String key, Object message) {
SyncFuture<Object> future = new SyncFuture<>(key);
if (SyncFutureMap.get(key) != null) {
log.error("SyncFuture send key={} value is not null ,this.getClass()={}", key, this.getClass());
return null;
}
if (channel == null) {
log.error("SyncFuture send key={} ,this.getClass()={} channel is null ", key, this.getClass());
return null;
}
SyncFutureMap.put(key, future);
channel.writeAndFlush(message);
return future;
}
@Override
public void close() {
try {
if (channel != null) {
channel.close();
}
//检查线程池是否关闭,如果没有关闭则关闭
EventLoopGroup eventLoopGroup = bootstrap.group();
if (!eventLoopGroup.isShutdown()) {
eventLoopGroup.shutdownGracefully();
}
} catch (Exception ex) {
}
}
/**
* 用于特殊情况扩展串口Handler
*/
protected ChannelHandler getExtendChannelHandler() {
return null;
}
解码时就可以直接解码
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception {
int length = byteBuf.readableBytes();
if (length > 0) {
byte[] bytesTmp = new byte[length];
byteBuf.readBytes(bytesTmp);
log.debug("jc5000 decode bytesTmp : {}", bytes2HexString(bytesTmp));
byte[] bytes = Arrays.copyOfRange(bytesTmp, 2, bytesTmp.length-2);
addCmd( bytes, bytesTmp[0], bytesTmp[1], list);
// jcBuffer.put(bytesTmp);
// dealbuf(list);
}
}
private void addCmd(byte[] bytes, byte len1, byte len2, List list) {
log.debug("Jc5000Service Jc5000Decoder decode jcBuffer.limit memory ={}",jcBuffer.limit());
Jc5000Response jc5000Response = new Jc5000Response();
byte cmdCode = (byte) (bytes[CMD_POS] & 0xff);
if (log.isDebugEnabled()) {
log.debug("Jc5000Service Jc5000Decoder decode command is cmdCode=" + cmdCode + "," + Jc5000CommandEnum.getJc5000CommandEnumKeyByCode(cmdCode));
}
jc5000Response.setCmdCode(cmdCode);
jc5000Response.setCommand(Jc5000CommandEnum.getJc5000CommandEnumByCode(cmdCode));
jc5000Response.setLEN(new byte[]{len1, len2});
jc5000Response.setBCC(bytes[bytes.length - 2]);
if (log.isDebugEnabled()) {
log.debug("Jc5000Service Jc5000Decoder success parse addcmd {}", bytes2HexString(bytes));
}
jc5000Response.setDATA(bytes);
byte reponseFlag = bytes[SUCCESS_POS];
jc5000Response.setReponseFlag(reponseFlag);
if (reponseFlag == Constants.SUCCESS) {
jc5000Response.setIS_SUCCEEDED(true);
log.info("Jc5000Service Jc5000Decoder cmd execute success");
} else if (reponseFlag == Constants.FAIL) {
byte[] errorCode = Arrays.copyOfRange(bytes, 5, bytes.length);
jc5000Response.setErrorcode(errorCode);
if (errorCode != null && errorCode.length >= 2) {
jc5000Response.setErrCode(String.format("%02dH", Integer.valueOf(errorCode[1])));
jc5000Response.setErrUniqueCode(bytes2HexString(new byte[]{errorCode[0],errorCode[1]}));
}
log.error("Jc5000Service Jc5000Decoder execute failure , {}",bytes2HexString(bytes));
}else{
log.error("Jc5000Service Jc5000Decoder execute ,other failure, result={}", reponseFlag);
}
list.add(jc5000Response);
}