From 34ad0023c881c97297fa3dfbf0da70a9938214a2 Mon Sep 17 00:00:00 2001 From: Guoqs <123@jsowell.com> Date: Wed, 27 Nov 2024 08:44:34 +0800 Subject: [PATCH 1/7] =?UTF-8?q?update=20=E8=8E=B7=E5=8F=96=E5=AD=97?= =?UTF-8?q?=E8=8A=82=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jsowell/common/core/domain/ykc/YKCDataProtocol.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jsowell-common/src/main/java/com/jsowell/common/core/domain/ykc/YKCDataProtocol.java b/jsowell-common/src/main/java/com/jsowell/common/core/domain/ykc/YKCDataProtocol.java index 67de4b837..861c2c229 100644 --- a/jsowell-common/src/main/java/com/jsowell/common/core/domain/ykc/YKCDataProtocol.java +++ b/jsowell-common/src/main/java/com/jsowell/common/core/domain/ykc/YKCDataProtocol.java @@ -61,13 +61,20 @@ public class YKCDataProtocol { this.crcByte = new byte[]{msg[msg.length - 2], msg[msg.length - 1]}; } + /** + * 获取字节数组 + */ + public byte[] getBytes() { + return Bytes.concat(this.head, this.length, this.serialNumber, this.encryptFlag, this.frameType, this.msgBody, this.crcByte); + } + /** * 转换为十六进制字符串 * * @return 报文 */ public String getHEXString() { - byte[] bytes = Bytes.concat(this.head, this.length, this.serialNumber, this.encryptFlag, this.frameType, this.msgBody, this.crcByte); + byte[] bytes = getBytes(); return BytesUtil.binary(bytes, 16); } } From 88b8339e22959649aeacab046aeb4073051c956b Mon Sep 17 00:00:00 2001 From: Guoqs <123@jsowell.com> Date: Wed, 27 Nov 2024 08:49:47 +0800 Subject: [PATCH 2/7] =?UTF-8?q?0x03=E5=B8=A7=EF=BC=8C=E5=88=99=E4=B8=8D?= =?UTF-8?q?=E8=BF=9B=E8=A1=8Ccrc=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/jsowell/common/util/YKCUtils.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java b/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java index ff345ecb4..529010614 100644 --- a/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java +++ b/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java @@ -52,6 +52,13 @@ public class YKCUtils { log.error("起始位必须是0x68"); return false; } + + // 如果是0x03帧,则不进行crc校验 + if (0x03 == frameType[0]) { + log.info("0x03帧,则不进行crc校验"); + return true; + } + // 序列号域+加密标志+帧类型标志+消息体 byte[] data = Bytes.concat(serialNumber, encryptFlag, frameType, msgBody); // 校验长度 From 9b95a07d8b038a775dc9c673a0b53251ae63fd8f Mon Sep 17 00:00:00 2001 From: Guoqs <123@jsowell.com> Date: Wed, 27 Nov 2024 09:19:41 +0800 Subject: [PATCH 3/7] =?UTF-8?q?update=20decoder=E6=94=B9=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jsowell/netty/decoder/YouDianDecoder.java | 138 ++++++++++++++++++ .../netty/decoder/YunKuaiChongDecoder.java | 138 ++++++++++++++++++ ...ctricBicyclesServerChannelInitializer.java | 4 +- .../NettyServerChannelInitializer.java | 4 +- 4 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianDecoder.java create mode 100644 jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianDecoder.java b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianDecoder.java new file mode 100644 index 000000000..4bea0677d --- /dev/null +++ b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianDecoder.java @@ -0,0 +1,138 @@ +package com.jsowell.netty.decoder; + +import com.jsowell.common.constant.Constants; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +@Slf4j +public class YouDianDecoder extends ByteToMessageDecoder { + private static final int HEADER_LENGTH_DNY = 3; // "DNY" 包头的长度 + private static final int HEADER_LENGTH_68 = 1; // 68 包头的长度 + + // 构造函数,初始化起始标志 + public YouDianDecoder() {} + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { + // log.info("StartAndLengthFieldFrameDecoder.decode"); + // 记录包头开始的index + int beginReader; + + // 循环查找包头 + while (true) { + if (buffer.readableBytes() < Math.min(HEADER_LENGTH_DNY, HEADER_LENGTH_68)) { + return; // 数据长度不足,等待更多数据 + } + + // 获取包头开始的index + beginReader = buffer.readerIndex(); + buffer.markReaderIndex(); + + // 判断是否为DNY包头或68包头 + if (isStartOfDnyHeader(buffer, beginReader) || isStartOf68Header(buffer, beginReader)) { + break; // 读到了协议的开始标志,结束while循环 + } + + // 未读到包头,略过一个字节 + buffer.resetReaderIndex(); + buffer.readByte(); + } + + // 检查包头是否是 "DNY" + if (buffer.readableBytes() >= HEADER_LENGTH_DNY) { + byte[] headerBytes = new byte[HEADER_LENGTH_DNY]; + buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY); + String header = new String(headerBytes, StandardCharsets.UTF_8); + // log.info("检查包头是否是DNY, header:{}", header); + if (Constants.EBIKE_HEADER.equals(header)) { + // 处理 DNY 协议 + decodeDnyMessage(buffer, out, beginReader); + return; + } + } + + // 检查包头是否是 68 协议 + if (buffer.readableBytes() >= HEADER_LENGTH_68) { + if (buffer.getUnsignedByte(beginReader) == 0x68) { + // 处理 68 协议 + decode68Message(buffer, out, beginReader); + return; + } + } + + // 未知协议,还原读指针 + buffer.resetReaderIndex(); + } + + // 判断是否为DNY包头 + private boolean isStartOfDnyHeader(ByteBuf buffer, int beginReader) { + if (buffer.readableBytes() >= HEADER_LENGTH_DNY) { + byte[] headerBytes = new byte[HEADER_LENGTH_DNY]; + buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY); + String header = new String(headerBytes, StandardCharsets.UTF_8); + return Constants.EBIKE_HEADER.equals(header); + } + return false; + } + + // 判断是否为68包头 + private boolean isStartOf68Header(ByteBuf buffer, int beginReader) { + if (buffer.readableBytes() >= HEADER_LENGTH_68) { + return buffer.getUnsignedByte(beginReader) == 0x68; + } + return false; + } + + // 处理68协议消息 + private void decode68Message(ByteBuf buffer, List out, int beginReader) { + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_68 + 1 + 2) { + buffer.readerIndex(beginReader); + return; + } + + // 获取消息长度 + int length = buffer.getUnsignedByte(beginReader + HEADER_LENGTH_68); + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_68 + 1 + length + 2) { + buffer.readerIndex(beginReader); + return; + } + + // 读取 data 数据 最后+2是帧校验域长度 + ByteBuf frame = buffer.retainedSlice(beginReader, HEADER_LENGTH_68 + 1 + length + 2); + buffer.readerIndex(beginReader + HEADER_LENGTH_68 + 1 + length + 2); + out.add(frame); + } + + // 处理DNY协议消息 + private void decodeDnyMessage(ByteBuf buffer, List out, int beginReader) { + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_DNY + 1) { + buffer.readerIndex(beginReader); + return; + } + + // 获取消息长度 + int length = buffer.getUnsignedByte(beginReader + HEADER_LENGTH_DNY); + // log.info("获取消息长度, length:{}", length); + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_DNY + 1 + length) { + buffer.readerIndex(beginReader); + return; + } + + // 读取 data 数据 + ByteBuf frame = buffer.retainedSlice(beginReader, HEADER_LENGTH_DNY + length + 2); + buffer.readerIndex(beginReader + HEADER_LENGTH_DNY + length + 2); + + + out.add(frame); + } + +} diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java new file mode 100644 index 000000000..cf74d6e7b --- /dev/null +++ b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java @@ -0,0 +1,138 @@ +package com.jsowell.netty.decoder; + +import com.jsowell.common.constant.Constants; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +@Slf4j +public class YunKuaiChongDecoder extends ByteToMessageDecoder { + private static final int HEADER_LENGTH_DNY = 3; // "DNY" 包头的长度 + private static final int HEADER_LENGTH_68 = 1; // 68 包头的长度 + + // 构造函数,初始化起始标志 + public YunKuaiChongDecoder() {} + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { + // log.info("StartAndLengthFieldFrameDecoder.decode"); + // 记录包头开始的index + int beginReader; + + // 循环查找包头 + while (true) { + if (buffer.readableBytes() < Math.min(HEADER_LENGTH_DNY, HEADER_LENGTH_68)) { + return; // 数据长度不足,等待更多数据 + } + + // 获取包头开始的index + beginReader = buffer.readerIndex(); + buffer.markReaderIndex(); + + // 判断是否为DNY包头或68包头 + if (isStartOfDnyHeader(buffer, beginReader) || isStartOf68Header(buffer, beginReader)) { + break; // 读到了协议的开始标志,结束while循环 + } + + // 未读到包头,略过一个字节 + buffer.resetReaderIndex(); + buffer.readByte(); + } + + // 检查包头是否是 "DNY" + if (buffer.readableBytes() >= HEADER_LENGTH_DNY) { + byte[] headerBytes = new byte[HEADER_LENGTH_DNY]; + buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY); + String header = new String(headerBytes, StandardCharsets.UTF_8); + // log.info("检查包头是否是DNY, header:{}", header); + if (Constants.EBIKE_HEADER.equals(header)) { + // 处理 DNY 协议 + decodeDnyMessage(buffer, out, beginReader); + return; + } + } + + // 检查包头是否是 68 协议 + if (buffer.readableBytes() >= HEADER_LENGTH_68) { + if (buffer.getUnsignedByte(beginReader) == 0x68) { + // 处理 68 协议 + decode68Message(buffer, out, beginReader); + return; + } + } + + // 未知协议,还原读指针 + buffer.resetReaderIndex(); + } + + // 判断是否为DNY包头 + private boolean isStartOfDnyHeader(ByteBuf buffer, int beginReader) { + if (buffer.readableBytes() >= HEADER_LENGTH_DNY) { + byte[] headerBytes = new byte[HEADER_LENGTH_DNY]; + buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY); + String header = new String(headerBytes, StandardCharsets.UTF_8); + return Constants.EBIKE_HEADER.equals(header); + } + return false; + } + + // 判断是否为68包头 + private boolean isStartOf68Header(ByteBuf buffer, int beginReader) { + if (buffer.readableBytes() >= HEADER_LENGTH_68) { + return buffer.getUnsignedByte(beginReader) == 0x68; + } + return false; + } + + // 处理68协议消息 + private void decode68Message(ByteBuf buffer, List out, int beginReader) { + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_68 + 1 + 2) { + buffer.readerIndex(beginReader); + return; + } + + // 获取消息长度 + int length = buffer.getUnsignedByte(beginReader + HEADER_LENGTH_68); + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_68 + 1 + length + 2) { + buffer.readerIndex(beginReader); + return; + } + + // 读取 data 数据 最后+2是帧校验域长度 + ByteBuf frame = buffer.retainedSlice(beginReader, HEADER_LENGTH_68 + 1 + length + 2); + buffer.readerIndex(beginReader + HEADER_LENGTH_68 + 1 + length + 2); + out.add(frame); + } + + // 处理DNY协议消息 + private void decodeDnyMessage(ByteBuf buffer, List out, int beginReader) { + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_DNY + 1) { + buffer.readerIndex(beginReader); + return; + } + + // 获取消息长度 + int length = buffer.getUnsignedByte(beginReader + HEADER_LENGTH_DNY); + // log.info("获取消息长度, length:{}", length); + // 检查剩余数据是否足够 + if (buffer.readableBytes() < HEADER_LENGTH_DNY + 1 + length) { + buffer.readerIndex(beginReader); + return; + } + + // 读取 data 数据 + ByteBuf frame = buffer.retainedSlice(beginReader, HEADER_LENGTH_DNY + length + 2); + buffer.readerIndex(beginReader + HEADER_LENGTH_DNY + length + 2); + + + out.add(frame); + } + +} diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java b/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java index 4e4cb6edc..c58d70bd5 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java @@ -1,6 +1,6 @@ package com.jsowell.netty.server.electricbicycles; -import com.jsowell.netty.decoder.StartAndLengthFieldFrameDecoder; +import com.jsowell.netty.decoder.YouDianDecoder; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; @@ -20,7 +20,7 @@ public class ElectricBicyclesServerChannelInitializer extends ChannelInitializer @Override protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast("frameDecoder", new StartAndLengthFieldFrameDecoder()); + pipeline.addLast("frameDecoder", new YouDianDecoder()); // pipeline.addLast("decoder", new MessageDecode()); // pipeline.addLast("encoder", new MessageEncode()); pipeline.addLast("decoder", new ByteArrayDecoder()); diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java index b343fde1b..c2237067a 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java @@ -1,6 +1,6 @@ package com.jsowell.netty.server.yunkuaichong; -import com.jsowell.netty.decoder.StartAndLengthFieldFrameDecoder; +import com.jsowell.netty.decoder.YunKuaiChongDecoder; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; @@ -21,7 +21,7 @@ public class NettyServerChannelInitializer extends ChannelInitializer Date: Wed, 27 Nov 2024 14:32:51 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E8=A7=A3=E7=A0=81=E5=99=A8=E6=8B=86?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jsowell/common/util/CRC16Util.java | 14 +++ .../netty/decoder/YkcProtocolDecoder.java | 105 ++++++++++++++++++ ...coder.java => YouDianProtocolDecoder.java} | 7 +- .../netty/decoder/YunKuaiChongDecoder.java | 8 +- ...ctricBicyclesServerChannelInitializer.java | 4 +- .../NettyServerChannelInitializer.java | 4 +- .../yunkuaichong/NettyServerHandler.java | 5 +- .../impl/YKCBusinessServiceImpl.java | 8 +- 8 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 jsowell-netty/src/main/java/com/jsowell/netty/decoder/YkcProtocolDecoder.java rename jsowell-netty/src/main/java/com/jsowell/netty/decoder/{YouDianDecoder.java => YouDianProtocolDecoder.java} (96%) diff --git a/jsowell-common/src/main/java/com/jsowell/common/util/CRC16Util.java b/jsowell-common/src/main/java/com/jsowell/common/util/CRC16Util.java index eceb4753f..b2ef04ec0 100644 --- a/jsowell-common/src/main/java/com/jsowell/common/util/CRC16Util.java +++ b/jsowell-common/src/main/java/com/jsowell/common/util/CRC16Util.java @@ -186,6 +186,20 @@ public class CRC16Util { /*以下方法得出的校验位:低位在前,高位在后*/ + public static short calculateCrc(short serialNumber, byte encryptFlag, byte frameType, byte[] messageBody) { + // short serialNumber 转byte[] + byte[] serialNumberBytes = new byte[2]; + // byte encryptFlag 转byte[] + byte[] encryptFlagBytes = new byte[1]; + // byte frameType 转byte[] + byte[] frameTypeBytes = new byte[1]; + // byte[] messageBody 转byte[] + // 序列号域+加密标志+帧类型标志+消息体 + byte[] data = Bytes.concat(serialNumberBytes, encryptFlagBytes, frameTypeBytes, messageBody); + int i = calcCrc16(data); + return (short) i; + } + /** * @param data 需要计算的数组 * @return CRC16校验值 diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YkcProtocolDecoder.java b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YkcProtocolDecoder.java new file mode 100644 index 000000000..b34136d2f --- /dev/null +++ b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YkcProtocolDecoder.java @@ -0,0 +1,105 @@ +package com.jsowell.netty.decoder; + +import com.jsowell.common.util.BytesUtil; +import com.jsowell.common.util.CRC16Util; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.DecoderException; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +public class YkcProtocolDecoder extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + log.info("YkcProtocolDecoder.decode"); + // 检查起始标志是否为0x68 + if (in.readableBytes() < 5) { + return; // 至少需要 起始标志 (1) + 数据长度 (1) + 序列号域 (2) + 帧校验域 (1) + } + + in.markReaderIndex(); // 标记当前读取位置 + byte startFlag = in.readByte(); + log.info("startFlag: {}", BytesUtil.binary(new byte[]{startFlag}, 16)); + if (startFlag != (byte) 0x68) { + throw new DecoderException("Invalid start flag: " + startFlag); + } + + // 读取数据长度 + byte dataLength = in.readByte(); + if (in.readableBytes() < dataLength + 2) { // 消息体 + 帧校验域长度 + in.resetReaderIndex(); + return; // 数据不足,等待更多字节 + } + + // 读取其他字段 + short serialNumber = in.readShort(); // 序列号域 + log.info("serialNumber: {}", BytesUtil.printHexBinary(new byte[]{(byte) serialNumber})); + byte encryptFlag = in.readByte(); // 加密标志 + log.info("encryptFlag: {}", BytesUtil.printHexBinary(new byte[]{encryptFlag})); + byte frameType = in.readByte(); // 帧类型标志 + log.info("frameType: {}", BytesUtil.printHexBinary(new byte[]{frameType})); + + // 读取消息体 + byte[] messageBody = new byte[dataLength - 4]; // 消息体长度 = 数据长度 - 固定字段长度 + log.info("messageBody: {}", BytesUtil.printHexBinary(messageBody)); + in.readBytes(messageBody); + + // 读取帧校验域 + short receivedCrc = in.readShort(); + + // 计算 CRC + short calculatedCrc = CRC16Util.calculateCrc(serialNumber, encryptFlag, frameType, messageBody); + if (calculatedCrc != receivedCrc) { + throw new DecoderException("CRC check failed. Expected: " + receivedCrc + ", Calculated: " + calculatedCrc); + } + + // 构造消息对象并传递给下一个处理器 + ProtocolMessage message = new ProtocolMessage(startFlag, dataLength, serialNumber, encryptFlag, frameType, messageBody, receivedCrc); + out.add(message); + } + + public static int bcdToDecimal(byte bcd1, byte bcd2) { + // BCD码转换为十进制 + int high = (bcd1 >> 4) & 0x0F; // 高4位 + int low = bcd1 & 0x0F; // 低4位 + int high2 = (bcd2 >> 4) & 0x0F; + int low2 = bcd2 & 0x0F; + + // 将BCD码拼接成十进制数 + return (high * 1000) + (low * 100) + (high2 * 10) + low2; + } + + // 自定义消息类 + public static class ProtocolMessage { + private final byte startFlag; + private final byte dataLength; + private final short serialNumber; + private final byte encryptFlag; + private final byte frameType; + private final byte[] messageBody; + private final short crc; + + public ProtocolMessage(byte startFlag, byte dataLength, short serialNumber, byte encryptFlag, byte frameType, byte[] messageBody, short crc) { + this.startFlag = startFlag; + this.dataLength = dataLength; + this.serialNumber = serialNumber; + this.encryptFlag = encryptFlag; + this.frameType = frameType; + this.messageBody = messageBody; + this.crc = crc; + } + + // Getter methods... + public byte getStartFlag() { return startFlag; } + public byte getDataLength() { return dataLength; } + public short getSerialNumber() { return serialNumber; } + public byte getEncryptFlag() { return encryptFlag; } + public byte getFrameType() { return frameType; } + public byte[] getMessageBody() { return messageBody; } + public short getCrc() { return crc; } + } +} diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianDecoder.java b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianProtocolDecoder.java similarity index 96% rename from jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianDecoder.java rename to jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianProtocolDecoder.java index 4bea0677d..c38ebef1f 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianDecoder.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YouDianProtocolDecoder.java @@ -9,13 +9,16 @@ import lombok.extern.slf4j.Slf4j; import java.nio.charset.StandardCharsets; import java.util.List; +/** + * 友电协议解码器 + */ @Slf4j -public class YouDianDecoder extends ByteToMessageDecoder { +public class YouDianProtocolDecoder extends ByteToMessageDecoder { private static final int HEADER_LENGTH_DNY = 3; // "DNY" 包头的长度 private static final int HEADER_LENGTH_68 = 1; // 68 包头的长度 // 构造函数,初始化起始标志 - public YouDianDecoder() {} + public YouDianProtocolDecoder() {} @Override protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java index cf74d6e7b..8841b1dd0 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/decoder/YunKuaiChongDecoder.java @@ -1,6 +1,7 @@ package com.jsowell.netty.decoder; import com.jsowell.common.constant.Constants; +import com.jsowell.common.core.domain.ykc.YKCDataProtocol; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; @@ -107,7 +108,12 @@ public class YunKuaiChongDecoder extends ByteToMessageDecoder { // 读取 data 数据 最后+2是帧校验域长度 ByteBuf frame = buffer.retainedSlice(beginReader, HEADER_LENGTH_68 + 1 + length + 2); buffer.readerIndex(beginReader + HEADER_LENGTH_68 + 1 + length + 2); - out.add(frame); + + // 转为YKCDataProtocol对象 + byte[] bytes = new byte[HEADER_LENGTH_68 + 1 + length + 2]; + frame.readBytes(bytes); + YKCDataProtocol ykcDataProtocol = new YKCDataProtocol(bytes); + out.add(ykcDataProtocol); } // 处理DNY协议消息 diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java b/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java index c58d70bd5..3b9ef4631 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/server/electricbicycles/ElectricBicyclesServerChannelInitializer.java @@ -1,6 +1,6 @@ package com.jsowell.netty.server.electricbicycles; -import com.jsowell.netty.decoder.YouDianDecoder; +import com.jsowell.netty.decoder.YouDianProtocolDecoder; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; @@ -20,7 +20,7 @@ public class ElectricBicyclesServerChannelInitializer extends ChannelInitializer @Override protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast("frameDecoder", new YouDianDecoder()); + pipeline.addLast("frameDecoder", new YouDianProtocolDecoder()); // pipeline.addLast("decoder", new MessageDecode()); // pipeline.addLast("encoder", new MessageEncode()); pipeline.addLast("decoder", new ByteArrayDecoder()); diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java index c2237067a..0d0184f16 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java @@ -20,8 +20,8 @@ public class NettyServerChannelInitializer extends ChannelInitializer Date: Wed, 27 Nov 2024 14:34:28 +0800 Subject: [PATCH 5/7] =?UTF-8?q?0x03=E5=B8=A7=EF=BC=8C=E5=88=99=E4=B8=8D?= =?UTF-8?q?=E8=BF=9B=E8=A1=8Ccrc=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/yunkuaichong/impl/YKCBusinessServiceImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java b/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java index 5b9d25d04..641edecae 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java @@ -37,10 +37,10 @@ public class YKCBusinessServiceImpl implements YKCBusinessService { @Override public byte[] process(byte[] msg, ChannelHandlerContext ctx) { - // if (!YKCUtils.checkMsg(msg)) { - // // 校验不通过,丢弃消息 - // return null; - // } + if (!YKCUtils.checkMsg(msg)) { + // 校验不通过,丢弃消息 + return null; + } YKCDataProtocol ykcDataProtocol = new YKCDataProtocol(msg); // 获取帧类型 String frameType = YKCUtils.frameType2Str(ykcDataProtocol.getFrameType()); From 9d61fa5dba4235d8ecfb31416e16013fc6284679 Mon Sep 17 00:00:00 2001 From: Guoqs <123@jsowell.com> Date: Wed, 27 Nov 2024 14:44:51 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E4=BD=BF=E7=94=A8YKCDataProtocol=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jsowell/common/util/YKCUtils.java | 54 ++++++++++++++ .../yunkuaichong/NettyServerHandler.java | 74 ++++++++++++++++--- .../yunkuaichong/YKCBusinessService.java | 4 +- .../impl/YKCBusinessServiceImpl.java | 14 ++++ 4 files changed, 136 insertions(+), 10 deletions(-) diff --git a/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java b/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java index 529010614..f96ee1580 100644 --- a/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java +++ b/jsowell-common/src/main/java/com/jsowell/common/util/YKCUtils.java @@ -84,6 +84,60 @@ public class YKCUtils { return false; } + public static boolean checkMsg(YKCDataProtocol ykcDataProtocol) { + // log.info("checkMsg:{}", BytesUtil.binary(msg, 16)); + // 起始标志 + byte[] head = ykcDataProtocol.getHead(); + // 数据长度 + byte[] length = ykcDataProtocol.getLength(); + // 序列号域 + byte[] serialNumber = ykcDataProtocol.getSerialNumber(); + // 加密标志 + byte[] encryptFlag = ykcDataProtocol.getEncryptFlag(); + // 帧类型标志 + byte[] frameType = ykcDataProtocol.getFrameType(); + // 消息体 + byte[] msgBody = ykcDataProtocol.getMsgBody(); + // 帧校验域 + byte[] crcByte = ykcDataProtocol.getCrcByte(); + + //起始位必须是0x68 + if (0x68 != head[0]) { + log.error("起始位必须是0x68"); + return false; + } + + // 如果是0x03帧,则不进行crc校验 + if (0x03 == frameType[0]) { + log.info("0x03帧,则不进行crc校验"); + return true; + } + + // 序列号域+加密标志+帧类型标志+消息体 + byte[] data = Bytes.concat(serialNumber, encryptFlag, frameType, msgBody); + // 校验长度 + if (data.length != BytesUtil.bytesToIntLittle(length)) { + log.error("数据长度不正确, 数据长度:{}, 实际长度:{}", BytesUtil.bytesToIntLittle(length), data.length); + return false; + } + // CRC校验 source target + String sourceCRC = String.format("%04x", BytesUtil.bytesToInt(crcByte, 0)); + String targetCRC = String.format("%04x", CRC16Util.calcCrc16(data)); + String oldTargetCRC = String.format("%04x", CRC16Util.calcCrc16Old(data)); + // 将高低位位置反转,得出新的crc + String lowString = StringUtils.substring(targetCRC, 0, 2); + String highString = StringUtils.substring(targetCRC, 2, 4); + String crc = highString + lowString; + // 若目标crc和高低位反转前/后的crc都不一致,则校验不通过 + if (sourceCRC.equalsIgnoreCase(targetCRC) || sourceCRC.equalsIgnoreCase(crc)) { + return true; + } + log.error("CRC校验不通过, 源crc:{}, 计算得出crc:{}, 老crc计算:{}, 高低位反转后crc:{}, 帧类型:{}, 帧名称:{}, 报文:{}" + , sourceCRC, targetCRC, oldTargetCRC, crc, YKCUtils.frameType2Str(frameType), + YKCFrameTypeCode.getFrameTypeStr(YKCUtils.frameType2Str(frameType)), BytesUtil.binary(ykcDataProtocol.getBytes(), 16)); + return false; + } + /** * 获取结果报文 * @param ykcDataProtocol diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerHandler.java b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerHandler.java index 10383c7ab..565f231ce 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerHandler.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerHandler.java @@ -67,16 +67,72 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** * 有客户端发消息会触发此函数 */ + // @Override + // public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception { + // // log.info("channelRead-aClass:{}", message.getClass()); + // // log.info("加载客户端报文channelRead=== channelId:" + ctx.channel().id() + ", msg:" + message); + // // 下面可以解析数据,保存数据,生成返回报文,将需要返回报文写入write函数 + // YKCDataProtocol ykcDataProtocol = (YKCDataProtocol) message; + // byte[] msg = ykcDataProtocol.getBytes(); + // + // // 获取帧类型 + // byte[] frameTypeBytes = BytesUtil.copyBytes(msg, 5, 1); + // String frameType = YKCUtils.frameType2Str(frameTypeBytes); + // + // // 判断该帧类型是否为某请求帧的应答帧 + // String requestFrameType = YKCFrameTypeCode.PileAnswersRelation.getRequestFrameType(frameType); + // // log.info("同步获取响应数据-判断该帧类型是否为某请求帧的应答帧, frameType:{}, requestFrameType:{}", frameType, requestFrameType); + // if (StringUtils.isNotBlank(requestFrameType)) { + // // 根据请求id,在集合中找到与外部线程通信的SyncPromise对象 + // String msgId = ctx.channel().id().toString() + "_" + requestFrameType; + // // log.info("同步获取响应数据-收到消息, msgId:{}", msgId); + // SyncPromise syncPromise = RpcUtil.getSyncPromiseMap().get(msgId); + // if(syncPromise != null) { + // // 设置响应结果 + // syncPromise.setRpcResult(msg); + // // 唤醒外部线程 + // // log.info("同步获取响应数据-唤醒外部线程, SyncPromise:{}", JSON.toJSONString(syncPromise)); + // syncPromise.wake(); + // } + // } + // + // // 获取序列号域 + // int serialNumber = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(msg, 2, 2)); + // + // // 获取channel + // Channel channel = ctx.channel(); + // + // // 心跳包0x03日志太多,造成日志文件过大,改为不打印 + // if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameType)) { + // log.info("【<<<<<平台收到消息<<<<<】channel:{}, 帧类型:{}, 帧名称:{}, 序列号域:{}, 报文:{}", + // channel.id(), frameType, YKCFrameTypeCode.getFrameTypeStr(frameType), serialNumber, + // BytesUtil.binary(msg, 16)); + // } + // + // // 处理数据 + // byte[] response = ykcService.process(msg, ctx); + // if (Objects.nonNull(response)) { + // // 响应客户端 + // ByteBuf buffer = ctx.alloc().buffer().writeBytes(response); + // this.channelWrite(channel.id(), buffer); + // if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameType)) { + // // 应答帧类型 + // byte[] responseFrameTypeBytes = YKCFrameTypeCode.PlatformAnswersRelation.getResponseFrameTypeBytes(frameTypeBytes); + // String responseFrameType = YKCUtils.frameType2Str(responseFrameTypeBytes); + // log.info("【>>>>>平台响应消息>>>>>】channel:{}, 响应帧类型:{}, 响应帧名称:{}, 原帧类型:{}, 原帧名称:{}, 序列号域:{}, response:{}", + // channel.id(), responseFrameType, YKCFrameTypeCode.getFrameTypeStr(responseFrameType), + // frameType, YKCFrameTypeCode.getFrameTypeStr(frameType), serialNumber, + // BytesUtil.binary(response, 16)); + // } + // } + // } + @Override public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception { - // log.info("channelRead-aClass:{}", message.getClass()); - // log.info("加载客户端报文channelRead=== channelId:" + ctx.channel().id() + ", msg:" + message); - // 下面可以解析数据,保存数据,生成返回报文,将需要返回报文写入write函数 YKCDataProtocol ykcDataProtocol = (YKCDataProtocol) message; - byte[] msg = ykcDataProtocol.getBytes(); // 获取帧类型 - byte[] frameTypeBytes = BytesUtil.copyBytes(msg, 5, 1); + byte[] frameTypeBytes = ykcDataProtocol.getFrameType(); String frameType = YKCUtils.frameType2Str(frameTypeBytes); // 判断该帧类型是否为某请求帧的应答帧 @@ -89,7 +145,7 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter { SyncPromise syncPromise = RpcUtil.getSyncPromiseMap().get(msgId); if(syncPromise != null) { // 设置响应结果 - syncPromise.setRpcResult(msg); + syncPromise.setRpcResult(ykcDataProtocol.getBytes()); // 唤醒外部线程 // log.info("同步获取响应数据-唤醒外部线程, SyncPromise:{}", JSON.toJSONString(syncPromise)); syncPromise.wake(); @@ -97,7 +153,7 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter { } // 获取序列号域 - int serialNumber = BytesUtil.bytesToIntLittle(BytesUtil.copyBytes(msg, 2, 2)); + int serialNumber = BytesUtil.bytesToIntLittle(ykcDataProtocol.getSerialNumber()); // 获取channel Channel channel = ctx.channel(); @@ -106,11 +162,11 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter { if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameType)) { log.info("【<<<<<平台收到消息<<<<<】channel:{}, 帧类型:{}, 帧名称:{}, 序列号域:{}, 报文:{}", channel.id(), frameType, YKCFrameTypeCode.getFrameTypeStr(frameType), serialNumber, - BytesUtil.binary(msg, 16)); + BytesUtil.binary(ykcDataProtocol.getBytes(), 16)); } // 处理数据 - byte[] response = ykcService.process(msg, ctx); + byte[] response = ykcService.process(ykcDataProtocol, ctx); if (Objects.nonNull(response)) { // 响应客户端 ByteBuf buffer = ctx.alloc().buffer().writeBytes(response); diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/YKCBusinessService.java b/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/YKCBusinessService.java index 102ac12fc..1bf85a02b 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/YKCBusinessService.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/YKCBusinessService.java @@ -1,6 +1,6 @@ package com.jsowell.netty.service.yunkuaichong; -import io.netty.channel.Channel; +import com.jsowell.common.core.domain.ykc.YKCDataProtocol; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelId; @@ -18,6 +18,8 @@ public interface YKCBusinessService { */ byte[] process(byte[] msg, ChannelHandlerContext ctx); + byte[] process(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext ctx); + /** * 桩退出 * @param channelId channelId diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java b/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java index 641edecae..5d088c382 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/service/yunkuaichong/impl/YKCBusinessServiceImpl.java @@ -50,6 +50,20 @@ public class YKCBusinessServiceImpl implements YKCBusinessService { return invokeStrategy.supplyProcess(ykcDataProtocol, ctx); } + @Override + public byte[] process(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext ctx) { + if (!YKCUtils.checkMsg(ykcDataProtocol)) { + // 校验不通过,丢弃消息 + return null; + } + // 获取帧类型 + String frameType = YKCUtils.frameType2Str(ykcDataProtocol.getFrameType()); + // 获取业务处理handler + AbstractYkcHandler invokeStrategy = YKCOperateFactory.getInvokeStrategy(frameType); + // AbstractYkcHandlerV2 invokeStrategy = ykcOperateFactoryV2.getInvokeStrategy(frameType); + return invokeStrategy.supplyProcess(ykcDataProtocol, ctx); + } + @Override public void exit(ChannelId channelId) { // 获取桩编号 From e861eec8680c60093a6a880c3f2ebc899c1a83ec Mon Sep 17 00:00:00 2001 From: Lemon Date: Wed, 27 Nov 2024 15:23:36 +0800 Subject: [PATCH 7/7] =?UTF-8?q?update=20=E7=94=98=E8=82=83=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/service/impl/GanSuPlatformServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/platform/service/impl/GanSuPlatformServiceImpl.java b/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/platform/service/impl/GanSuPlatformServiceImpl.java index 173295015..776cc5c73 100644 --- a/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/platform/service/impl/GanSuPlatformServiceImpl.java +++ b/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/platform/service/impl/GanSuPlatformServiceImpl.java @@ -186,8 +186,6 @@ public class GanSuPlatformServiceImpl implements ThirdPartyPlatformService { .stationLat(new BigDecimal(pileStationInfo.getStationLat())) .construction(Integer.parseInt(pileStationInfo.getConstruction())) .parkNums(Integer.parseInt(pileStationInfo.getParkNums())) - // todo .electricityFee() - // todo .serviceFee() .build(); JSONObject electricityFee = new JSONObject(); @@ -440,6 +438,7 @@ public class GanSuPlatformServiceImpl implements ThirdPartyPlatformService { if (StringUtils.isBlank(token)) { return null; } + // 调用联联平台接口 String jsonString = JSON.toJSONString(chargeOrderInfo); String result = HttpRequestUtil.sendPost(token, jsonString, url, dataSecret, dataSecretIv, operatorId, signSecret);