一:
首先,你需要下载一个额外的支持Java串口通信操作的jar包,由于java.comm比较老了,而且不支持64位系统,这里推荐Rxtx这个jar包(32位/64位均支持)。
官方下载地址:http://fizzed.com/oss/rxtx-for-java (注:可能需要FQ才能下载)
二:
根据Install.txt里的要求:
Copy RXTXcomm.jar ---> \jre\lib\ext
Copy rxtxSerial.dll ---> \jre\bin
Copy rxtxParallel.dll ---> \jre\bin
将文件拷贝到相应的地方去,同时将RXTXcomm.jar导入到eclipse中,build path
三:
关于该jar包的使用,我写了一个CommPortDemo.java类,该类提供关于串口通信的各简单服务,代码如下
package com.zhuyun.ui;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.TooManyListenersException;
import com.zhuyun.serialException.NoSuchPort;
import com.zhuyun.serialException.NotASerialPort;
import com.zhuyun.serialException.PortInUse;
import com.zhuyun.serialException.ReadDataFromSerialPortFailure;
import com.zhuyun.serialException.SendDataToSerialPortFailure;
import com.zhuyun.serialException.SerialPortInputStreamCloseFailure;
import com.zhuyun.serialException.SerialPortOutputStreamCloseFailure;
import com.zhuyun.serialException.SerialPortParameterFailure;
import com.zhuyun.serialException.TooManyListeners;
public class CommPortDemo {
public static Map<String, SerialPort> map = new HashMap<String, SerialPort>();
public static String[] listPortChoices() {
//列举出本机所有可用串口
CommPortIdentifier portId;
Enumeration en = CommPortIdentifier.getPortIdentifiers();
ArrayList<String> list = new ArrayList<String>();
// iterate through the ports.
while (en.hasMoreElements()) {
portId = (CommPortIdentifier) en.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
list.add(portId.getName());
}
}
String[] ports = new String[list.size()];
return list.toArray(ports);
}
/**
* 打开串口
* @param portName 端口名称
* @param baudrate 波特率
* @return 串口对象
*/
public static final SerialPort openPort(String portName) throws SerialPortParameterFailure, NotASerialPort, NoSuchPort, PortInUse{
try {
//通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
//打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 2000);
//判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
map.put(portName, serialPort);
try {
//设置一下串口的波特率等参数
serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
}
return serialPort;
}
else {
//不是串口
throw new NotASerialPort();
}
} catch (NoSuchPortException e1) {
throw new NoSuchPort();
} catch (PortInUseException e2) {
throw new PortInUse();
}
}
/**
* 关闭串口
* @param serialport 待关闭的串口对象
*/
public static void closePort(String portName) {
SerialPort serialPort = map.get(portName);
if (serialPort != null) {
serialPort.close();
serialPort = null;
}
}
/**
* 往串口发送数据
* @param serialPort 串口对象
* @param order 待发送数据
* @throws SendDataToSerialPortFailure 向串口发送数据失败
* @throws SerialPortOutputStreamCloseFailure 关闭串口对象的输出流出错
*/
public static void sendToPort(String portName, byte[] order) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure{
SerialPort serialPort = map.get(portName);
OutputStream out = null;
try {
out = serialPort.getOutputStream();
out.write(order);
out.flush();
} catch (IOException e) {
throw new SendDataToSerialPortFailure();
} finally {
try {
if (out != null) {
out.close();
out = null;
}
} catch (IOException e) {
throw new SerialPortOutputStreamCloseFailure();
}
}
}
/**
* 从串口读取数据
* @param serialPort 当前已建立连接的SerialPort对象
* @return 读取到的数据
* @throws ReadDataFromSerialPortFailure 从串口读取数据时出错
* @throws SerialPortInputStreamCloseFailure 关闭串口对象输入流出错
*/
public static byte[] readFromPort(String portName) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure{
SerialPort serialPort = map.get(portName);
InputStream in = null;
byte[] bytes = null;
try {
in = serialPort.getInputStream();
int bufflenth = in.available(); //获取buffer里的数据长度
while (bufflenth != 0) {
bytes = new byte[bufflenth]; //初始化byte数组为buffer中数据的长度
in.read(bytes);
bufflenth = in.available();
}
} catch (IOException e) {
throw new ReadDataFromSerialPortFailure();
} finally {
try {
if (in != null) {
in.close();
in = null;
}
} catch(IOException e) {
throw new SerialPortInputStreamCloseFailure();
}
}
return bytes;
}
/**
* 添加监听器
* @param port 串口对象
* @param listener 串口监听器
* @throws TooManyListeners 监听类对象过多
*/
public static void addListener(SerialPort port, SerialPortEventListener listener) throws TooManyListeners{
try {
//给串口添加监听器
port.addEventListener(listener);
//设置当有数据到达时唤醒监听接收线程
port.notifyOnDataAvailable(true);
//设置当通信中断时唤醒中断线程
port.notifyOnBreakInterrupt(true);
} catch (TooManyListenersException e) {
throw new TooManyListeners();
}
}
}
注:该类方法中 throw 的 Exception 都是我自定义的 Exception,之所以这么做是为了方便在主程序中进行相应处理,下面贴其中一个Exception出来给大家做下说明:
(注意我所有自定义的 Exception 都放在 serialException 包里)
package com.zhuyun.serialException;
public class SerialPortParameterFailure extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public SerialPortParameterFailure() {}
@Override
public String toString() {
return "设置串口参数失败!打开串口操作未完成!";
}
}
package com.zhuyun.serialException;
public class NoSuchPort extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public NoSuchPort(){}
@Override
public String toString() {
return "不存在该端口!";
}
}
package com.zhuyun.serialException;
public class NotASerialPort extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public NotASerialPort() {}
@Override
public String toString() {
return "不是一个串口!";
}
}
package com.zhuyun.serialException;
public class PortInUse extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public PortInUse(){}
@Override
public String toString() {
return "端口已在使用中";
}
}
package com.zhuyun.serialException;
public class ReadDataFromSerialPortFailure extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public ReadDataFromSerialPortFailure(){}
@Override
public String toString() {
return "从串口读取数据失败!";
}
}
package com.zhuyun.serialException;
public class SendDataToSerialPortFailure extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public SendDataToSerialPortFailure(){}
@Override
public String toString() {
return "发送数据到串口失败!";
}
}
package com.zhuyun.serialException;
public class SerialPortInputStreamCloseFailure extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public SerialPortInputStreamCloseFailure(){}
@Override
public String toString() {
return "串口输入流关闭失败!";
}
}
package com.zhuyun.serialException;
public class SerialPortOutputStreamCloseFailure extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public SerialPortOutputStreamCloseFailure(){}
@Override
public String toString() {
return "串口输出流关闭失败!";
}
}
package com.zhuyun.serialException;
public class TooManyListeners extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public TooManyListeners(){}
@Override
public String toString() {
return "太多监听器啦!";
}
}
每个自定义的Exception类我都重写了它的 toString() 方法,便于主程序捕捉到该Exception后打印对应的错误信息
补充:CSUM代码
package com.zhuyun.util;
/**
* 计算校验和
* @author zhouyinfei
*
*/
public class CSUM {
public static byte encode(byte[] byteArray){
int sum = 0;
for (byte b : byteArray) {
sum += b;
}
int sum2 = -sum;
byte csum = (byte) ((byte)sum2 & 0xFF);
return csum;
}
}
四:
主程序类的使用,UI.java里含有程序的入口地址(main方法),主界面功能比较简单,就是检测串口,向串口发送属性名和属性值,接收从串口发送过来的字节(不要问我为什么界面名称叫“泰坦”),简单的界面如下:
主界面代码如下:
package com.zhuyun.ui;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.*;
import com.zhuyun.data.receive.parse.DataParse;
import com.zhuyun.data.receive.parse.DataProcess;
import com.zhuyun.data.send.parse.DataPack;
import com.zhuyun.data.send.parse.IsNumeric;
import com.zhuyun.serialException.NoSuchPort;
import com.zhuyun.serialException.NotASerialPort;
import com.zhuyun.serialException.PortInUse;
import com.zhuyun.serialException.ReadDataFromSerialPortFailure;
import com.zhuyun.serialException.SendDataToSerialPortFailure;
import com.zhuyun.serialException.SerialPortInputStreamCloseFailure;
import com.zhuyun.serialException.SerialPortOutputStreamCloseFailure;
import com.zhuyun.serialException.SerialPortParameterFailure;
import com.zhuyun.serialException.TooManyListeners;
public class UI extends JFrame implements ActionListener, ItemListener{
private static final long serialVersionUID = 1L;
// 定义组件
JPanel jp1, jp2, jp3, jp4, jp5;// 面板
JLabel jlb1, jlb2, jlb3, jlb4;// 标签
JButton jb1, jb2, jb3, jb4;// 按钮
JTextArea jta;// 文本 框
JTextField jtf1, jtf2; // 文本
JComboBox<String> jcb1, jcb2; // 下拉菜单
static String[] ports;
int requestId = 1;
public static void main(String[] args) {
ports = CommPortDemo.listPortChoices();
new UI();
}
// 构造函数
public UI() {
// 创建面板
jp1 = new JPanel();
jp2 = new JPanel();
jp3 = new JPanel();
jp4 = new JPanel();
jp5 = new JPanel();
// 创建标签
jlb1 = new JLabel("串口");
jlb2 = new JLabel("attrName");
jlb3 = new JLabel("attrValue");
jlb4 = new JLabel("类型");
// 创建按钮
jb1 = new JButton("发送");
jb2 = new JButton("打开串口");
jb3 = new JButton("关闭串口");
jb4 = new JButton("清空日志");
//设置监听
jb1.addActionListener(this);
jb2.addActionListener(this);
jb3.addActionListener(this);
jb4.addActionListener(this);
// 创建文本
jtf1 = new JTextField(10);
jtf2 = new JTextField(10);
// 创建文本框
jta = new JTextArea(16,50);
//创建滚动框
JScrollPane jsp = new JScrollPane();
jsp.setViewportView(jta);
// 创建下拉菜单
// String[] a = { "COM1", "COM2" };
String[] b = { "消息类型String", "消息类型int", "时间请求" , "查询连接状态", "复位"};
jcb1 = new JComboBox<String>(ports);
jcb2 = new JComboBox<String>(b);
jcb2.addItemListener(this);
// 加入各个组件
JPanel jp6 = new JPanel();
jp6.add(jlb1);
jp6.add(jcb1);
jp1.add(jp6);
jp1.add(jb2);
jp1.add(jb3);
jp1.setLayout(new FlowLayout());
jp2.add(jlb2);
jp2.add(jtf1);
jp2.add(jlb3);
jp2.add(jtf2);
jp3.add(jlb4);
jp3.add(jcb2);
jp3.add(jb1);
jp5.add(jp1);
jp5.add(jp2);
jp5.add(jp3);
jp5.setLayout(new GridLayout(1, 3));
jp4.add(jb4);
jp4.add(jsp);
// 加入到JFrame
jp5.setBounds(0, 0, 600, 100);
jp4.setBounds(0, 120, 600, 550);
this.setLayout(null);
this.add(jp5);
this.add(jp4, BorderLayout.SOUTH);
// 设置布局管理
// this.setLayout(new GridLayout(2, 1));
// 设置窗体
this.setTitle("泰坦");// 窗体标签
this.setSize(600, 500);// 窗体大小
this.setLocationRelativeTo(null);// 在屏幕中间显示(居中显示)
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 退出关闭JFrame
this.setVisible(true);// 显示窗体
// 锁定窗体
this.setResizable(false);
}
@Override
public void actionPerformed(ActionEvent e) {
String attrName = jtf1.getText();
String attrValue = jtf2.getText();
String type = jcb2.getSelectedItem().toString();
String serialPort = jcb1.getSelectedItem().toString();
if (e.getActionCommand() == "发送") {
if (type.equals("消息类型int") && !IsNumeric.isNumeric(attrValue) ) {
JOptionPane.showMessageDialog(null,"属性类型和属性值不匹配!","提示消息",JOptionPane.WARNING_MESSAGE);
}else if ((attrName.isEmpty() || attrName.equals("") || attrValue.isEmpty() || attrValue.equals(""))
&& !type.equals("时间请求") && !type.equals("查询连接状态") && !type.equals("复位")) {
JOptionPane.showMessageDialog(null,"属性名称或属性值为空!","提示消息",JOptionPane.WARNING_MESSAGE);
}else {
try {
byte[] order = null;
if (type.equals("时间请求")) {
// order = DataPack.timeReqPack(requestId);
order = new byte[]{(byte) 0xDA, (byte) 0xAA, 0x1, 0x1, 0x0, 0x2, 0x8, (byte) 0xF4};
CommPortDemo.sendToPort(serialPort, order);
jta.append("发送:req_Id:" + requestId + " type=" + type +
" 到串口" + serialPort + "\n");
}else if(type.equals("查询连接状态")){
order = DataPack.stateReqPack(requestId);
CommPortDemo.sendToPort(serialPort, order);
jta.append("发送:req_Id:" + requestId + " type=" + type +
" 到串口" + serialPort + "\n");
}else if (type.equals("复位")) {
order = DataPack.resetReqPack(requestId);
CommPortDemo.sendToPort(serialPort, order);
jta.append("发送:req_Id:" + requestId + " type=" + type +
" 到串口" + serialPort + "\n");
}else if (type.equals("消息类型String")) {
order = DataPack.eventStringReqPack(requestId, attrName, attrValue);
CommPortDemo.sendToPort(serialPort, order);
jta.append("发送:req_Id:" + requestId + " attrName:" + attrName + " attrValue=" + attrValue + " type=" + type +
" 到串口" + serialPort + "\n");
}else if (type.equals("消息类型int")) {
order = DataPack.eventIntReqPack(requestId, attrName, Integer.parseInt(attrValue));
CommPortDemo.sendToPort(serialPort, order);
jta.append("发送:req_Id:" + requestId + " attrName:" + attrName + " attrValue=" + attrValue + " type=" + type +
" 到串口" + serialPort + "\n");
}
requestId ++;
} catch (SendDataToSerialPortFailure | SerialPortOutputStreamCloseFailure e1) {
JOptionPane.showMessageDialog(null, e1, "错误", JOptionPane.INFORMATION_MESSAGE);
}
}
}else if (e.getActionCommand() == "打开串口") {
try {
SerialPort openPort = CommPortDemo.openPort(serialPort);
CommPortDemo.addListener(openPort, new SerialListener()); //添加监听器
jta.append("打开串口:" + serialPort + "\n");
} catch (SerialPortParameterFailure | NotASerialPort | NoSuchPort | PortInUse | TooManyListeners e1) {
JOptionPane.showMessageDialog(null, e1, "错误", JOptionPane.INFORMATION_MESSAGE);
}
}else if (e.getActionCommand() == "关闭串口") {
CommPortDemo.closePort(serialPort);
jta.append("关闭串口:" + serialPort + "\n");
}else if (e.getActionCommand() == "清空日志"){
jta.setText("");
}
}
@Override
public void itemStateChanged(ItemEvent e)
{
if(e.getStateChange() == ItemEvent.SELECTED)
{
if (jcb2.getSelectedItem() == "消息类型String"
|| jcb2.getSelectedItem() == "消息类型int") {
jtf1.setEditable(true);
jtf2.setEditable(true);
}else {
jtf1.setEditable(false);
jtf2.setEditable(false);
}
}
}
private class SerialListener implements SerialPortEventListener {
String serialPort = jcb1.getSelectedItem().toString();
/**
* 处理监控到的串口事件
*/
@Override
public void serialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()) {
case : // 10 通讯中断
JOptionPane.showMessageDialog(null, "与串口设备通讯中断", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.OE: // 7 溢位(溢出)错误
JOptionPane.showMessageDialog(null, "位溢出错误", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.FE: // 9 帧错误
JOptionPane.showMessageDialog(null, "帧错误", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case : // 8 奇偶校验错误
JOptionPane.showMessageDialog(null, "奇偶校验错误", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case : // 6 载波检测
JOptionPane.showMessageDialog(null, "载波检测", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.CTS: // 3 清除待发送数据
JOptionPane.showMessageDialog(null, "清除待发送数据", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.DSR: // 4 待发送数据准备好了
JOptionPane.showMessageDialog(null, "待发送数据准备好了", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.RI: // 5 振铃指示
JOptionPane.showMessageDialog(null, "振铃指示", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 输出缓冲区已清空
JOptionPane.showMessageDialog(null, "输出缓冲区已清空", "错误", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用数据
byte[] data = null;
try {
if (serialPort == null) {
JOptionPane.showMessageDialog(null, "串口对象为空!监听失败!", "错误", JOptionPane.INFORMATION_MESSAGE);
}
else {
data = CommPortDemo.readFromPort(serialPort); //读取数据,存入字节数组
//System.out.println(new String(data));
// 自定义解析过程,你在实际使用过程中可以按照自己的需求在接收到数据后对数据进行解析
if (data == null || data.length < 1) { //检查数据是否读取正确
JOptionPane.showMessageDialog(null, "读取数据过程中未获取到有效数据!请检查设备或程序!", "错误", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
else { //按16进制打印
StringBuffer sb = new StringBuffer();
for (byte b : data) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append("0x" + hex.toUpperCase() + " ");
}
jta.append(serialPort + "收到数据:" + sb.toString() + "\n");
DataParse dp = new DataParse();
for (byte b : data) {
dp.parseData(b);
}
String result = DataProcess.processData(dp.packets);
jta.append(serialPort + "解析结果:" + result + "\n\n");
}
}
} catch (ReadDataFromSerialPortFailure | SerialPortInputStreamCloseFailure e) {
JOptionPane.showMessageDialog(null, e, "错误", JOptionPane.INFORMATION_MESSAGE);
System.exit(0); //发生读取错误时显示错误信息后退出系统
}
break;
}
}
}
}
下面是对各种请求的格式封装:
package com.zhuyun.data.send.parse;
import java.nio.ByteBuffer;
public class DataPack {
public static byte[] timeReqPack(int requestId){
byte[] byteArray = new byte[]{(byte) requestId, (byte) 0x1, (byte) 0x0, (byte) 0x2, (byte)0x8};
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.put((byte) 0xDA);
buffer.put((byte) 0xAA);
buffer.put(byteArray);
buffer.put(CSUM.encode(byteArray));
return buffer.array();
}
public static byte[] eventStringReqPack(int requestId, String attrName, String attrValue){
byte[] data = (attrName + "\n" + attrValue).getBytes(); //数据
int dataLength = data.length; //数据的长度
byte high = (byte) (((dataLength+1) >> 8) & 0xFF); //长度的高8位
byte low = (byte) ((dataLength+1) & 0xFF); //长度的低8位
byte[] byteArray = new byte[]{(byte) requestId, (byte)0x2, high, low};
ByteBuffer bb = ByteBuffer.allocate(4 + dataLength); //临时的ByteBuffer
bb.put(byteArray);
bb.put(data);
ByteBuffer buffer = ByteBuffer.allocate(7 + dataLength);
buffer.put((byte) 0xDA);
buffer.put((byte) 0xAA);
buffer.put(bb.array());
buffer.put(CSUM.encode(bb.array()));
return buffer.array();
}
public static byte[] eventIntReqPack(int requestId, String attrName, int attrValue){
byte[] intValue = new byte[]{ //整型转换成4个字节的byte[]
(byte) ((attrValue>>24) & 0xFF) ,
(byte) ((attrValue>>16) & 0xFF) ,
(byte) ((attrValue>>8) & 0xFF) ,
(byte) (attrValue & 0xFF)
};
int dataLength = attrName.length() + 1 + 4;
byte high = (byte) (((dataLength+1) >> 8) & 0xFF); //长度的高8位
byte low = (byte) ((dataLength+1) & 0xFF); //长度的低8位
byte[] byteArray = new byte[]{(byte) requestId, (byte)0x3, high, low};
ByteBuffer bb = ByteBuffer.allocate(4 + dataLength); //临时的ByteBuffer
bb.put(byteArray);
bb.put(attrName.getBytes());
bb.put((byte) '\n');
bb.put(intValue);
ByteBuffer buffer = ByteBuffer.allocate(7 + dataLength);
buffer.put((byte) 0xDA);
buffer.put((byte) 0xAA);
buffer.put(bb.array());
buffer.put(CSUM.encode(bb.array()));
return buffer.array();
}
public static byte[] stateReqPack(int requestId){
byte[] byteArray = new byte[]{(byte) requestId, (byte) 0x0, (byte) 0x0, (byte) 0x1};
ByteBuffer buffer = ByteBuffer.allocate(7);
buffer.put((byte) 0xDA);
buffer.put((byte) 0xAA);
buffer.put(byteArray);
buffer.put(CSUM.encode(byteArray)); //CSUM校验
return buffer.array();
}
public static byte[] resetReqPack(int requestId){
byte[] byteArray = new byte[]{(byte) requestId, (byte) 0x4, (byte) 0x0, (byte) 0x1};
ByteBuffer buffer = ByteBuffer.allocate(7);
buffer.put((byte) 0xDA);
buffer.put((byte) 0xAA);
buffer.put(byteArray);
buffer.put(CSUM.encode(byteArray));
return buffer.array();
}
}
下面是检测字符串是否为数字的方法:
package com.zhuyun.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class IsNumeric { //判断字符串是否为数字的方法
public static boolean isNumeric(String str){
Pattern pattern = Pattern.compile("^-?[0-9]+");
Matcher isNum = pattern.matcher(str);
if( !isNum.matches() ){
return false;
}
return true; }
}
下面是枚举类型
package com.zhuyun.data.receive.parse;
public enum ParseState {
PARSE_SYNC0,
PARSE_SYNC1,
PARSE_REQID,
PARSE_TYPE,
PARSE_LEN0,
PARSE_LEN1,
PARSE_DATA,
PARSE_CSUM;
}
下面是对于收到数据判断的状态机
package com.zhuyun.data.receive.parse;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import com.zhuyun.data.send.parse.CSUM;
public class DataParse {
public static final int ZOT_UART_DATA_MAX_LEN = 384;
private static ParseState parseState = ParseState.PARSE_SYNC0;
public List<byte[]> packets = new ArrayList<byte[]>();
public static byte[] packet;
private static byte reqId ;
private static byte type ;
private static byte lenHigh = 0;
private static byte lenLow = 0;
private static byte[] datas;
private static byte dataIndex;
private static int len = 0; //数据的长度
public void parseData(byte data){
switch (parseState) {
case PARSE_SYNC0:
if (data == (byte)0xDA) {
parseState = ParseState.PARSE_SYNC1;
}
break;
case PARSE_SYNC1:
if (data == (byte)0xAA) {
dataIndex = 0; //符合帧的格式,就把数据初始化为0
len = 0; //符合帧的格式,就把数据初始化为0
parseState = ParseState.PARSE_REQID;
}else {
parseState = ParseState.PARSE_SYNC0; //回到最初状态
}
break;
case PARSE_REQID:
reqId = data;
parseState = ParseState.PARSE_TYPE;
break;
case PARSE_TYPE:
type = data;
parseState = ParseState.PARSE_LEN0;
break;
case PARSE_LEN0:
lenHigh = data; //将高位长度暂时保存
parseState = ParseState.PARSE_LEN1;
break;
case PARSE_LEN1:
lenLow = data;
len = lenHigh * 256 + lenLow - 1; //减去csum的长度得到数据的长度
parseState = ParseState.PARSE_DATA;
if (len > ZOT_UART_DATA_MAX_LEN) { //数据超过了最大长度
parseState = ParseState.PARSE_SYNC0; //回到最初状态
}
datas = new byte[len]; //给数据段初始化
break;
case PARSE_DATA:
datas[dataIndex ++] = data;
if (dataIndex >= len) {
parseState = ParseState.PARSE_CSUM;
}
break;
case PARSE_CSUM:
byte[] b = new byte[]{reqId, type, lenHigh, lenLow};
ByteBuffer bf = ByteBuffer.allocate(4 + len);
bf.put(b);
bf.put(datas);
byte csum = CSUM.encode(bf.array());
if (csum == data) {
ByteBuffer bf2 = ByteBuffer.allocate(5 + len);
bf2.put(bf.array());
bf2.put(csum);
packet = bf2.array();
packets.add(packet);
}else {
System.out.println("csum error!uart_csum=" + (csum&0xFF));
}
parseState = ParseState.PARSE_SYNC0; //回到最初状态
break;
default:
break;
}
}
}
下面是对于状态机过滤后的数据的处理:
package com.zhuyun.data.receive.parse;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import net.sf.json.JSONObject;
public class DataProcess {
public static String processData(List<byte[]> packets){
List<String> list = new ArrayList<String>();
byte state;
int dataLen;
ByteBuffer bf;
for (byte[] packet : packets) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("reqId", packet[0] & 0xFF);
switch (packet[1]) {
case (byte) 0x80:
jsonObject.put("CMD", "串口帧格式错误");
break;
case (byte) 0x81:
jsonObject.put("CMD", "查询连接状态的响应");
state = packet[4];
if (state == 0x0) {
jsonObject.put("state", "模组和服务器连接断开");
} else if(state == 0x1){
jsonObject.put("state", "模组和服务器连接正常");
}
break;
case (byte) 0x82:
jsonObject.put("CMD", "模组和服务器连接状态发生变化(主动)");
state = packet[4];
if (state == 0x0) {
jsonObject.put("state", "模组和服务器连接断开");
} else if(state == 0x1){
jsonObject.put("state", "模组和服务器连接正常");
}
break;
case (byte) 0x83:
jsonObject.put("CMD", "时间请求的响应");
int year = packet[4] & 0xFF;
int month = packet[5] & 0xFF;
int day = packet[6] & 0xFF;
int hour = packet[7] & 0xFF;
int minute = packet[8] & 0xFF;
int second = packet[9] & 0xFF;
jsonObject.put("time", year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second);
break;
case (byte) 0x84:
jsonObject.put("CMD", "向开发者发送字符串类型的属性(主动)");
dataLen = (packet[2] & 0xFF)*16 + (packet[3]&0xFF) -1;
bf = ByteBuffer.allocate(dataLen);
for (int i = 4; i <= 4 + dataLen; i++) {
bf.put(packet[i]);
}
String data = new String(bf.array());
jsonObject.put("StringData", data);
break;
case (byte) 0x85:
jsonObject.put("CMD", "向开发者发送int类型的属性(主动)");
dataLen = (packet[2] & 0xFF)*16 + (packet[3]&0xFF) -1;
bf = ByteBuffer.allocate(dataLen); //所有数据段内容
for (int i = 4; i <= 4 + dataLen; i++) {
bf.put(packet[i]);
}
byte[] attrName = new byte[dataLen-5]; //属性名
bf.get(attrName, 0, dataLen-5);
byte[] attrValue = new byte[4]; //属性值(int)
bf.get(attrValue, dataLen-4, 4);
String name = new String(attrName);
int value = ((attrValue[0] & 0xFF)<<24) +
((attrValue[1] & 0xFF)<<16) +
((attrValue[2] & 0xFF)<<8) +
(attrValue[3] & 0xFF);
jsonObject.put("IntData", name + ":" + value);
break;
case (byte) 0x86:
jsonObject.put("CMD", "event请求的响应");
state = packet[4];
if (state == 0x0) {
jsonObject.put("state", "发送属性请求失败");
} else if(state == 0x1){
jsonObject.put("state", "发送属性请求成功");
}
break;
case (byte) 0x87:
jsonObject.put("CMD", "复位请求的响应");
state = packet[4];
if (state == 0x0) {
jsonObject.put("state", "复位失败失败");
} else if(state == 0x1){
jsonObject.put("state", "复位成功成功");
}
break;
default:
break;
}
list.add(jsonObject.toString());
}
return list.toString();
}
}