package com.zy.common.utils; 
 | 
  
 | 
import com.zy.common.model.annotations.*; 
 | 
import org.springframework.expression.EvaluationContext; 
 | 
import org.springframework.expression.Expression; 
 | 
import org.springframework.expression.ExpressionParser; 
 | 
import org.springframework.expression.spel.standard.SpelExpressionParser; 
 | 
import org.springframework.expression.spel.support.StandardEvaluationContext; 
 | 
  
 | 
import java.io.ByteArrayInputStream; 
 | 
import java.io.ByteArrayOutputStream; 
 | 
import java.io.DataInputStream; 
 | 
import java.io.DataOutputStream; 
 | 
import java.lang.reflect.Array; 
 | 
import java.lang.reflect.Field; 
 | 
import java.nio.charset.StandardCharsets; 
 | 
import java.util.*; 
 | 
  
 | 
/** 
 | 
 * Tcp协议报文基类 
 | 
 * @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; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 判断大小端 
 | 
     * @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; 
 | 
    } 
 | 
     
 | 
    @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) { 
 | 
        StringBuilder buf = new StringBuilder(bytes.length * 2); 
 | 
        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]; 
 | 
        byte[] bytes = new byte[str.length() / 2]; 
 | 
        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; 
 | 
    } 
 | 
  
 | 
     
 | 
    /** 
 | 
     * 求校验和的算法 
 | 
     * @param b 需要求校验和的字节数组 
 | 
     * @return 校验和 
 | 
     */ 
 | 
    public static byte sumCheck(byte[] b,int start, int end){ 
 | 
        int sum = 0; 
 | 
        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转换为string 
 | 
    public static String AscToStr(byte[] bytes){ 
 | 
        StringBuffer sbu = new StringBuffer(); 
 | 
        for (byte aByte : bytes) { 
 | 
            sbu.append(Character.toString((char) aByte)); 
 | 
        } 
 | 
        return sbu.toString();  
 | 
    } 
 | 
  
 | 
} 
 |