数据帧格式如下表所示,数据帧开始标志和帧结束标志为FFH,其他字段不能出现FFH,如果数据确实为FFH,需对其进行转义处理。
发送数据时,如果在其它字段中出现FFH字节时,将FFH分解为FEH和01H这两个字节发送,如果在其它字段出现FEH字节时,需将FEH分解为FEH和00H这两个字节来发送。
接收数据时,如果出现“FE 01”这样连续两个字节时将之合为一个字节FFH;如果出现“FE 00”这样连续两个字节时将之合为一个字节FEH。
注:发送数据帧时:先校验,后做转义处理;接收数据帧时:先做转义处理,再做校验。
/**
* 指令内容转义工具类
*/
public class ESCUtil {
/**
* 数据帧编码,添加帧头,添加帧尾,添加校验,校验方式:异或校验
*
* @param ibuf 传入数组,不含帧序号
* @param fram_num_yn 是否添加帧序号,0:添加帧序号,非0:不添加帧序号
* @return
*/
public static byte[] ptkCode(byte[] ibuf, int fram_num_yn) {
/**
* 校验位
*/
byte chk;
/**
* 数组下标
*/
int slen = 0;
/**
* 转义前数组长度
*/
int ilen;
/**
* 帧内容数据最大长度
*/
int SENDBUF_SIZE = 36;
/**
* 转义后的数组
*/
byte[] returnBytes;
/**
* 中间量,动态添加
*/
List<Byte> temp;
/**
* 传入参数检验
*/
if (ibuf == null) {
throw new IllegalInstructException("解析数据为空");
}
ilen = ibuf.length;
//检查数据长度是否正确
if (ilen > SENDBUF_SIZE) {
throw new IllegalInstructException("解析数据长度不正确");//发送字节数超长
}
temp = new ArrayList<>();
//加入帧头
// temp[slen++] = -1;
// temp[slen++] = -1;
temp.add((byte) -1);
temp.add((byte) -1);
//添加帧序号
if (fram_num_yn == 0) {
temp.add((byte) 19);
chk = 19;
} else {
chk = 00;
}
//拷贝数据,转义
for (int i = temp.size(); i < ilen - 2; i++) {
chk ^= ibuf[i];//异或校验
if (ibuf[i] == -1) {
temp.add((byte) -2);
temp.add((byte) 1);
} else if (ibuf[i] == -2) {
temp.add((byte) -2);
temp.add((byte) 0);
} else {
temp.add(ibuf[i]);
}
}
// 校验位编码
if (chk == -1) {
temp.add((byte) -2);
temp.add((byte) 1);
} else if (chk == -2) {
temp.add((byte) -2);
temp.add((byte) 0);
} else {
temp.add(chk);
}
// 添加帧尾
temp.add((byte) -1);
Object[] objs = temp.toArray();
returnBytes = new byte[objs.length];
for (int i = 0; i < objs.length; i++) {
Byte bbyte = (Byte) objs[i];
returnBytes[i] = bbyte.byteValue();
}
return returnBytes;
}
/**
* 解析上传指令:进行数据包的解码工作,去头去尾,通过校验码判断数据包数据是否正确
*
* @param ibuf 传入的回复指令数组
* @param fram_num_yn 解码后的帧内容是否包含帧序号,0:不包含帧序号,非0:包含帧序号
* @throws Exception
*/
public static byte[] pktDecode(byte[] ibuf, int fram_num_yn) {
/**
*转义前原始指令数组长度
*/
int ilen;
/**
* 输入指令最大长度:全部为转义指令时
*/
int PARSE_FRM_MAXLEN = 80;
/**
* 转义后帧内容长度
*/
int SENDBUF_SIZE = 41;
/**
* 转移后帧内容
*/
byte[] ReceiveBuf = new byte[SENDBUF_SIZE];
/**
* 回复指令下标
*/
int slen = 0;
/**
* 校验码
*/
byte Chk = 0;
/**
*帧内容开始位置
*/
char start = 0;
/**
* 输出帧内容数组,不包含/帧序号
*/
byte[] obuf;
/**
* 临时变量
*/
List<Byte> tempList;
if (ibuf == null) {
throw new IllegalInstructException("传入数据为空");
}
ilen = ibuf.length;
// 原始帧长度至少为5:2个字节帧头,1 个字节帧序号,1个字节校验位,1个字节帧尾
if ((ilen < 5) || (ilen > PARSE_FRM_MAXLEN)) {
throw new IllegalInstructException("解析数据长度不正确");
}
//检验帧头和者帧尾
if (-1 != ibuf[0] || -1 != ibuf[ibuf.length - 1]) {
throw new IllegalInstructException("无效的帧头或帧尾");
}
//检验是否包含帧序号,生成不同长度的帧内容数组
if (fram_num_yn != 0) {
obuf = new byte[SENDBUF_SIZE];
} else {
obuf = new byte[SENDBUF_SIZE - 1];
}
//帧内容开始位置下标
if (ibuf[2] == -1) {
start = 3;
} else if (ibuf[1] == -1) {
start = 2;
} else {
start = 1;
}
//取帧内容,去除帧头,帧尾
for (int i = start; i < ibuf.length - 1; i++) {
ReceiveBuf[slen] = ibuf[i];
//转义操作:或
if (-2 == ibuf[i]) {
ReceiveBuf[slen] |= ibuf[i + 1];
i++;
}
slen++;
}
//异或校验码
for (int i = 0; i < slen - 1; i++) {
Chk ^= ReceiveBuf[i];
}
//验证校验码
if (Chk == ReceiveBuf[slen - 1]) {
if (fram_num_yn != 0) {//包含帧序号
for (int i = 0; i < slen; i++) {
obuf[i] = ReceiveBuf[i];
}
} else {//不包含帧序号{
for (int i = 1; i < slen; i++) {
obuf[i - 1] = ReceiveBuf[i];
}
}
//增加帧头、帧尾
byte[] out = new byte[obuf.length + start + 1];
for (int i = 0; i < start; i++) {
out[i] = -1;
}
System.arraycopy(obuf, 0, out, 2, obuf.length);
out[out.length - 1] = -1;
return out;
} else {
throw new IllegalInstructException("校验失败!");
}
}
}