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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
package com.dy.common.mw.protocol.p206V202404;
 
import com.dy.common.mw.channel.tcp.TcpIoSessionAttrIdIsRtuAddr;
import com.dy.common.mw.protocol.p206V1.ProtocolConstantV206V1;
import com.dy.common.mw.protocol.p206V2.ProtocolConstantV206V2;
import com.dy.common.util.ByteUtil;
import com.dy.common.util.ByteUtilUnsigned;
import com.dy.common.util.CRC16;
import com.dy.common.util.CRC8_for_2_0;
import org.apache.mina.core.session.IoSession;
 
 
public class CommonV202404 {
 
    /**
     * 在Io会话中设置协议名称及版本号
     * @param ioSession 会话
     */
    public void setThisProtocolArr2IoSession(IoSession ioSession){
        ioSession.setAttribute(TcpIoSessionAttrIdIsRtuAddr.sessionArrProtocolName, ProtocolConstantV206V1.protocolName) ;
        ioSession.setAttribute(TcpIoSessionAttrIdIsRtuAddr.sessionArrProtocolName, ProtocolConstantV206V1.protocolVer) ;
    }
    /**
     * 检查头
     * @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
                && bs[0] == ProtocolConstantV206V202404.P_Head_Byte){
            if(bs[2] == ProtocolConstantV206V202404.P_Head_Byte){
                if(bs[3] == (byte)0x80 || bs[3] == (byte)0x81){
                    return true ;
                }else{
                    return false ;
                }
            }else if((byte)(bs[2] & 0xF8) == ProtocolConstantV206V202404.P_Head_Byte){
                //如果控制域、地址域、用户数据域(应用层)的字节总数大于255,则通过扩展第二个开始字符0x69的低3位作为长L的高位扩展
                //0xF8二进制: 11111000
                if(bs[3] == (byte)0x80 || bs[3] == (byte)0x81){
                    return true ;
                }else{
                    return false ;
                }
            }else{
                return false ;
            }
        }else{
            return false ;
        }
    }
 
    /**
     * 检查协议类型
     * @param bs 上行字节数组
     * @return 协议类型
     * @throws Exception 异常
     */
    public Boolean protocolType_p206TrueUgFalse(byte[] bs){
        if(bs == null){
            return null ;
        }else if(bs.length >= (ProtocolConstantV206V202404.ctrlIndex)
                && bs[0] == ProtocolConstantV206V202404.P_Head_Byte
                && bs[2] == ProtocolConstantV206V202404.P_Head_Byte){
            return true ;
        }else if(bs.length >= (ProtocolConstantV206V202404.UG_codeIndex)
                && bs[0] == ProtocolConstantV206V202404.UG_P_Head_Byte
                && bs[3] == ProtocolConstantV206V202404.UG_P_Head_Byte){
            return false ;
        }else{
            return null ;
        }
    }
 
    /**
     * 检查尾
     * @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 parseDataLen4P202404(byte[] bs)throws Exception{
        return parseFrameLen(bs) ;
    }
 
    /**
     * 分析用户数据域字节数(升级协议)
     * @param bs 上行字节数组
     * @return 数据长度
     * @throws Exception 异常
     */
    public int parseDataLen4Ug(byte[] bs)throws Exception{
        int len = ByteUtilUnsigned.bytes2Short_LE(bs, ProtocolConstantV206V2.UG_dataLenIndex_start) ;
        return len - ProtocolConstantV206V2.UG_lenCmd - ProtocolConstantV206V2.UG_lenRtuAddr ;
    }
 
 
    /**
     * 分析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 功能码
     */
    public String parseCode(byte[] bs, boolean p202404TrueUgFalse){
        if(p202404TrueUgFalse) {
            return ByteUtil.bytes2Hex(bs, false, ProtocolConstantV206V202404.codeIndex, 1);
        }else{
            return ByteUtil.bytes2Hex(bs, false, ProtocolConstantV206V202404.UG_codeIndex, 2);
        }
    }
    /**
     * 校验和检查
     * @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 ;
        }
    }
 
    /**
     * 校验和检查
     * @param bs  上行字节数组
     * @param p202404TrueUgFalse 202404协议为true,升级协议为false
     * @return 通过null,未通过返回原因
     * @throws Exception 异常
     */
    public String checkCrc_str(byte[] bs, boolean p202404TrueUgFalse) throws Exception {
        if(p202404TrueUgFalse){
            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 ;
            }
        }else{
            short crcCompute = new CRC16().CRC(bs, 0, bs.length - 4) ;
            short crcInBs = ByteUtil.bytes2Short_BE(bs,bs.length - 3) ;
            //int crcInBs = ByteUtilUnsigned.bytes2Short_BE(bs, bs.length - 3) ;
            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(0x20),表示此报文已被拆分为若干帧
        //FCB = 1(0x10),表示只发一次
        b = (byte)(b |0x10) ;
        //DIR = 0 下行,则功能码采用0
        return b ;
    }
 
