liurunyu
2024-05-06 4b32f94e12f0afab074eee93a96eeb722b5bb090
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package com.dy.common.mw.protocol.p206V202404;
 
import com.dy.common.util.ByteUtil;
import com.dy.common.util.ByteUtilUnsigned;
import com.dy.common.util.CRC8_for_2_0;
 
 
public class CommonV202404 {
    /**
     * 检查头
     * @param bs 上行字节数组
     * @return true是,false否
     * @throws Exception  异常
     */
    public Boolean isThisProtocolHead(byte[] bs) throws Exception{
        if(bs == null){
            return null ;
        }else if(bs.length >= (ProtocolConstantV206V202404.ctrlIndex - 1)
                && bs[0] == ProtocolConstantV206V202404.P_Head_Byte){
            if(bs[2] == ProtocolConstantV206V202404.P_Head_Byte){
                return true ;
            }else if((byte)(bs[2] & 0xF8) == ProtocolConstantV206V202404.P_Head_Byte){
                //如果控制域、地址域、用户数据域(应用层)的字节总数大于255,则通过扩展第二个开始字符0x68的低3位作为长L的高位扩展
                //0xF8二进制: 11111000
                return true ;
            }else{
                return false ;
            }
        }else{
            return false ;
        }
    }
 
    /**
     * 检查头
     * @param bs 上行字节数组
     * @throws Exception 异常
     */
    public void checkHead(byte[] bs) throws Exception{
        if(bs.length < ProtocolConstantV206V202404.lenHead2Code
                || bs[0] != ProtocolConstantV206V202404.P_Head_Byte){
            throw new Exception("上行数据帧头不正确!") ;
        }else{
            if(bs.length >= ProtocolConstantV206V202404.lenHead2Code){
                if(bs[2] != ProtocolConstantV206V202404.P_Head_Byte
                    && (byte)(bs[2] & 0xF8) != ProtocolConstantV206V202404.P_Head_Byte){
                    throw new Exception("上行数据帧头不正确!") ;
                }
            }else{
                throw new Exception("上行数据帧头不正确!") ;
            }
        }
    }
 
    /**
     * 检查尾
     * @param bs 上行字节数组
     * @throws Exception 异常
     */
    public void checkTail(byte[] bs) throws Exception{
        if(bs.length < 1 || bs[bs.length - 1] != ProtocolConstantV206V202404.P_Tail_Byte){
            throw new Exception("上行数据尾不正确!") ;
        }
    }
 
 
    /**
     * 分析帧长度
     * @param bs 上行字节数组
     * @return 数据长度
     * @throws Exception 异常
     */
    public int parseFrameLen(byte[] bs)throws Exception{
        if(bs[2] != ProtocolConstantV206V202404.P_Head_Byte
                && (byte)(bs[2] & 0xF8) != ProtocolConstantV206V202404.P_Head_Byte){
            int lenH = bs[2] & 0x07 ;
            int lenL = ByteUtilUnsigned.byte2Byte(bs, ProtocolConstantV206V202404.dataLenIndex) ;
            return (lenH * 100) + (lenL + ProtocolConstantV206V202404.lenHead2ctrl + ProtocolConstantV206V202404.lenTail) ;
        }else{
            int len = ByteUtilUnsigned.byte2Byte(bs, ProtocolConstantV206V202404.dataLenIndex) ;
            return len + ProtocolConstantV206V202404.lenHead2ctrl + ProtocolConstantV206V202404.lenTail ;
        }
    }
 
 
    /**
     * 分析用户数据域字节数
     * @param bs 上行字节数组
     * @return 数据长度
     * @throws Exception 异常
     */
    public int parseDataLen(byte[] bs)throws Exception{
        return parseFrameLen(bs) ;
    }
 
 
    /**
     * 分析Rtu地址
     * @param bs 上行字节数组
     * @return 控制器地址
     * @throws Exception 异常
     */
    public String parseRtuAddr(byte[] bs)throws Exception{
        String rtuAddrBCD = "" + ByteUtil.BCD2Long_BE(bs, ProtocolConstantV206V202404.rtuAddr1Index_start, ProtocolConstantV206V202404.rtuAddr1Index_end) ;
        String rtuAddrStr = "" + ByteUtilUnsigned.bytes2Short_LE(bs, ProtocolConstantV206V202404.rtuAddr2Index_start) ;
        while(rtuAddrStr.length() < 5){
            rtuAddrStr = "0" + rtuAddrStr ;
        }
        return rtuAddrBCD + rtuAddrStr ;
    }
 
 
    /**
     * 分析Rtu地址
     * @param bs 上行字节数组
     * @param index 启始位
     * @return 控制器地址
     * @throws Exception 异常
     */
    public String parseRtuAddr(byte[] bs, int index)throws Exception{
        String rtuAddrBCD = "" + ByteUtil.BCD2Long_BE(bs, index, index + ProtocolConstantV206V202404.rtuAddr1Index_end - ProtocolConstantV206V202404.rtuAddr1Index_start) ;//地址是大端模式
        String rtuAddrStr = "" + ByteUtilUnsigned.bytes2Short_LE(bs, index + 1 + ProtocolConstantV206V202404.rtuAddr1Index_end - ProtocolConstantV206V202404.rtuAddr1Index_start) ;
        while(rtuAddrStr.length() < 5){
            rtuAddrStr = "0" + rtuAddrStr ;
        }
        return rtuAddrBCD + rtuAddrStr ;
    }
 
 
    /**
     * 分析功能码
     * @param bs 上行字节数组
     * @return 功能码
     */
    public String parseCode(byte[] bs){
        return ByteUtil.bytes2Hex(bs, false, ProtocolConstantV206V202404.codeIndex, 1) ;
    }
 
