|  |  |  | 
|---|
|  |  |  | * @author vincent | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public class Struct implements java.io.Serializable { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private transient Map<String,byte[]> decodeByteMapping = new HashMap<>(); | 
|---|
|  |  |  | private transient Map<String,byte[]> encodeByteMapping = new HashMap<>(); | 
|---|
|  |  |  | // 解码前的原生字节数组 | 
|---|
|  |  |  | private transient byte[] dataBytes = null; | 
|---|
|  |  |  | private transient byte[] decodeBytes = null; | 
|---|
|  |  |  | private transient byte[] encodeBytes = null; | 
|---|
|  |  |  | private transient byte[] unusedBytes = null; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public Class getClassType(String name){ | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private transient Map<String, byte[]> decodeByteMapping = new HashMap<>(); | 
|---|
|  |  |  | private transient Map<String, byte[]> encodeByteMapping = new HashMap<>(); | 
|---|
|  |  |  | // 解码前的原生字节数组 | 
|---|
|  |  |  | private transient byte[] dataBytes = null; | 
|---|
|  |  |  | private transient byte[] decodeBytes = null; | 
|---|
|  |  |  | private transient byte[] encodeBytes = null; | 
|---|
|  |  |  | private transient byte[] unusedBytes = null; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 判断大小端 | 
|---|
|  |  |  | * @return true: 小端 / false: 大端 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public boolean isReverse() { | 
|---|
|  |  |  | return this.getClass().getAnnotation(little.class)!=null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | @SuppressWarnings({"unchecked", "rawtypes"}) | 
|---|
|  |  |  | public static <T extends Struct> T decode(Class<? extends Struct> cls, byte[] bytes) throws Exception { | 
|---|
|  |  |  | Struct instance = cls.newInstance(); | 
|---|
|  |  |  | return instance.decode(bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 获取字节数组 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public byte[] toBytes() throws Exception { | 
|---|
|  |  |  | return encode(this); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public Map<String,String> toDecodeHexMapping() throws Exception{ | 
|---|
|  |  |  | if(this.decodeByteMapping==null)this.decodeByteMapping=new HashMap<>(); | 
|---|
|  |  |  | Map<String, String> mapping = getHex(this.decodeByteMapping); | 
|---|
|  |  |  | if(this.decodeBytes!=null){ | 
|---|
|  |  |  | mapping.put("byte[]", Struct.toHex(this.decodeBytes)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return mapping; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public Map<String,String> toEncodeHexMapping() throws Exception{ | 
|---|
|  |  |  | if(this.encodeBytes==null)this.encode(this); | 
|---|
|  |  |  | if(this.encodeByteMapping==null)this.encodeByteMapping=new HashMap<>(); | 
|---|
|  |  |  | Map<String, String> mapping = getHex(this.encodeByteMapping); | 
|---|
|  |  |  | mapping.put("byte[]", Struct.toHex(this.encodeBytes)); | 
|---|
|  |  |  | return mapping; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public Map<String,String> getHex(Map<String,byte[]> bytemapping) throws Exception { | 
|---|
|  |  |  | Map<String,String> map = new LinkedHashMap<>(); | 
|---|
|  |  |  | Field[] fields = this.getClass().getFields(); | 
|---|
|  |  |  | for (Field field : fields) { | 
|---|
|  |  |  | byte[] bytes = bytemapping.get(field.getName()); | 
|---|
|  |  |  | if(bytes!=null){ | 
|---|
|  |  |  | map.put(field.getName(), Struct.toHex(bytes)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return map; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 转java对象到数组 | 
|---|
|  |  |  | public byte[] encode(Struct entity) throws Exception { | 
|---|
|  |  |  | ByteArrayOutputStream baOs = new ByteArrayOutputStream(); | 
|---|
|  |  |  | write(new DataOutputStream(baOs), entity); | 
|---|
|  |  |  | entity.encodeBytes = baOs.toByteArray(); | 
|---|
|  |  |  | return entity.encodeBytes; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 写数据到输出流 | 
|---|
|  |  |  | * | 
|---|
|  |  |  | * @param dos | 
|---|
|  |  |  | * @param entity | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public void write(DataOutputStream dos, Struct entity) throws Exception { | 
|---|
|  |  |  | if(entity==null)return; | 
|---|
|  |  |  | if(entity.encodeByteMapping==null)entity.encodeByteMapping=new HashMap<>(); | 
|---|
|  |  |  | Field[] fields = entity.getClass().getFields(); | 
|---|
|  |  |  | EvaluationContext context = new StandardEvaluationContext(); | 
|---|
|  |  |  | for (Field field : Objects.requireNonNull(ReflectUtils.removeStaticField(fields))) { | 
|---|
|  |  |  | field.setAccessible(true); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | String name = field.getName(); | 
|---|
|  |  |  | Class<?> type = field.getType(); | 
|---|
|  |  |  | String typeName = type.getSimpleName(); | 
|---|
|  |  |  | if(typeName.equals("Struct")){ | 
|---|
|  |  |  | if(entity.getClassType(name)!=null){ | 
|---|
|  |  |  | type = entity.getClassType(name); | 
|---|
|  |  |  | typeName = type.getSimpleName(); | 
|---|
|  |  |  | }else if(field.get(entity)!=null){ | 
|---|
|  |  |  | type = field.get(entity).getClass(); | 
|---|
|  |  |  | typeName = type.getSimpleName(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | size size = field.getAnnotation(size.class); | 
|---|
|  |  |  | bit bit = field.getAnnotation(bit.class); | 
|---|
|  |  |  | expr expr = field.getAnnotation(expr.class); | 
|---|
|  |  |  | flag flag = field.getAnnotation(flag.class); | 
|---|
|  |  |  | if(size==null && bit==null && expr==null){ | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | int bited = 0;//已写位数 | 
|---|
|  |  |  | int length = 0;//定义的长度 | 
|---|
|  |  |  | if(size!=null){ | 
|---|
|  |  |  | length = size.value(); | 
|---|
|  |  |  | bited+=size.value()*8; | 
|---|
|  |  |  | }else if(bit!=null){ | 
|---|
|  |  |  | //按位写方式未实现处理 | 
|---|
|  |  |  | throw new RuntimeException("按位写方式未实现处理"); | 
|---|
|  |  |  | }else if(expr!=null){ | 
|---|
|  |  |  | ExpressionParser parser = new SpelExpressionParser(); | 
|---|
|  |  |  | Expression exp = parser.parseExpression(expr.value()); | 
|---|
|  |  |  | Integer len = null; | 
|---|
|  |  |  | try{ | 
|---|
|  |  |  | len = CommonUtils.parseInt(exp.getValue(context, entity)); | 
|---|
|  |  |  | }catch (Exception e){ | 
|---|
|  |  |  | len = 0; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if(len!=null){ | 
|---|
|  |  |  | if(len==-1)continue; | 
|---|
|  |  |  | length = len; | 
|---|
|  |  |  | bited+=len*8; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | context.setVariable(name, field.get(entity)); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | byte[] bytes = new byte[length]; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (typeName.equals("int") || typeName.equals("Integer")){ | 
|---|
|  |  |  | dos.write(bytes=convert(length,field.getInt(entity))); | 
|---|
|  |  |  | }else if (typeName.equals("long") || typeName.equals("Long")){ | 
|---|
|  |  |  | dos.write(bytes=convert(length,field.getLong(entity))); | 
|---|
|  |  |  | }else if (typeName.equals("short") || typeName.equals("Short")){ | 
|---|
|  |  |  | dos.write(bytes=convert(length,field.getShort(entity))); | 
|---|
|  |  |  | }else if (typeName.equals("byte") || typeName.equals("Byte")){ | 
|---|
|  |  |  | dos.write(bytes=convert(length,field.getByte(entity))); | 
|---|
|  |  |  | }else if (typeName.equals("String")) { | 
|---|
|  |  |  | String str = (String) field.get(entity); | 
|---|
|  |  |  | if (str == null) | 
|---|
|  |  |  | str = ""; | 
|---|
|  |  |  | str = str.replaceAll(" ", " "); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | byte[] bts = null; | 
|---|
|  |  |  | if(flag!=null && flag.value().equals("ASCII")){ | 
|---|
|  |  |  | bts = Struct.StrToAsc(str); | 
|---|
|  |  |  | }else if(flag!=null && flag.value().equals("BCD")){ | 
|---|
|  |  |  | bts = Struct.str2Bcd(str); | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | bts = str.getBytes(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if(length==0){//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } else if (typeName.equals("byte[]")) { | 
|---|
|  |  |  | byte[] bts = (byte[]) field.get(entity); | 
|---|
|  |  |  | if(bts!=null){ | 
|---|
|  |  |  | if(length==0){//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }else if (type.isArray()) { | 
|---|
|  |  |  | ByteArrayOutputStream baOs1 = new ByteArrayOutputStream(); | 
|---|
|  |  |  | DataOutputStream dos1 = new DataOutputStream(baOs1); | 
|---|
|  |  |  | for (Object object : (Object[]) field.get(entity)){ | 
|---|
|  |  |  | write(dos1, (Struct) object); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte[] bts  = baOs1.toByteArray(); | 
|---|
|  |  |  | if(length==0){//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | byte[] bts = encode((Struct) field.get(entity)); | 
|---|
|  |  |  | if(length==0){//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | entity.encodeByteMapping.put(name, bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public byte[] convert(int size, long value) throws Exception{ | 
|---|
|  |  |  | ByteArrayOutputStream baOs = new ByteArrayOutputStream(); | 
|---|
|  |  |  | DataOutputStream dos = new DataOutputStream(baOs); | 
|---|
|  |  |  | switch(size){ | 
|---|
|  |  |  | case 1://byte | 
|---|
|  |  |  | dos.write((byte) value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case 2://short | 
|---|
|  |  |  | dos.writeShort(this.isReverse() ? Short.reverseBytes((short)value) : (short)value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case 4://int | 
|---|
|  |  |  | dos.writeInt(this.isReverse() ? Integer.reverseBytes((int)value) : (int)value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case 8://long | 
|---|
|  |  |  | dos.writeLong(this.isReverse() ? Long.reverseBytes((long)value) : (long)value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return baOs.toByteArray(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public long convertEx(long value,int size) throws Exception{ | 
|---|
|  |  |  | switch(size){ | 
|---|
|  |  |  | case 1://byte | 
|---|
|  |  |  | return value; | 
|---|
|  |  |  | case 2://short | 
|---|
|  |  |  | return (this.isReverse() ? Short.reverseBytes((short)value) : (short)value); | 
|---|
|  |  |  | case 4://int | 
|---|
|  |  |  | return (this.isReverse() ? Integer.reverseBytes((int)value) : (int)value); | 
|---|
|  |  |  | case 8://long | 
|---|
|  |  |  | return (this.isReverse() ? Long.reverseBytes((long)value) : (long)value); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return 0; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | @SuppressWarnings({ "unchecked", "rawtypes" }) | 
|---|
|  |  |  | public static <T extends Struct> T decode(Class<? extends Struct> cls,byte[] bytes) throws Exception{ | 
|---|
|  |  |  | Struct instance = cls.newInstance(); | 
|---|
|  |  |  | return instance.decode(bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 解码: 字节数组 ====>> java对象 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | @SuppressWarnings({ "unchecked", "rawtypes" }) | 
|---|
|  |  |  | public <T extends Struct> T decode(byte[] bytes) throws Exception{ | 
|---|
|  |  |  | this.dataBytes = bytes; | 
|---|
|  |  |  | Struct instance = this; | 
|---|
|  |  |  | Class<? extends Struct> cls = this.getClass(); | 
|---|
|  |  |  | Field[] fields = cls.getFields(); | 
|---|
|  |  |  | if(this.decodeByteMapping==null)instance.decodeByteMapping=new HashMap(); | 
|---|
|  |  |  | // spring el表达式 | 
|---|
|  |  |  | EvaluationContext context = new StandardEvaluationContext(); | 
|---|
|  |  |  | context.setVariable("_", bytes.length); | 
|---|
|  |  |  | int bited = 0;//已读位数 | 
|---|
|  |  |  | for(Field field: Objects.requireNonNull(ReflectUtils.removeStaticField(fields))){ | 
|---|
|  |  |  | size size = field.getAnnotation(size.class); | 
|---|
|  |  |  | bit bit = field.getAnnotation(bit.class); | 
|---|
|  |  |  | expr expr = field.getAnnotation(expr.class); | 
|---|
|  |  |  | flag flag = field.getAnnotation(flag.class); | 
|---|
|  |  |  | // 没有size、bit、expr注解的变量不进行解析 | 
|---|
|  |  |  | if(size==null && bit==null && expr==null){ | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | String name = field.getName(); | 
|---|
|  |  |  | Class<?> type = field.getType(); | 
|---|
|  |  |  | String typeName = type.getSimpleName(); | 
|---|
|  |  |  | if(typeName.equals("Struct")){ | 
|---|
|  |  |  | if(instance.getClassType(name)!=null){ | 
|---|
|  |  |  | type = instance.getClassType(name); | 
|---|
|  |  |  | typeName = type.getSimpleName(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | context.setVariable("__", bytes.length-bited/8); | 
|---|
|  |  |  | byte[] bts = null; | 
|---|
|  |  |  | if(size!=null){ | 
|---|
|  |  |  | bts = new byte[size.value()]; | 
|---|
|  |  |  | System.arraycopy(bytes, bited/8, bts, 0, size.value()); | 
|---|
|  |  |  | bited+=size.value()*8; | 
|---|
|  |  |  | }else if(bit!=null){ | 
|---|
|  |  |  | int val = 0; | 
|---|
|  |  |  | String strBin = ""; | 
|---|
|  |  |  | for(int i=bited;i<bited+bit.value();i++){//逐位扫描 | 
|---|
|  |  |  | if((bytes[i/8] & (1 << (i%8))) != 0){//如果位为1 | 
|---|
|  |  |  | strBin+="1"; | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | strBin+="0"; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | val = Integer.parseInt(strBin, 2); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte[] result = new byte[4]; | 
|---|
|  |  |  | result[3] = (byte)(val); | 
|---|
|  |  |  | result[2] = (byte)(val >> 8); | 
|---|
|  |  |  | result[1] = (byte)(val >> 16); | 
|---|
|  |  |  | result[0] = (byte)(val >> 24); | 
|---|
|  |  |  | bts = new byte[(int)Math.ceil((double)bit.value()/8.0)]; | 
|---|
|  |  |  | System.arraycopy(result, result.length-bts.length, bts, 0, bts.length); | 
|---|
|  |  |  | bited+=bit.value(); | 
|---|
|  |  |  | }else if(expr!=null){ | 
|---|
|  |  |  | ExpressionParser parser = new SpelExpressionParser(); | 
|---|
|  |  |  | Expression exp = parser.parseExpression(expr.value()); | 
|---|
|  |  |  | Integer len = CommonUtils.parseInt(exp.getValue(context,instance)); | 
|---|
|  |  |  | if(len!=null){ | 
|---|
|  |  |  | if(len==-1)continue; | 
|---|
|  |  |  | bts = new byte[len]; | 
|---|
|  |  |  | System.arraycopy(bytes, bited/8, bts, 0, len); | 
|---|
|  |  |  | bited+=len*8; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if(bts.length==0)continue; | 
|---|
|  |  |  | DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bts)); | 
|---|
|  |  |  | if (typeName.equals("int") || typeName.equals("Integer")){ | 
|---|
|  |  |  | field.setInt(instance,(int) convertEx(dis.readInt(),4)); | 
|---|
|  |  |  | }else if (typeName.equals("long") || typeName.equals("Long")){ | 
|---|
|  |  |  | field.setLong(instance, convertEx(dis.readLong(),8)); | 
|---|
|  |  |  | }else if (typeName.equals("short") || typeName.equals("Short")){ | 
|---|
|  |  |  | field.setShort(instance, (short) convertEx(dis.readShort(),2)); | 
|---|
|  |  |  | }else if (typeName.equals("byte") || typeName.equals("Byte")){ | 
|---|
|  |  |  | field.setByte(instance, (byte) convertEx(dis.readByte(),1)); | 
|---|
|  |  |  | }else if (typeName.equals("byte[]")){ | 
|---|
|  |  |  | field.set(instance, bts); | 
|---|
|  |  |  | }else if (typeName.equals("String")) { | 
|---|
|  |  |  | String strStr = new String(bts, StandardCharsets.UTF_8); | 
|---|
|  |  |  | if(flag!=null && flag.value().equals("BCD")){ | 
|---|
|  |  |  | strStr = bcd2Str(bts); | 
|---|
|  |  |  | }else if(flag!=null && flag.value().equals("ASCII")){ | 
|---|
|  |  |  | strStr = Struct.AscToStr(bts); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | strStr = strStr.replaceAll("[\u0000]", ""); | 
|---|
|  |  |  | field.set(instance, strStr); | 
|---|
|  |  |  | }else if (type.isArray()) { | 
|---|
|  |  |  | byte[] arrayBytes = bts; | 
|---|
|  |  |  | List list = new ArrayList(); | 
|---|
|  |  |  | while(arrayBytes.length>0 && list.size()<256){ | 
|---|
|  |  |  | Struct item = decode((Class<? extends Struct>) type.getComponentType(),arrayBytes); | 
|---|
|  |  |  | list.add(item); | 
|---|
|  |  |  | arrayBytes = item.unusedBytes; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | Object array = Array.newInstance(type.getComponentType(), list.size()); | 
|---|
|  |  |  | for(int i=0;i<list.size();i++){ | 
|---|
|  |  |  | Array.set(array, i, list.get(i)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | field.set(instance, array); | 
|---|
|  |  |  | }else if(type==Struct.class || Struct.class.isAssignableFrom(type)){ | 
|---|
|  |  |  | byte[] arrayBytes = bts; | 
|---|
|  |  |  | Struct item = decode((Class<? extends Struct>) type,arrayBytes); | 
|---|
|  |  |  | field.set(instance, item); | 
|---|
|  |  |  | }else{ | 
|---|
|  |  |  | throw new RuntimeException(cls.getSimpleName()+"字段"+field.getName()+"类型未知"); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | instance.decodeByteMapping.put(name, bts); | 
|---|
|  |  |  | context.setVariable(name, field.get(instance)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte[] useBytes = new byte[bited/8]; | 
|---|
|  |  |  | System.arraycopy(bytes, 0 , useBytes, 0, bited/8); | 
|---|
|  |  |  | instance.decodeBytes = useBytes; | 
|---|
|  |  |  | byte[] unusedBytes = new byte[bytes.length-(bited/8)]; | 
|---|
|  |  |  | if(unusedBytes.length>0){ | 
|---|
|  |  |  | System.arraycopy(bytes, useBytes.length , unusedBytes, 0, unusedBytes.length); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | instance.unusedBytes = unusedBytes; | 
|---|
|  |  |  | return (T) instance; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 字节数组 ===>> 转为十六进制字符串 | 
|---|
|  |  |  | public static String toHex(byte[] bytes) { | 
|---|
|  |  |  | // 字节数组 ===>> 转为十六进制字符串 | 
|---|
|  |  |  | public static String toHex(byte[] bytes) { | 
|---|
|  |  |  | StringBuilder buf = new StringBuilder(bytes.length * 2); | 
|---|
|  |  |  | for(byte b : bytes) { // 使用String的format方法进行转换 | 
|---|
|  |  |  | for (byte b : bytes) { // 使用String的format方法进行转换 | 
|---|
|  |  |  | buf.append(String.format("%02x", b & 0xff)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return buf.toString().toUpperCase(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static byte[] toBytes(String str) { | 
|---|
|  |  |  | if(str == null || str.trim().equals("")) return new byte[0]; | 
|---|
|  |  |  | 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++) { | 
|---|
|  |  |  | 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; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * @功能: BCD码转为10进制串(阿拉伯数据) | 
|---|
|  |  |  | * @参数: BCD码 | 
|---|
|  |  |  | * @结果: 10进制串 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public static String bcd2Str(byte[] bytes) { | 
|---|
|  |  |  | StringBuffer temp = new StringBuffer(bytes.length * 2); | 
|---|
|  |  |  | for (int i = 0; i < bytes.length; i++) { | 
|---|
|  |  |  | temp.append((byte) ((bytes[i] & 0xf0) >>> 4)); | 
|---|
|  |  |  | temp.append((byte) (bytes[i] & 0x0f)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp | 
|---|
|  |  |  | .toString().substring(1) : temp.toString(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * @功能: 10进制串转为BCD码 | 
|---|
|  |  |  | * @参数: 10进制串 | 
|---|
|  |  |  | * @结果: BCD码 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public static byte[] str2Bcd(String asc) { | 
|---|
|  |  |  | int len = asc.length(); | 
|---|
|  |  |  | int mod = len % 2; | 
|---|
|  |  |  | if (mod != 0) { | 
|---|
|  |  |  | asc = "0" + asc; | 
|---|
|  |  |  | len = asc.length(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte abt[] = new byte[len]; | 
|---|
|  |  |  | if (len >= 2) { | 
|---|
|  |  |  | len = len / 2; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte bbt[] = new byte[len]; | 
|---|
|  |  |  | abt = asc.getBytes(); | 
|---|
|  |  |  | int j, k; | 
|---|
|  |  |  | for (int p = 0; p < asc.length() / 2; p++) { | 
|---|
|  |  |  | if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) { | 
|---|
|  |  |  | j = abt[2 * p] - '0'; | 
|---|
|  |  |  | } else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) { | 
|---|
|  |  |  | j = abt[2 * p] - 'a' + 0x0a; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | j = abt[2 * p] - 'A' + 0x0a; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) { | 
|---|
|  |  |  | k = abt[2 * p + 1] - '0'; | 
|---|
|  |  |  | } else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) { | 
|---|
|  |  |  | k = abt[2 * p + 1] - 'a' + 0x0a; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | k = abt[2 * p + 1] - 'A' + 0x0a; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | int a = (j << 4) + k; | 
|---|
|  |  |  | byte b = (byte) a; | 
|---|
|  |  |  | bbt[p] = b; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return bbt; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static class TestMessage { | 
|---|
|  |  |  | public @size(1) byte begin = 0x7e;//起始符 | 
|---|
|  |  |  | public @size(2) short type = 0;//消息ID | 
|---|
|  |  |  | public @bit(2) byte retain;//消息体属性>保留 | 
|---|
|  |  |  | public @bit(1) byte subpkg;//消息体属性>是否分包,0表示不分包 | 
|---|
|  |  |  | public @bit(3) byte encrypt;//消息体属性>加密方式,0表示不加密 | 
|---|
|  |  |  | public @bit(10) short length;//消息体长度 | 
|---|
|  |  |  | public @size(6) @flag("BCD") String number;//手机号码 | 
|---|
|  |  |  | public @size(2) short serial;//消息流水号 | 
|---|
|  |  |  | public @expr("#subpkg==1?2:0") short pkgs;//分包数 | 
|---|
|  |  |  | public @expr("#subpkg==1?2:0") short index;//包序号 | 
|---|
|  |  |  | public @expr("#length") byte[] data;//数据部分 | 
|---|
|  |  |  | public @size(1) byte verify;//校验码 | 
|---|
|  |  |  | public @size(1) byte end = 0x7e;//结束符 | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static byte getXor(byte[] datas,int start, int end){ | 
|---|
|  |  |  | byte temp=datas[start]; | 
|---|
|  |  |  | for (int i = start+1; i <end; i++) { | 
|---|
|  |  |  | temp ^=datas[i]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return temp; | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * @功能: BCD码转为10进制串(阿拉伯数据) | 
|---|
|  |  |  | * @参数: BCD码 | 
|---|
|  |  |  | * @结果: 10进制串 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public static String bcd2Str(byte[] bytes) { | 
|---|
|  |  |  | StringBuffer temp = new StringBuffer(bytes.length * 2); | 
|---|
|  |  |  | for (int i = 0; i < bytes.length; i++) { | 
|---|
|  |  |  | temp.append((byte) ((bytes[i] & 0xf0) >>> 4)); | 
|---|
|  |  |  | temp.append((byte) (bytes[i] & 0x0f)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp | 
|---|
|  |  |  | .toString().substring(1) : temp.toString(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * @功能: 10进制串转为BCD码 | 
|---|
|  |  |  | * @参数: 10进制串 | 
|---|
|  |  |  | * @结果: BCD码 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public static byte[] str2Bcd(String asc) { | 
|---|
|  |  |  | int len = asc.length(); | 
|---|
|  |  |  | int mod = len % 2; | 
|---|
|  |  |  | if (mod != 0) { | 
|---|
|  |  |  | asc = "0" + asc; | 
|---|
|  |  |  | len = asc.length(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte abt[] = new byte[len]; | 
|---|
|  |  |  | if (len >= 2) { | 
|---|
|  |  |  | len = len / 2; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte bbt[] = new byte[len]; | 
|---|
|  |  |  | abt = asc.getBytes(); | 
|---|
|  |  |  | int j, k; | 
|---|
|  |  |  | for (int p = 0; p < asc.length() / 2; p++) { | 
|---|
|  |  |  | if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) { | 
|---|
|  |  |  | j = abt[2 * p] - '0'; | 
|---|
|  |  |  | } else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) { | 
|---|
|  |  |  | j = abt[2 * p] - 'a' + 0x0a; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | j = abt[2 * p] - 'A' + 0x0a; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) { | 
|---|
|  |  |  | k = abt[2 * p + 1] - '0'; | 
|---|
|  |  |  | } else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) { | 
|---|
|  |  |  | k = abt[2 * p + 1] - 'a' + 0x0a; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | k = abt[2 * p + 1] - 'A' + 0x0a; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | int a = (j << 4) + k; | 
|---|
|  |  |  | byte b = (byte) a; | 
|---|
|  |  |  | bbt[p] = b; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return bbt; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static byte getXor(byte[] datas, int start, int end) { | 
|---|
|  |  |  | byte temp = datas[start]; | 
|---|
|  |  |  | for (int i = start + 1; i < end; i++) { | 
|---|
|  |  |  | temp ^= datas[i]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return temp; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 求校验和的算法 | 
|---|
|  |  |  | * @param b 需要求校验和的字节数组 | 
|---|
|  |  |  | * @return 校验和 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public static byte sumCheck(byte[] b,int start, int end){ | 
|---|
|  |  |  | public static byte sumCheck(byte[] b, int start, int end) { | 
|---|
|  |  |  | int sum = 0; | 
|---|
|  |  |  | for(int i = start; i < end; i++){ | 
|---|
|  |  |  | for (int i = start; i < end; i++) { | 
|---|
|  |  |  | sum = sum + b[i]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return (byte) (sum & 0xff); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //字符串转换为ascii | 
|---|
|  |  |  | public static byte[] StrToAsc(String content){ | 
|---|
|  |  |  | try { | 
|---|
|  |  |  | return content.getBytes(StandardCharsets.US_ASCII); | 
|---|
|  |  |  | } catch (Exception e) { | 
|---|
|  |  |  | e.printStackTrace(); | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //字符串转换为ascii | 
|---|
|  |  |  | public static byte[] StrToAsc(String content) { | 
|---|
|  |  |  | try { | 
|---|
|  |  |  | return content.getBytes(StandardCharsets.US_ASCII); | 
|---|
|  |  |  | } catch (Exception e) { | 
|---|
|  |  |  | e.printStackTrace(); | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | //ascii转换为string | 
|---|
|  |  |  | public static String AscToStr(byte[] bytes){ | 
|---|
|  |  |  | StringBuffer sbu = new StringBuffer(); | 
|---|
|  |  |  | for (byte aByte : bytes) { | 
|---|
|  |  |  | sbu.append(Character.toString((char) aByte)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return sbu.toString(); | 
|---|
|  |  |  | public static String AscToStr(byte[] bytes) { | 
|---|
|  |  |  | StringBuffer sbu = new StringBuffer(); | 
|---|
|  |  |  | for (byte aByte : bytes) { | 
|---|
|  |  |  | sbu.append(Character.toString((char) aByte)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return sbu.toString(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public Class getClassType(String name) { | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 判断大小端 | 
|---|
|  |  |  | * @return true: 小端 / false: 大端 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public boolean isReverse() { | 
|---|
|  |  |  | return this.getClass().getAnnotation(little.class) != null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 获取字节数组 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public byte[] toBytes() throws Exception { | 
|---|
|  |  |  | return encode(this); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public Map<String, String> toDecodeHexMapping() throws Exception { | 
|---|
|  |  |  | if (this.decodeByteMapping == null) this.decodeByteMapping = new HashMap<>(); | 
|---|
|  |  |  | Map<String, String> mapping = getHex(this.decodeByteMapping); | 
|---|
|  |  |  | if (this.decodeBytes != null) { | 
|---|
|  |  |  | mapping.put("byte[]", Struct.toHex(this.decodeBytes)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return mapping; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public Map<String, String> toEncodeHexMapping() throws Exception { | 
|---|
|  |  |  | if (this.encodeBytes == null) this.encode(this); | 
|---|
|  |  |  | if (this.encodeByteMapping == null) this.encodeByteMapping = new HashMap<>(); | 
|---|
|  |  |  | Map<String, String> mapping = getHex(this.encodeByteMapping); | 
|---|
|  |  |  | mapping.put("byte[]", Struct.toHex(this.encodeBytes)); | 
|---|
|  |  |  | return mapping; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public Map<String, String> getHex(Map<String, byte[]> bytemapping) throws Exception { | 
|---|
|  |  |  | Map<String, String> map = new LinkedHashMap<>(); | 
|---|
|  |  |  | Field[] fields = this.getClass().getFields(); | 
|---|
|  |  |  | for (Field field : fields) { | 
|---|
|  |  |  | byte[] bytes = bytemapping.get(field.getName()); | 
|---|
|  |  |  | if (bytes != null) { | 
|---|
|  |  |  | map.put(field.getName(), Struct.toHex(bytes)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return map; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 转java对象到数组 | 
|---|
|  |  |  | public byte[] encode(Struct entity) throws Exception { | 
|---|
|  |  |  | ByteArrayOutputStream baOs = new ByteArrayOutputStream(); | 
|---|
|  |  |  | write(new DataOutputStream(baOs), entity); | 
|---|
|  |  |  | entity.encodeBytes = baOs.toByteArray(); | 
|---|
|  |  |  | return entity.encodeBytes; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 写数据到输出流 | 
|---|
|  |  |  | * | 
|---|
|  |  |  | * @param dos | 
|---|
|  |  |  | * @param entity | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public void write(DataOutputStream dos, Struct entity) throws Exception { | 
|---|
|  |  |  | if (entity == null) return; | 
|---|
|  |  |  | if (entity.encodeByteMapping == null) entity.encodeByteMapping = new HashMap<>(); | 
|---|
|  |  |  | Field[] fields = entity.getClass().getFields(); | 
|---|
|  |  |  | EvaluationContext context = new StandardEvaluationContext(); | 
|---|
|  |  |  | for (Field field : Objects.requireNonNull(ReflectUtils.removeStaticField(fields))) { | 
|---|
|  |  |  | field.setAccessible(true); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | String name = field.getName(); | 
|---|
|  |  |  | Class<?> type = field.getType(); | 
|---|
|  |  |  | String typeName = type.getSimpleName(); | 
|---|
|  |  |  | if (typeName.equals("Struct")) { | 
|---|
|  |  |  | if (entity.getClassType(name) != null) { | 
|---|
|  |  |  | type = entity.getClassType(name); | 
|---|
|  |  |  | typeName = type.getSimpleName(); | 
|---|
|  |  |  | } else if (field.get(entity) != null) { | 
|---|
|  |  |  | type = field.get(entity).getClass(); | 
|---|
|  |  |  | typeName = type.getSimpleName(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | size size = field.getAnnotation(size.class); | 
|---|
|  |  |  | bit bit = field.getAnnotation(bit.class); | 
|---|
|  |  |  | expr expr = field.getAnnotation(expr.class); | 
|---|
|  |  |  | flag flag = field.getAnnotation(flag.class); | 
|---|
|  |  |  | if (size == null && bit == null && expr == null) { | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | int bited = 0;//已写位数 | 
|---|
|  |  |  | int length = 0;//定义的长度 | 
|---|
|  |  |  | if (size != null) { | 
|---|
|  |  |  | length = size.value(); | 
|---|
|  |  |  | bited += size.value() * 8; | 
|---|
|  |  |  | } else if (bit != null) { | 
|---|
|  |  |  | //按位写方式未实现处理 | 
|---|
|  |  |  | throw new RuntimeException("按位写方式未实现处理"); | 
|---|
|  |  |  | } else if (expr != null) { | 
|---|
|  |  |  | ExpressionParser parser = new SpelExpressionParser(); | 
|---|
|  |  |  | Expression exp = parser.parseExpression(expr.value()); | 
|---|
|  |  |  | Integer len = null; | 
|---|
|  |  |  | try { | 
|---|
|  |  |  | len = CommonUtils.parseInt(exp.getValue(context, entity)); | 
|---|
|  |  |  | } catch (Exception e) { | 
|---|
|  |  |  | len = 0; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (len != null) { | 
|---|
|  |  |  | if (len == -1) continue; | 
|---|
|  |  |  | length = len; | 
|---|
|  |  |  | bited += len * 8; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | context.setVariable(name, field.get(entity)); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | byte[] bytes = new byte[length]; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (typeName.equals("int") || typeName.equals("Integer")) { | 
|---|
|  |  |  | dos.write(bytes = convert(length, field.getInt(entity))); | 
|---|
|  |  |  | } else if (typeName.equals("long") || typeName.equals("Long")) { | 
|---|
|  |  |  | dos.write(bytes = convert(length, field.getLong(entity))); | 
|---|
|  |  |  | } else if (typeName.equals("short") || typeName.equals("Short")) { | 
|---|
|  |  |  | dos.write(bytes = convert(length, field.getShort(entity))); | 
|---|
|  |  |  | } else if (typeName.equals("byte") || typeName.equals("Byte")) { | 
|---|
|  |  |  | dos.write(bytes = convert(length, field.getByte(entity))); | 
|---|
|  |  |  | } else if (typeName.equals("String")) { | 
|---|
|  |  |  | String str = (String) field.get(entity); | 
|---|
|  |  |  | if (str == null) | 
|---|
|  |  |  | str = ""; | 
|---|
|  |  |  | str = str.replaceAll(" ", " "); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | byte[] bts = null; | 
|---|
|  |  |  | if (flag != null && flag.value().equals("ASCII")) { | 
|---|
|  |  |  | bts = Struct.StrToAsc(str); | 
|---|
|  |  |  | } else if (flag != null && flag.value().equals("BCD")) { | 
|---|
|  |  |  | bts = Struct.str2Bcd(str); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | bts = str.getBytes(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (length == 0) {//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } else if (typeName.equals("byte[]")) { | 
|---|
|  |  |  | byte[] bts = (byte[]) field.get(entity); | 
|---|
|  |  |  | if (bts != null) { | 
|---|
|  |  |  | if (length == 0) {//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } else if (type.isArray()) { | 
|---|
|  |  |  | ByteArrayOutputStream baOs1 = new ByteArrayOutputStream(); | 
|---|
|  |  |  | DataOutputStream dos1 = new DataOutputStream(baOs1); | 
|---|
|  |  |  | for (Object object : (Object[]) field.get(entity)) { | 
|---|
|  |  |  | write(dos1, (Struct) object); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte[] bts = baOs1.toByteArray(); | 
|---|
|  |  |  | if (length == 0) {//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | byte[] bts = encode((Struct) field.get(entity)); | 
|---|
|  |  |  | if (length == 0) {//如果长度未设置,以当前实际的字节为准 | 
|---|
|  |  |  | bytes = new byte[bts.length]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | System.arraycopy(bts, 0, bytes, 0, bts.length); | 
|---|
|  |  |  | dos.write(bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | entity.encodeByteMapping.put(name, bytes); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public byte[] convert(int size, long value) throws Exception { | 
|---|
|  |  |  | ByteArrayOutputStream baOs = new ByteArrayOutputStream(); | 
|---|
|  |  |  | DataOutputStream dos = new DataOutputStream(baOs); | 
|---|
|  |  |  | switch (size) { | 
|---|
|  |  |  | case 1://byte | 
|---|
|  |  |  | dos.write((byte) value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case 2://short | 
|---|
|  |  |  | dos.writeShort(this.isReverse() ? Short.reverseBytes((short) value) : (short) value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case 4://int | 
|---|
|  |  |  | dos.writeInt(this.isReverse() ? Integer.reverseBytes((int) value) : (int) value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case 8://long | 
|---|
|  |  |  | dos.writeLong(this.isReverse() ? Long.reverseBytes((long) value) : (long) value); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return baOs.toByteArray(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public long convertEx(long value, int size) throws Exception { | 
|---|
|  |  |  | switch (size) { | 
|---|
|  |  |  | case 1://byte | 
|---|
|  |  |  | return value; | 
|---|
|  |  |  | case 2://short | 
|---|
|  |  |  | return (this.isReverse() ? Short.reverseBytes((short) value) : (short) value); | 
|---|
|  |  |  | case 4://int | 
|---|
|  |  |  | return (this.isReverse() ? Integer.reverseBytes((int) value) : (int) value); | 
|---|
|  |  |  | case 8://long | 
|---|
|  |  |  | return (this.isReverse() ? Long.reverseBytes((long) value) : (long) value); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return 0; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 解码: 字节数组 ====>> java对象 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | @SuppressWarnings({"unchecked", "rawtypes"}) | 
|---|
|  |  |  | public <T extends Struct> T decode(byte[] bytes) throws Exception { | 
|---|
|  |  |  | this.dataBytes = bytes; | 
|---|
|  |  |  | Struct instance = this; | 
|---|
|  |  |  | Class<? extends Struct> cls = this.getClass(); | 
|---|
|  |  |  | Field[] fields = cls.getFields(); | 
|---|
|  |  |  | if (this.decodeByteMapping == null) instance.decodeByteMapping = new HashMap(); | 
|---|
|  |  |  | // spring el表达式 | 
|---|
|  |  |  | EvaluationContext context = new StandardEvaluationContext(); | 
|---|
|  |  |  | context.setVariable("_", bytes.length); | 
|---|
|  |  |  | int bited = 0;//已读位数 | 
|---|
|  |  |  | for (Field field : Objects.requireNonNull(ReflectUtils.removeStaticField(fields))) { | 
|---|
|  |  |  | size size = field.getAnnotation(size.class); | 
|---|
|  |  |  | bit bit = field.getAnnotation(bit.class); | 
|---|
|  |  |  | expr expr = field.getAnnotation(expr.class); | 
|---|
|  |  |  | flag flag = field.getAnnotation(flag.class); | 
|---|
|  |  |  | // 没有size、bit、expr注解的变量不进行解析 | 
|---|
|  |  |  | if (size == null && bit == null && expr == null) { | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | String name = field.getName(); | 
|---|
|  |  |  | Class<?> type = field.getType(); | 
|---|
|  |  |  | String typeName = type.getSimpleName(); | 
|---|
|  |  |  | if (typeName.equals("Struct")) { | 
|---|
|  |  |  | if (instance.getClassType(name) != null) { | 
|---|
|  |  |  | type = instance.getClassType(name); | 
|---|
|  |  |  | typeName = type.getSimpleName(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | context.setVariable("__", bytes.length - bited / 8); | 
|---|
|  |  |  | byte[] bts = null; | 
|---|
|  |  |  | if (size != null) { | 
|---|
|  |  |  | bts = new byte[size.value()]; | 
|---|
|  |  |  | System.arraycopy(bytes, bited / 8, bts, 0, size.value()); | 
|---|
|  |  |  | bited += size.value() * 8; | 
|---|
|  |  |  | } else if (bit != null) { | 
|---|
|  |  |  | int val = 0; | 
|---|
|  |  |  | String strBin = ""; | 
|---|
|  |  |  | for (int i = bited; i < bited + bit.value(); i++) {//逐位扫描 | 
|---|
|  |  |  | if ((bytes[i / 8] & (1 << (i % 8))) != 0) {//如果位为1 | 
|---|
|  |  |  | strBin += "1"; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | strBin += "0"; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | val = Integer.parseInt(strBin, 2); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte[] result = new byte[4]; | 
|---|
|  |  |  | result[3] = (byte) (val); | 
|---|
|  |  |  | result[2] = (byte) (val >> 8); | 
|---|
|  |  |  | result[1] = (byte) (val >> 16); | 
|---|
|  |  |  | result[0] = (byte) (val >> 24); | 
|---|
|  |  |  | bts = new byte[(int) Math.ceil((double) bit.value() / 8.0)]; | 
|---|
|  |  |  | System.arraycopy(result, result.length - bts.length, bts, 0, bts.length); | 
|---|
|  |  |  | bited += bit.value(); | 
|---|
|  |  |  | } else if (expr != null) { | 
|---|
|  |  |  | ExpressionParser parser = new SpelExpressionParser(); | 
|---|
|  |  |  | Expression exp = parser.parseExpression(expr.value()); | 
|---|
|  |  |  | Integer len = CommonUtils.parseInt(exp.getValue(context, instance)); | 
|---|
|  |  |  | if (len != null) { | 
|---|
|  |  |  | if (len == -1) continue; | 
|---|
|  |  |  | bts = new byte[len]; | 
|---|
|  |  |  | System.arraycopy(bytes, bited / 8, bts, 0, len); | 
|---|
|  |  |  | bited += len * 8; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (bts.length == 0) continue; | 
|---|
|  |  |  | DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bts)); | 
|---|
|  |  |  | if (typeName.equals("int") || typeName.equals("Integer")) { | 
|---|
|  |  |  | field.setInt(instance, (int) convertEx(dis.readInt(), 4)); | 
|---|
|  |  |  | } else if (typeName.equals("long") || typeName.equals("Long")) { | 
|---|
|  |  |  | field.setLong(instance, convertEx(dis.readLong(), 8)); | 
|---|
|  |  |  | } else if (typeName.equals("short") || typeName.equals("Short")) { | 
|---|
|  |  |  | field.setShort(instance, (short) convertEx(dis.readShort(), 2)); | 
|---|
|  |  |  | } else if (typeName.equals("byte") || typeName.equals("Byte")) { | 
|---|
|  |  |  | field.setByte(instance, (byte) convertEx(dis.readByte(), 1)); | 
|---|
|  |  |  | } else if (typeName.equals("byte[]")) { | 
|---|
|  |  |  | field.set(instance, bts); | 
|---|
|  |  |  | } else if (typeName.equals("String")) { | 
|---|
|  |  |  | String strStr = new String(bts, StandardCharsets.UTF_8); | 
|---|
|  |  |  | if (flag != null && flag.value().equals("BCD")) { | 
|---|
|  |  |  | strStr = bcd2Str(bts); | 
|---|
|  |  |  | } else if (flag != null && flag.value().equals("ASCII")) { | 
|---|
|  |  |  | strStr = Struct.AscToStr(bts); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | strStr = strStr.replaceAll("[\u0000]", ""); | 
|---|
|  |  |  | field.set(instance, strStr); | 
|---|
|  |  |  | } else if (type.isArray()) { | 
|---|
|  |  |  | byte[] arrayBytes = bts; | 
|---|
|  |  |  | List list = new ArrayList(); | 
|---|
|  |  |  | while (arrayBytes.length > 0 && list.size() < 256) { | 
|---|
|  |  |  | Struct item = decode((Class<? extends Struct>) type.getComponentType(), arrayBytes); | 
|---|
|  |  |  | list.add(item); | 
|---|
|  |  |  | arrayBytes = item.unusedBytes; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | Object array = Array.newInstance(type.getComponentType(), list.size()); | 
|---|
|  |  |  | for (int i = 0; i < list.size(); i++) { | 
|---|
|  |  |  | Array.set(array, i, list.get(i)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | field.set(instance, array); | 
|---|
|  |  |  | } else if (type == Struct.class || Struct.class.isAssignableFrom(type)) { | 
|---|
|  |  |  | byte[] arrayBytes = bts; | 
|---|
|  |  |  | Struct item = decode((Class<? extends Struct>) type, arrayBytes); | 
|---|
|  |  |  | field.set(instance, item); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | throw new RuntimeException(cls.getSimpleName() + "字段" + field.getName() + "类型未知"); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | instance.decodeByteMapping.put(name, bts); | 
|---|
|  |  |  | context.setVariable(name, field.get(instance)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | byte[] useBytes = new byte[bited / 8]; | 
|---|
|  |  |  | System.arraycopy(bytes, 0, useBytes, 0, bited / 8); | 
|---|
|  |  |  | instance.decodeBytes = useBytes; | 
|---|
|  |  |  | byte[] unusedBytes = new byte[bytes.length - (bited / 8)]; | 
|---|
|  |  |  | if (unusedBytes.length > 0) { | 
|---|
|  |  |  | System.arraycopy(bytes, useBytes.length, unusedBytes, 0, unusedBytes.length); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | instance.unusedBytes = unusedBytes; | 
|---|
|  |  |  | return (T) instance; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static class TestMessage { | 
|---|
|  |  |  | public @size(1) byte begin = 0x7e;//起始符 | 
|---|
|  |  |  | public @size(2) short type = 0;//消息ID | 
|---|
|  |  |  | public @bit(2) byte retain;//消息体属性>保留 | 
|---|
|  |  |  | public @bit(1) byte subpkg;//消息体属性>是否分包,0表示不分包 | 
|---|
|  |  |  | public @bit(3) byte encrypt;//消息体属性>加密方式,0表示不加密 | 
|---|
|  |  |  | public @bit(10) short length;//消息体长度 | 
|---|
|  |  |  | public @size(6) | 
|---|
|  |  |  | @flag("BCD") String number;//手机号码 | 
|---|
|  |  |  | public @size(2) short serial;//消息流水号 | 
|---|
|  |  |  | public @expr("#subpkg==1?2:0") short pkgs;//分包数 | 
|---|
|  |  |  | public @expr("#subpkg==1?2:0") short index;//包序号 | 
|---|
|  |  |  | public @expr("#length") byte[] data;//数据部分 | 
|---|
|  |  |  | public @size(1) byte verify;//校验码 | 
|---|
|  |  |  | public @size(1) byte end = 0x7e;//结束符 | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|