    /**
     * 1个HEX
     * 0x01:正常刷卡开泵/阀用水
     * 0x02:平台远程开泵/阀用水
     * 0x08:用户远程开泵/阀用水
     * 0x0b:巡检卡,
     * 0x04:其它
     * @param type
     * @return
     */
    public static String openValveType(byte type){
        return switch (type) {
            case 1 -> "刷卡开阀";
            case 2 -> "中心站开阀";
            case 8 -> "用户远程开阀";
            case 11 -> "巡检卡开阀";
            case 4 -> "其它开阀";
            default -> "未知";
        };
    }
    /**
     * 0x01:刷卡开阀 => p206V1 1
     * 0x02:中心站开阀 => p206V1 3
     * 0x08:用户App远程开阀 => p206V1 3
     * 0x0B:巡检卡开阀 => p206V1 11
     * @param type
     * @return
     */
    public static byte openType2P206V1(byte type){
        return switch (type) {
            case 1 -> (byte)1 ;
            case 2 -> (byte)3 ;
            case 8 -> (byte)8 ;
            case 11 -> (byte)11 ;
            default -> (byte)-1;
        };
    }
 
    /**
     * 得到关开阀类型名称
     * 0x00:刷卡闭
     * 0x01:平台关
     * 0x02:APP关
     * 0x03:非法卡关
     * 0x04:水表通讯异常关
     * 0x05:电表异常关
     * 0x06:剩余水量不足关
     * 0x07:剩余金额为0关
     * 0x08:开泵/阀后管道没有流量关
     * 0x09:掉电再上电关
     * 0x0a:水表瞬时流量为0关
     * 0x0b:刷卡开泵,远程关
     * 0x0c:电池低电压关
     * @param type 字节
     * @return 名称
     */
    public static String closeValveType(byte type){
        return switch (type) {
            case 0 -> "刷卡关阀";
            case 1 -> "平台关阀";
            case 2 -> "APP关阀";
            case 3 -> "非法卡关阀";
            case 4 -> "水表通讯异常关阀";
            case 5 -> "电表异常关阀";
            case 6 -> "剩余水量不足关阀";
            case 7 -> "剩余金额为0关阀";
            case 8 -> "管道没有流量关阀";
            case 9 -> "掉电再上电关阀";
            case 10 -> "水表瞬时流量为0关阀";
            case 11 -> "刷卡开远程关阀";
            case 12 -> "电池低电压关阀";
            default -> "未知";
        };
    }
    /**
     * 得到关开阀类型名称
     * 0x00:刷卡闭 => p206V1 2
     * 0x01:平台关 => p206V1 4
     * 0x02:APP关 => p206V1 4
     * 0x03:非法卡关 => p206V1 12
     * 0x04:水表通讯异常关 => p206V1 6
     * 0x05:电表异常关 => p206V1 6
     * 0x06:剩余水量不足关 => p206V1 5
     * 0x07:剩余金额为0关 => p206V1 5
     * 0x08:开泵/阀后管道没有流量关 => p206V1 16
     * 0x09:掉电再上电关, => p206V1 7
     * 0x0a:水表瞬时流量为0关, => p206V1 5
     * 0x0b:刷卡开泵,远程关。 => p206V1 9
     * 0x0c:电池低电压关。 => p206V1 7
     * @param type 字节
     * @return 名称
     */
    public static byte closeType2P206V1(byte type){
        return switch (type) {
            case 0 -> (byte)2;
            case 1,11 -> (byte)4;
            case 2 -> (byte)9;
            case 3 -> (byte)12;
            case 4,5,10 -> (byte)6;
            case 6,7 -> (byte)5;
            case 8 -> (byte)16;
            case 9,12 -> (byte)7;
            default -> (byte)-1;
        };
    }
 
    public static String ctrlDevType(String hex){
        return switch (hex) {
            case "01" -> "测控一体阀";
            case "02" -> "表阀一体机";
            case "57" -> "井电控制器";
            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() ;
    }
    */
 
}