    /**
     * 校验和检查
     * @param bs  上行字节数组
     * @return 通过null,未通过返回原因
     * @throws Exception 异常
     */
    public String checkCrc_str(byte[] bs) throws Exception {
        byte crcCompute = (byte)new CRC8_for_2_0().CRC8(bs, ProtocolConstantV206V202404.ctrlIndex, bs.length - 3) ;
        byte crcInBs = bs[bs.length - 2] ;
        if(crcCompute == crcInBs){
            return null ;
        }else{
            return "计算CRC是:" + crcCompute + ",上传CRC是" + crcInBs ;
        }
    }
 
 
    /*
    构造控制域
    D7                  D6                  D5~D4            D3~D0
    传输方向位 DIR        拆分标志位 DIV       帧计数位 FCB       功能码
    1、传输方向位(DIR)
       DIR=0:表示此帧文是由中心站发出的下行报文。
       DIR=1:表示此帧文是由终端站发出的上行报文。
       每帧报文的通信过程中是不变的。
    2、拆分标志位(DIV)
       DIV=1:表示此报文已被拆分为若干帧,接收后需要拼接。此时控制域后增加一个字节,为拆分帧记数DIVS,采用BIN倒计数(255-1),1表示最后一帧数据。启动站发送时自动加上发送,从动站返帧时对应加上确认。
       DIV=0:表示此帧报文为单帧。
    3、帧计数位(FCB):
       FCB表示每个站连续的发送/确认或请求/响应服务的变化位。FCB位用来防止信息传输的丢失和重复。
       启动站向同一从动站传输新的发送/确认或请求/响应传输报务时,启动站将设置FCB值,若超时未到从站的报文,或接收出现差错,则启动站将FCB减1,重复原来的发送/确认或请求/响应服务,直到FCB减为0,表示本次传输服务失败。
       从动站收到启动站FCB值不为0的报文并按照要求确认或响应时,应返回相应的FCB值。
    */
    public byte createCtrl(byte dir, byte funcCode){
        byte b = dir;//DIR = 1(0x80),表示此帧报文是由终端发出的上行报文;
        b = (byte)(b | funcCode) ;
        //DIV = 1(0x40),表示此报文已被拆分为若干帧
        //FCB = 1(0x08),表示只发一次
        b = (byte)(b |0x08) ;
        //DIR = 0 下行,则功能码采用0
        return b ;
    }
 
    /**
     * 得到关开阀类型名称
     * @param type 字节
     * @return 名称
     */
    public static String openCloseValveType(byte type){
        return switch (type) {
            case 1 -> "刷卡开阀";
            case 2 -> "刷卡关阀";
            case 3 -> "中心站开阀";
            case 4 -> "中心站关阀";
            case 5 -> "欠费关阀";
            case 6 -> "流量计故障关阀";
            case 7 -> "紧急关阀";
            case 8 -> "用户远程开阀";
            case 9 -> "用户远程关阀";
            case 16 -> "管道无水自动关阀";
            default -> "未知";
        };
    }
 
    /*
     * 分析版本号
     * @param bs  上行字节数组
     * @return 版本号
     * @throws Exception 异常
    public String parseVersion(byte[] bs)throws Exception{
        short ver = ByteUtilUnsigned.byte2Byte(bs, ProtocolConstantV206V202404.versionIndex) ;
        char[] cs = ("" + ver).toCharArray() ;
        StringBuilder vs = new StringBuilder() ;
        for(byte i = 0 ; i < cs.length; i++){
            if(i == 0){
                vs.append(cs[i]) ;
            }else{
                vs.append(".").append(cs[i]) ;
            }
        }
        return vs.toString() ;
    }
    */
 
}