app/src/main/java/com/example/agvcontroller/protocol/AGV_02_DOWN.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/protocol/AgvPackage.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/protocol/CRCUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/protocol/IMessageBody.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/protocol/PacHeader.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/protocol/ProtocolEncoder.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
app/src/main/java/com/example/agvcontroller/protocol/ProtocolUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
app/src/main/java/com/example/agvcontroller/protocol/AGV_02_DOWN.java
New file @@ -0,0 +1,47 @@ package com.zy.acs.common.domain.protocol; import com.zy.acs.common.utils.Utils; import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.io.Serializable; /** * 动作命令包 * Created by vincent on 2023/3/21 */ @Data public class AGV_02_DOWN implements IMessageBody, Serializable { private static final long serialVersionUID = 1664188062202647371L; @Override public byte[] writeToBytes() { String serialNo = Utils.zeroFill(this.serialNo, 16); byte[] serialNoBytes = Utils.reverse(serialNo.getBytes()); byte cmdCodeByte = (byte) this.getCmdCode(); byte valByte = (byte) this.getVal(); byte[] cmdBodyBytes = commandBody.writeToBytes(); return Utils.merge(serialNoBytes, cmdCodeByte, valByte, cmdBodyBytes); } @Override public void readFromBytes(byte[] messageBodyBytes) { } // 流水号 - 16 private String serialNo; // 命令码 private int cmdCode; // 属性值 private int val; // 动作参数 private ICommandBody commandBody; } app/src/main/java/com/example/agvcontroller/protocol/AgvPackage.java
New file @@ -0,0 +1,157 @@ package com.zy.acs.gateway.domain; import com.core.common.Cools; import com.zy.acs.gateway.constant.AgvConstant; import com.zy.acs.gateway.constant.PacErrorType; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import java.util.Objects; /** * AGV报文模型 * Created by vincent on 2023/3/10 */ public class AgvPackage implements PackageSupport { /** * 源数据包缓冲区(引用) */ private ByteBuf sourceBuff; /** * 原始消息对应的16进制字符串 */ private String sourceHexStr; /** * 消息头部 */ private PacHeader header; /** * 消息体 */ private PacBody body; /** * 消息的校正码 */ private int validCode; /** * 是否为校验异常包 */ private boolean errorPac; /** * 校验异常类型 */ private PacErrorType pacErrorType; public static AgvPackage valueOfEmpty() { AgvPackage agvPackage = new AgvPackage(); agvPackage.setHeader(new PacHeader()) .setBody(new PacBody()) .setValidCode((short) 0) .setErrorPac(false); return agvPackage; } public String toLogString() { StringBuilder pacSb = new StringBuilder(); pacSb.append("AGV(").append(getHeader().getUniqueNo()).append(")") .append("上传") .append("[").append(getHeader().getProtocolType().getDes()).append("]消息.") .append("原始消息[").append(getSourceHexStr()).append("]"); return pacSb.toString(); } public ByteBuf getSourceBuff() { // 因为处理buffer时,总是希望拿到一个重置了readerIndex的buf if (Objects.nonNull(this.sourceBuff)) { this.sourceBuff.resetReaderIndex(); } return this.sourceBuff; } public AgvPackage setSourceBuff(ByteBuf sourceBuff) { this.sourceBuff = sourceBuff; if (Cools.isEmpty(this.sourceHexStr)) { this.sourceBuff.resetReaderIndex(); this.sourceHexStr = ByteBufUtil.hexDump(this.sourceBuff).toUpperCase(); this.sourceBuff.resetReaderIndex(); } return this; } public PacHeader getHeader() { return header; } public AgvPackage setHeader(PacHeader header) { this.header = header; return this; } public PacBody getBody() { return body; } public AgvPackage setBody(PacBody body) { this.body = body; return this; } public int getValidCode() { return validCode; } public AgvPackage setValidCode(int validCode) { this.validCode = validCode; return this; } public boolean isErrorPac() { return errorPac; } public AgvPackage setErrorPac(boolean errorPac) { this.errorPac = errorPac; return this; } public String getSourceHexStr() { return sourceHexStr; } public AgvPackage setSourceHexStr(String sourceHexStr) { this.sourceHexStr = sourceHexStr; return this; } public PacErrorType getPacErrorType() { return pacErrorType; } public AgvPackage setPacErrorType(PacErrorType pacErrorType) { this.pacErrorType = pacErrorType; return this; } public ByteBuf convert(ByteBuf byteBuf){ byteBuf.writeByte(this.getHeader().getStartSymbol()) .writeShort(this.getHeader().getContentLength()) .writeByte(this.getHeader().getProtocolType().getCode()) .writeBytes(this.getHeader().getUniqueNo().getBytes(AgvConstant.CHARSET_GBK)) .writeByte(this.getHeader().getEncryptType().getCode()) .writeBytes(this.getBody().getContent()) .writeByte(this.getValidCode()); // 计算并设置校验码 // this.setValidCode(ValidUtil.caculateValidByteFromBuff(byteBuf)); byteBuf.resetReaderIndex(); byteBuf.writerIndex(byteBuf.readableBytes() - 1).writeByte(this.getValidCode()); return byteBuf; } } app/src/main/java/com/example/agvcontroller/protocol/CRCUtils.java
New file @@ -0,0 +1,41 @@ package com.zy.acs.gateway.utils; /** * CRC16循环冗余校验码 * Created by vincent on 2023/3/13 */ public class CRCUtils { /** * 一个字节占 8位 */ private static final int BITS_OF_BYTE = 8; /** * 多项式 */ private static final int POLYNOMIAL = 0XA001; /** * CRC寄存器默认初始值 */ private static final int INITIAL_VALUE = 0XFFFF; /** * CRC16 编码 * @param bytes 编码内容 * @return 编码结果 */ public static int crc16(byte[] bytes) { int res = INITIAL_VALUE; for (byte data : bytes) { // 把byte转换成int后再计算 res = res ^ (data & 0XFF); for (int i = 0; i < BITS_OF_BYTE; i++) { res = (res & 0X0001) == 1 ? (res >> 1) ^ POLYNOMIAL : res >> 1; } } return res; } } app/src/main/java/com/example/agvcontroller/protocol/IMessageBody.java
New file @@ -0,0 +1,11 @@ package com.zy.acs.common.domain.protocol; import java.io.Serializable; public interface IMessageBody extends Serializable { byte[] writeToBytes(); void readFromBytes(byte[] messageBodyBytes); } app/src/main/java/com/example/agvcontroller/protocol/PacHeader.java
New file @@ -0,0 +1,98 @@ package com.zy.acs.gateway.domain; import com.zy.acs.gateway.constant.EncryType; import com.zy.acs.gateway.constant.ProtocolType; import lombok.Data; import org.apache.commons.lang.builder.ToStringBuilder; /** * 协议头 * Created by vincent on 2019-04-03 */ @Data public class PacHeader { /** * 起始符 * 固定为"0xEE || 0xAA" */ private byte startSymbol; /** * 数据单元长度 */ private int contentLength; /** * 唯一标识码 */ private String uniqueNo; /** * 时间戳 */ private int timestamp; /** * 命令标识 */ private ProtocolType protocolType; /** * 流水号 */ private String serialNum; /** * 数据单元加密方式 */ private EncryType encryptType; public PacHeader setStartSymbol(Byte startSymbol) { this.startSymbol = startSymbol; return this; } public PacHeader setProtocolType(ProtocolType protocolType) { this.protocolType = protocolType; return this; } public PacHeader setTimestamp(Integer timestamp) { this.timestamp = timestamp; return this; } public PacHeader setSerialNum(String serialNum) { this.serialNum = serialNum; return this; } public PacHeader setUniqueNo(String uniqueNo) { this.uniqueNo = uniqueNo; return this; } public PacHeader setEncryptType(EncryType encryptType) { this.encryptType = encryptType; return this; } public PacHeader setContentLength(int contentLength) { this.contentLength = contentLength; return this; } @Override public String toString() { return new ToStringBuilder(this) .append("startSymbol", startSymbol) .append("contentLength", contentLength) .append("uniqueNo", uniqueNo) .append("timestamp", timestamp) .append("protocolType", protocolType.getDes()) .append("serialNum", serialNum) .toString(); } } app/src/main/java/com/example/agvcontroller/protocol/ProtocolEncoder.java
New file @@ -0,0 +1,89 @@ package com.zy.acs.gateway.handler.coder; import com.core.common.RadixTools; import com.zy.acs.common.utils.Utils; import com.zy.acs.gateway.config.SystemProperties; import com.zy.acs.gateway.constant.PackagePart; import com.zy.acs.gateway.domain.AgvPackage; import com.zy.acs.gateway.utils.ValidUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 编码器 * 此解码器将会生成校验码 * 处理方式: 异或和 * Created by vincent on 2019-04-02 */ @Component @ChannelHandler.Sharable public class ProtocolEncoder extends MessageToByteEncoder<Object> { private static final Logger log = LoggerFactory.getLogger(ProtocolEncoder.class); @Autowired private SystemProperties systemProperties; @Override protected void encode(ChannelHandlerContext ctx, Object obj, ByteBuf out) { if (obj instanceof ByteBuf){ out.writeBytes((ByteBuf) obj); } else if (obj instanceof byte[]){ out.writeBytes((byte[]) obj); } else if (obj instanceof AgvPackage){ AgvPackage pac = (AgvPackage)obj; byte[] bodyBytes = pac.getBody().getMessageBody().writeToBytes(); // body String uniqueNo = pac.getHeader().getUniqueNo(); byte[] uniquenoBytes = RadixTools.intToBytes(Integer.parseInt(pac.getHeader().getUniqueNo())); // uniqueno int len = PackagePart.UNIQUENO.getLen() // len + PackagePart.TIMESTAMP.getLen() + PackagePart.COMMAND_MARK.getLen() + bodyBytes.length; out.writeByte(pac.getHeader().getStartSymbol()) // symbol .writeShortLE(len) .writeBytes(Utils.reverse(uniquenoBytes)) // uniqueno .writeIntLE((int) (System.currentTimeMillis() / 1000)) // timestamp .writeByte(pac.getHeader().getProtocolType().getCode()) // type .writeBytes(bodyBytes) // body .writeShort(pac.getValidCode()) // valid ; pac.setValidCode(ValidUtil.calculateValidByteFromBuff(out)); out.resetReaderIndex(); out.writerIndex(out.readableBytes() - 2); out.writeShortLE(pac.getValidCode()); if (systemProperties.isPrintPacLog()){ log.info("Agv [{}] 下行 [{}] >>> {}", uniqueNo, pac.getHeader().getProtocolType().getDes(), ByteBufUtil.hexDump(out).toUpperCase()); } } } // EE | 11 00 | 01 00 00 00 | 0C AB 12 64 | F0 | 01 00 | 01 01 | 00 00 00 00 | 4C F7 // # | len | uniqueno | timestamp | cmd | content | crc } app/src/main/java/com/example/agvcontroller/protocol/ProtocolUtils.java
New file @@ -0,0 +1,86 @@ package com.zy.acs.gateway.utils; import com.zy.acs.common.domain.AgvProtocol; import com.zy.acs.common.domain.protocol.IMessageBody; import com.zy.acs.gateway.constant.ProtocolPojoType; import com.zy.acs.gateway.constant.ProtocolType; import com.zy.acs.gateway.domain.AgvPackage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Created by vincent on 2019-04-03 */ public class ProtocolUtils { private static final Logger log = LoggerFactory.getLogger(ProtocolUtils.class); // 下行协议报文组装 public static AgvPackage installDownProtocol(AgvProtocol protocol){ ProtocolPojoType protocolPojo = ProtocolPojoType.query(protocol.getMessageBody().getClass()); assert protocolPojo != null; AgvPackage ackPackage = AgvPackage.valueOfEmpty(); ackPackage.getHeader() .setStartSymbol((byte)0xEE) .setContentLength(0) .setUniqueNo(protocol.getAgvNo()) .setTimestamp(protocol.getTimestamp()) .setProtocolType(protocolPojo.protocolType) ; ackPackage.getBody().setMessageBody(protocol.getMessageBody()); return ackPackage; } // 下行协议报文组装 public static AgvPackage installDownProtocol(String uniqueNo, ProtocolType protocolType){ AgvPackage ackPackage = AgvPackage.valueOfEmpty(); ackPackage.getHeader() .setStartSymbol((byte)0xEE) .setContentLength(0) .setUniqueNo(uniqueNo) .setTimestamp((int)System.currentTimeMillis()/1000) .setProtocolType(protocolType) ; ProtocolPojoType protocolPojo = ProtocolPojoType.query(protocolType); assert protocolPojo != null; IMessageBody iMessageBody = null; try { iMessageBody = protocolPojo.clazz.newInstance(); } catch (ReflectiveOperationException reflectiveOperationException) { log.error("MessageBodyHandler ReflectiveOperationException", reflectiveOperationException); log.error("java反射创建实例失败:{}", protocolPojo.clazz.getName()); return null; } ackPackage.getBody().setMessageBody(iMessageBody); return ackPackage; } }