最近三个月的时间,做了几个小的项目,或者说是任务吧,都涉及到了协议的实现。也参与了协议的制定,在这里就先说说协议的实现吧。一开始做个是给出了已经实现的协议,我们就是要在这个协议上完成开发。先看一下协议的部分吧~。
数据帧头
|
x串口通道数据
|
y串口通道数据
|
…
|
…
|
4byte
|
N byte
|
M byte
|
|
|
数据帧头定义
控制字
|
当前ip包中承载的实际
串口数据通道数
|
数据包累加循环计数
|
1byte
|
1 byte
|
2 byte
|
00(H)
|
0~64
|
0~65535
|
某X 或 Y串口通道数据:
节点标识2
|
节点表识1
|
串口通道号
|
保留字节
|
承载有效数据长度N(低位)
|
承载有效数据长度N(高位)
|
data
|
1byte
|
1 byte
|
1 byte
|
1 byte
|
1 byte
|
1 byte
|
N x byte
|
00
|
0~64
|
1~8
|
00
|
xx
|
xx
|
0~65535
|
先看MsgPacket的实现:
package comm;
import java.nio.ByteBuffer;
import java.util.Date;
public class MsgPacket {
private int packetId;
private static final int MSGPACKET_LENGTH= 300; //设定每个包传输数据的最大长度
private ChannelData[] channelData;
private byte[] packetData; //消息包的数据字节信息
/**
* 根据输入的信息构造消息包,并将信息进行编码,用于数据的发送
* @param packetId 当前数据包的循环累计计数,即包标识
* @param channelData 当前数据包中的串口通道数据信息
*/
public MsgPacket(int packetId, ChannelData[] channelData){
this.packetId= packetId;
byte CONTROL_HEAD= 0x00;
byte CHANNEL_SUM= (byte)channelData.length;
byte[] PACKET_ID= getPacketId(packetId);
ByteBuffer dataBuffer= ByteBuffer.allocate(MSGPACKET_LENGTH);
dataBuffer.put(CONTROL_HEAD); //填加数据帧头控制字
dataBuffer.put(CHANNEL_SUM); //填加实际负载的串口通道数
dataBuffer.put(PACKET_ID); //填加当前包的循环累计计数
for(int i= 0; i< CHANNEL_SUM; i++){
dataBuffer.put(channelData[i].getMsgData()); //填加串口通道数据
}
dataBuffer.flip();
packetData= new byte[dataBuffer.limit()];
dataBuffer.get(packetData); //将串口通道数据信息放入字节数组
}
/**
* 使用接收到的字节数组构造消息包,用于数据的解析
* @param msg
*/
public MsgPacket(byte[] msg){
System.out.println(new Date());
byte CONTROL_HEAD= msg[0];
System.out.println("数据控制头信息: "+ CONTROL_HEAD);
byte CHANNEL_SUM= msg[1];
int PACKET_ID= byte2int(msg[2])+ byte2int(msg[3])* 256; //通过表示数据包累加计数的两位确定数据包标识
System.out.println("当前数据包标识: "+ PACKET_ID);
channelData= new ChannelData[CHANNEL_SUM];
int position= 4; //串口通道数据的起始位置
int data_sum= 0; //存放串口通道数据长度
for(int i= 1; i<= CHANNEL_SUM; i++){
int length= byte2int(msg[position+ 4])+ byte2int(msg[position+ 5])* 256;
ByteBuffer dataBuffer= ByteBuffer.allocate(length+ 6); //将当前通道数据存入字节缓冲区
for(int j= 0; j< dataBuffer.limit(); j++){
dataBuffer.put(msg[position+ j]);
}
dataBuffer.flip();
byte[] data= new byte[length+ 6]; //创建字节数组存放串口通道数据信息
// dataBuffer.get(data); //将字节缓冲区信息放入该数组
for(int j= 0; j< length+ 6; j++){
data[j]= dataBuffer.get(j);
}
ChannelData channel= new ChannelData(data); //将串口通道数据信息组成串口通道数据对象
channelData[i-1]= channel; //将该对象放入对象数组中
data_sum+= length; //计算串口通道数据信息的长度
position+= length+ 6* i; //计算下一个串口通道数据的起始位置
}
}
/**
* 将字节数据转换成整型数据
* @param value
* @return
*/
public int byte2int(byte value){
return value>=0? value: value+256;
}
/**
* 获得当前数据包循环累计计数的字节表示数组
* @param id
* @return
*/
public byte[] getPacketId(int id){
byte[] packetId= new byte[2];
if(id< 256){
packetId[0]= (byte)id; //低位在前,高位在后
packetId[1]= 0;
}else if(id> 256){
packetId[0]= (byte)(id& 0x00ff);
packetId[1]= (byte)((id& 0xff00)>>>8);
}
return packetId;
}
/**
* 返回当前数据包的循环累计计数
* @return
*/
public int getPacketId() {
return packetId;
}
/**
* 返回消息包发送数据信息的字节数组
* @return
*/
public byte[] getPacketData() {
return packetData;
}
/**
* 返回消息包的串口通道数据信息数组
* @return
*/
public ChannelData[] getChannelData() {
return channelData;
}
}
然后再看看ChannelData的实现:
package comm;
import java.nio.ByteBuffer;
public class ChannelData {
private int nodeID; //确定设备分布的基站编号
private byte channelID;
private static final byte CHANNEL_LENGTH= 100; //设置串口通道数据最大发送数据长度
private final byte RESERVE= 0X00;
private int length= 0; //存储串口通道数据信息的有效数据长度
private byte[] data; //存储串口通道数据信息
private String msg; //返回串口通道数据信息的字符串
/**
* 根据接收的字节数组创建串口通道数据信息
* @param data
*/
public ChannelData(byte[] data){
this.data= data;
this.nodeID= byte2int(data[0])+ byte2int(data[1])* 256;
this.channelID= data[2];
this.length= byte2int(data[4])+ byte2int(data[5])* 256;
StringBuffer strBuffer= new StringBuffer();
for(int i= 6; i< length+ 6; i++){
String value;
if(data[i]>= 16){
value= Integer.toHexString(data[i]).toUpperCase();
}else if(data[i]>= 0){
value= "0".concat(Integer.toHexString(data[i])).toUpperCase();
}else{
value= Integer.toHexString(data[i]).substring(6).toUpperCase(); //当数据小于0时转换成十六进制显示时取后两位数据位,忽略符号位
}
strBuffer.append(value+ " ");
}
this.msg= strBuffer.toString();
}
/**
* 根据输入发送数据创建串口通道数据
* @param node
* @param channelID
* @param codingData 发送数据信息编码后的字节数组
*/
public ChannelData(int nodeID, byte channelID, byte[] codingData){
this.nodeID= (byte)nodeID;
this.channelID= channelID;
this.length= codingData.length;
ByteBuffer byteBuffer= ByteBuffer.allocate(CHANNEL_LENGTH);
byteBuffer.put(int2byte(nodeID)); //填加节点标识
byteBuffer.put(channelID); //填加穿口通道号
byteBuffer.put(RESERVE); //填加保留字节
byteBuffer.put(int2byte(codingData.length)); //填加承载有效数据长度
byteBuffer.put(codingData); //将发送数据信息放入缓冲区
byteBuffer.flip();
data= new byte[byteBuffer.limit()];
byteBuffer.get(data); //将串口通道数据信息放入字节数组
}
/**
* 将字节数据转换成整型数据
* @param value
* @return
*/
public int byte2int(byte value){
return value>=0? value: value+256;
}
/**
* 将INT数据类型转换成字节数组
* @param id
* @return
*/
public byte[] int2byte(int id){
byte[] channelId= new byte[2];
if(id< 256){
channelId[0]= (byte)id;
channelId[1]= 0;
}else if(id> 256){
channelId[0]= (byte)(id& 0x00ff); //低位在前,高位在后
channelId[1]= (byte)((id& 0xff00)>>>8);
}
return channelId;
}
/**
* 返回发送数据信息的串口通道标识
* @return
*/
public int getNodeID() {
return nodeID;
}
/**
* 返回发送数据信息的串口通道标识
* @return
*/
public byte getChannelID() {
return channelID;
}
/**
* 返回发送数据信息的长度,去掉数据控制信息
* @return
*/
public int getLength() {
return length;
}
/**
* 获得串口通道数据信息的字符串表示
* @return
*/
public String getMsg() {
return msg;
}
/**
* 返回表示串口通道数据信息的字节数组
* @return
*/
public byte[] getMsgData() {
return data;
}
}
对于协议个部分,无非就是对数据的封装和解析,在对数据进行封装时,我主要采取使用参数构造对象,然后对参数按协议进行封装.至于对协议的解析,也是将字节数组作为参数构造对象,然后通过
调用该对象的方法就可以得到数据信息了~~