基于Java編寫串口通信工具
最近一門課要求編寫一個上位機(jī)串口通信工具,我基于Java編寫了一個帶有圖形界面的簡單串口通信工具,下面詳述一下過程,供大家參考 ^_^
一:
首先,你需要下載一個額外的支持Java串口通信操作的jar包,由于java.comm比較老了,而且不支持64位系統(tǒng),這里推薦Rxtx這個jar包(32位/64位均支持)。
官方下載地址:http://fizzed.com/oss/rxtx-for-java (注:可能需要FQ才能下載)
不能FQ的童鞋,可以在這里下載:
http://xiazai.jb51.net/201612/yuanma/javamfzrxtx(jb51.net).rar(32位)
http://xiazai.jb51.net/201612/yuanma/javamfzrxtx(jb51.net).rar(64位)
二:
下載解壓jar包并在 Java Build Path 下引入:
捕獲

注:如果運(yùn)行過程中拋出 java.lang.UnsatisfiedLinkError 錯誤,請將rxtx解壓包中的 rxtxParallel.dll,rxtxSerial.dll 這兩個文件復(fù)制到 C:\Windows\System32 目錄下即可解決該錯誤。
三:
關(guān)于該jar包的使用,我寫了一個SerialTool.java類,該類提供關(guān)于串口通信的各簡單服務(wù),代碼如下(注意該類位于 serialPort 包里):
package serialPort;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.TooManyListenersException;
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 serialException.*;
/**
* 串口服務(wù)類,提供打開、關(guān)閉串口,讀取、發(fā)送串口數(shù)據(jù)等服務(wù)(采用單例設(shè)計模式)
* @author zhong
*
*/
public class SerialTool {
private static SerialTool serialTool = null;
static {
//在該類被ClassLoader加載時就初始化一個SerialTool對象
if (serialTool == null) {
serialTool = new SerialTool();
}
}
//私有化SerialTool類的構(gòu)造方法,不允許其他類生成SerialTool對象
private SerialTool() {}
/**
* 獲取提供服務(wù)的SerialTool對象
* @return serialTool
*/
public static SerialTool getSerialTool() {
if (serialTool == null) {
serialTool = new SerialTool();
}
return serialTool;
}
/**
* 查找所有可用端口
* @return 可用端口名稱列表
*/
public static final ArrayList<String> findPort() {
//獲得當(dāng)前所有可用串口
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
ArrayList<String> portNameList = new ArrayList<>();
//將可用串口名添加到List并返回該List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
/**
* 打開串口
* @param portName 端口名稱
* @param baudrate 波特率
* @return 串口對象
* @throws SerialPortParameterFailure 設(shè)置串口參數(shù)失敗
* @throws NotASerialPort 端口指向設(shè)備不是串口類型
* @throws NoSuchPort 沒有該端口對應(yīng)的串口設(shè)備
* @throws PortInUse 端口已被占用
*/
public static final SerialPort openPort(String portName, int baudrate) 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;
try {
//設(shè)置一下串口的波特率等參數(shù)
serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
throw new SerialPortParameterFailure();
}
//System.out.println("Open " + portName + " sucessfully !");
return serialPort;
}
else {
//不是串口
throw new NotASerialPort();
}
} catch (NoSuchPortException e1) {
throw new NoSuchPort();
} catch (PortInUseException e2) {
throw new PortInUse();
}
}
/**
* 關(guān)閉串口
* @param serialport 待關(guān)閉的串口對象
*/
public static void closePort(SerialPort serialPort) {
if (serialPort != null) {
serialPort.close();
serialPort = null;
}
}
/**
* 往串口發(fā)送數(shù)據(jù)
* @param serialPort 串口對象
* @param order 待發(fā)送數(shù)據(jù)
* @throws SendDataToSerialPortFailure 向串口發(fā)送數(shù)據(jù)失敗
* @throws SerialPortOutputStreamCloseFailure 關(guān)閉串口對象的輸出流出錯
*/
public static void sendToPort(SerialPort serialPort, byte[] order) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure {
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();
}
}
}
/**
* 從串口讀取數(shù)據(jù)
* @param serialPort 當(dāng)前已建立連接的SerialPort對象
* @return 讀取到的數(shù)據(jù)
* @throws ReadDataFromSerialPortFailure 從串口讀取數(shù)據(jù)時出錯
* @throws SerialPortInputStreamCloseFailure 關(guān)閉串口對象輸入流出錯
*/
public static byte[] readFromPort(SerialPort serialPort) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure {
InputStream in = null;
byte[] bytes = null;
try {
in = serialPort.getInputStream();
int bufflenth = in.available(); //獲取buffer里的數(shù)據(jù)長度
while (bufflenth != 0) {
bytes = new byte[bufflenth]; //初始化byte數(shù)組為buffer中數(shù)據(jù)的長度
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;
}
/**
* 添加監(jiān)聽器
* @param port 串口對象
* @param listener 串口監(jiān)聽器
* @throws TooManyListeners 監(jiān)聽類對象過多
*/
public static void addListener(SerialPort port, SerialPortEventListener listener) throws TooManyListeners {
try {
//給串口添加監(jiān)聽器
port.addEventListener(listener);
//設(shè)置當(dāng)有數(shù)據(jù)到達(dá)時喚醒監(jiān)聽接收線程
port.notifyOnDataAvailable(true);
//設(shè)置當(dāng)通信中斷時喚醒中斷線程
port.notifyOnBreakInterrupt(true);
} catch (TooManyListenersException e) {
throw new TooManyListeners();
}
}
}
注:該類方法中 throw 的 Exception 都是我自定義的 Exception,之所以這么做是為了方便在主程序中進(jìn)行相應(yīng)處理,下面貼其中一個Exception出來給大家做下說明:
(注意我所有自定義的 Exception 都放在 serialException 包里)
package serialException;
public class SerialPortParameterFailure extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public SerialPortParameterFailure() {}
@Override
public String toString() {
return "設(shè)置串口參數(shù)失?。〈蜷_串口操作未完成!";
}
}
每個自定義的Exception類我都重寫了它的 toString() 方法,便于主程序捕捉到該Exception后打印對應(yīng)的錯誤信息
其中在serialException包里還有一個專門將接收到的Exception對象內(nèi)的錯誤信息提取出來轉(zhuǎn)換成字符串并返回的類,代碼如下:
package serialException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 負(fù)責(zé)將傳入的Exception中的錯誤信息提取出來并轉(zhuǎn)換成字符串;
* @author zhong
*
*/
public class ExceptionWriter {
/**
* 將Exception中的錯誤信息封裝到字符串中并返回該字符串
* @param e 包含錯誤的Exception
* @return 錯誤信息字符串
*/
public static String getErrorInfoFromException(Exception e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
e.printStackTrace(pw);
return "\r\n" + sw.toString() + "\r\n";
} catch (Exception e2) {
return "出錯啦!未獲取到錯誤信息,請檢查后重試!";
} finally {
try {
if (pw != null) {
pw.close();
}
if (sw != null) {
sw.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
四:
主程序類的使用,Client.java里含有程序的入口地址(main方法),它的作用是顯示一個歡迎界面并調(diào)用DataView.java這個類進(jìn)行實(shí)際的串口數(shù)據(jù)顯示。
Client.java代碼如下:
package serialPort;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Label;
import java.awt.Panel;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JOptionPane;
import serialException.ExceptionWriter;
/**
* 主程序
* @author zhong
*
*/
public class Client extends Frame{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 程序界面寬度
*/
public static final int WIDTH = 800;
/**
* 程序界面高度
*/
public static final int HEIGHT = 620;
/**
* 程序界面出現(xiàn)位置(橫坐標(biāo))
*/
public static final int LOC_X = 200;
/**
* 程序界面出現(xiàn)位置(縱坐標(biāo))
*/
public static final int LOC_Y = 70;
Color color = Color.WHITE;
Image offScreen = null; //用于雙緩沖
//設(shè)置window的icon(這里我自定義了一下Windows窗口的icon圖標(biāo),因?yàn)閷?shí)在覺得哪個小咖啡圖標(biāo)不好看 = =)
Toolkit toolKit = getToolkit();
Image icon = toolKit.getImage(Client.class.getResource("computer.png"));
//持有其他類
DataView dataview = new DataView(this); //主界面類(顯示監(jiān)控數(shù)據(jù)主面板)
/**
* 主方法
* @param args //
*/
public static void main(String[] args) {
new Client().launchFrame();
}
/**
* 顯示主界面
*/
public void launchFrame() {
this.setBounds(LOC_X, LOC_Y, WIDTH, HEIGHT); //設(shè)定程序在桌面出現(xiàn)的位置
this.setTitle("CDIO工程項(xiàng)目"); //設(shè)置程序標(biāo)題
this.setIconImage(icon);
this.setBackground(Color.white); //設(shè)置背景色
this.addWindowListener(new WindowAdapter() {
//添加對窗口狀態(tài)的監(jiān)聽
public void windowClosing(WindowEvent arg0) {
//當(dāng)窗口關(guān)閉時
System.exit(0); //退出程序
}
});
this.addKeyListener(new KeyMonitor()); //添加鍵盤監(jiān)聽器
this.setResizable(false); //窗口大小不可更改
this.setVisible(true); //顯示窗口
new Thread(new RepaintThread()).start(); //開啟重畫線程
}
/**
* 畫出程序界面各組件元素
*/
public void paint(Graphics g) {
Color c = g.getColor();
g.setFont(new Font("微軟雅黑", Font.BOLD, 40));
g.setColor(Color.black);
g.drawString("歡迎使用上位機(jī)實(shí)時監(jiān)控系統(tǒng)", 45, 190);
g.setFont(new Font("微軟雅黑", Font.ITALIC, 26));
g.setColor(Color.BLACK);
g.drawString("Version:1.0 Powered By:ZhongLei", 280, 260);
g.setFont(new Font("微軟雅黑", Font.BOLD, 30));
g.setColor(color);
g.drawString("————點(diǎn)擊Enter鍵進(jìn)入主界面————", 100, 480);
//使文字 "————點(diǎn)擊Enter鍵進(jìn)入主界面————" 黑白閃爍
if (color == Color.WHITE) color = Color.black;
else if (color == color.BLACK) color = Color.white;
}
/**
* 雙緩沖方式重畫界面各元素組件
*/
public void update(Graphics g) {
if (offScreen == null) offScreen = this.createImage(WIDTH, HEIGHT);
Graphics gOffScreen = offScreen.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.white);
gOffScreen.fillRect(0, 0, WIDTH, HEIGHT); //重畫背景畫布
this.paint(gOffScreen); //重畫界面元素
gOffScreen.setColor(c);
g.drawImage(offScreen, 0, 0, null); //將新畫好的畫布“貼”在原畫布上
}
/*
* 內(nèi)部類形式實(shí)現(xiàn)對鍵盤事件的監(jiān)聽
*/
private class KeyMonitor extends KeyAdapter {
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_ENTER) { //當(dāng)監(jiān)聽到用戶敲擊鍵盤enter鍵后執(zhí)行下面的操作
setVisible(false); //隱去歡迎界面
dataview.setVisible(true); //顯示監(jiān)測界面
dataview.dataFrame(); //初始化監(jiān)測界面
}
}
}
/*
* 重畫線程(每隔250毫秒重畫一次)
*/
private class RepaintThread implements Runnable {
public void run() {
while(true) {
repaint();
try {
Thread.sleep(250);
} catch (InterruptedException e) {
//重畫線程出錯拋出異常時創(chuàng)建一個Dialog并顯示異常詳細(xì)信息
String err = ExceptionWriter.getErrorInfoFromException(e);
JOptionPane.showMessageDialog(null, err, "錯誤", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
}
}
}
}
運(yùn)行截圖:
注:實(shí)際運(yùn)行過程中最下面的“點(diǎn)擊Enter鍵進(jìn)入主界面”有一個一閃一閃的效果(是通過每隔一段時間重畫一次界面,讓這句話以白黑兩色反復(fù)交替出現(xiàn)實(shí)現(xiàn)的),雙緩沖方式利于解決重畫時界面閃爍的問題(如果不使用雙緩沖方式的話相當(dāng)于每次重畫時是在舊界面上一點(diǎn)一點(diǎn)畫上新東西,而雙緩沖實(shí)質(zhì)上是通過先在內(nèi)存中直接畫好一張新界面圖,然后一次性直接用新界面覆蓋掉舊界面)

DataView.java代碼如下:(該類用于實(shí)時顯示串口數(shù)據(jù))
簡單說明:
硬件設(shè)備每隔一段時間通過串口發(fā)送一次數(shù)據(jù)到計算機(jī),該串口工具成功連接至硬件設(shè)備并添加監(jiān)聽后,會在每次接收到數(shù)據(jù)時解析數(shù)據(jù)并更新界面;
你在使用時很可能需求跟我不一樣,該類僅供參考,實(shí)際使用中你很可能需要重新制作數(shù)據(jù)顯示界面以及數(shù)據(jù)解析方式
package serialPort;
import java.awt.Button;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Label;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.TooManyListenersException;
import javax.swing.JOptionPane;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import serialException.*;
/**
* 監(jiān)測數(shù)據(jù)顯示類
* @author Zhong
*
*/
public class DataView extends Frame {
/**
*
*/
private static final long serialVersionUID = 1L;
Client client = null;
private List<String> commList = null; //保存可用端口號
private SerialPort serialPort = null; //保存串口對象
private Font font = new Font("微軟雅黑", Font.BOLD, 25);
private Label tem = new Label("暫無數(shù)據(jù)", Label.CENTER); //溫度
private Label hum = new Label("暫無數(shù)據(jù)", Label.CENTER); //濕度
private Label pa = new Label("暫無數(shù)據(jù)", Label.CENTER); //壓強(qiáng)
private Label rain = new Label("暫無數(shù)據(jù)", Label.CENTER); //雨量
private Label win_sp = new Label("暫無數(shù)據(jù)", Label.CENTER); //風(fēng)速
private Label win_dir = new Label("暫無數(shù)據(jù)", Label.CENTER); //風(fēng)向
private Choice commChoice = new Choice(); //串口選擇(下拉框)
private Choice bpsChoice = new Choice(); //波特率選擇
private Button openSerialButton = new Button("打開串口");
Image offScreen = null; //重畫時的畫布
//設(shè)置window的icon
Toolkit toolKit = getToolkit();
Image icon = toolKit.getImage(DataView.class.getResource("computer.png"));
/**
* 類的構(gòu)造方法
* @param client
*/
public DataView(Client client) {
this.client = client;
commList = SerialTool.findPort(); //程序初始化時就掃描一次有效串口
}
/**
* 主菜單窗口顯示;
* 添加Label、按鈕、下拉條及相關(guān)事件監(jiān)聽;
*/
public void dataFrame() {
this.setBounds(client.LOC_X, client.LOC_Y, client.WIDTH, client.HEIGHT);
this.setTitle("CDIO工程項(xiàng)目");
this.setIconImage(icon);
this.setBackground(Color.white);
this.setLayout(null);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent arg0) {
if (serialPort != null) {
//程序退出時關(guān)閉串口釋放資源
SerialTool.closePort(serialPort);
}
System.exit(0);
}
});
tem.setBounds(140, 103, 225, 50);
tem.setBackground(Color.black);
tem.setFont(font);
tem.setForeground(Color.white);
add(tem);
hum.setBounds(520, 103, 225, 50);
hum.setBackground(Color.black);
hum.setFont(font);
hum.setForeground(Color.white);
add(hum);
pa.setBounds(140, 193, 225, 50);
pa.setBackground(Color.black);
pa.setFont(font);
pa.setForeground(Color.white);
add(pa);
rain.setBounds(520, 193, 225, 50);
rain.setBackground(Color.black);
rain.setFont(font);
rain.setForeground(Color.white);
add(rain);
win_sp.setBounds(140, 283, 225, 50);
win_sp.setBackground(Color.black);
win_sp.setFont(font);
win_sp.setForeground(Color.white);
add(win_sp);
win_dir.setBounds(520, 283, 225, 50);
win_dir.setBackground(Color.black);
win_dir.setFont(font);
win_dir.setForeground(Color.white);
add(win_dir);
//添加串口選擇選項(xiàng)
commChoice.setBounds(160, 397, 200, 200);
//檢查是否有可用串口,有則加入選項(xiàng)中
if (commList == null || commList.size()<1) {
JOptionPane.showMessageDialog(null, "沒有搜索到有效串口!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
}
else {
for (String s : commList) {
commChoice.add(s);
}
}
add(commChoice);
//添加波特率選項(xiàng)
bpsChoice.setBounds(526, 396, 200, 200);
bpsChoice.add("1200");
bpsChoice.add("2400");
bpsChoice.add("4800");
bpsChoice.add("9600");
bpsChoice.add("14400");
bpsChoice.add("19200");
bpsChoice.add("115200");
add(bpsChoice);
//添加打開串口按鈕
openSerialButton.setBounds(250, 490, 300, 50);
openSerialButton.setBackground(Color.lightGray);
openSerialButton.setFont(new Font("微軟雅黑", Font.BOLD, 20));
openSerialButton.setForeground(Color.darkGray);
add(openSerialButton);
//添加打開串口按鈕的事件監(jiān)聽
openSerialButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//獲取串口名稱
String commName = commChoice.getSelectedItem();
//獲取波特率
String bpsStr = bpsChoice.getSelectedItem();
//檢查串口名稱是否獲取正確
if (commName == null || commName.equals("")) {
JOptionPane.showMessageDialog(null, "沒有搜索到有效串口!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
}
else {
//檢查波特率是否獲取正確
if (bpsStr == null || bpsStr.equals("")) {
JOptionPane.showMessageDialog(null, "波特率獲取錯誤!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
}
else {
//串口名、波特率均獲取正確時
int bps = Integer.parseInt(bpsStr);
try {
//獲取指定端口名及波特率的串口對象
serialPort = SerialTool.openPort(commName, bps);
//在該串口對象上添加監(jiān)聽器
SerialTool.addListener(serialPort, new SerialListener());
//監(jiān)聽成功進(jìn)行提示
JOptionPane.showMessageDialog(null, "監(jiān)聽成功,稍后將顯示監(jiān)測數(shù)據(jù)!", "提示", JOptionPane.INFORMATION_MESSAGE);
} catch (SerialPortParameterFailure | NotASerialPort | NoSuchPort | PortInUse | TooManyListeners e1) {
//發(fā)生錯誤時使用一個Dialog提示具體的錯誤信息
JOptionPane.showMessageDialog(null, e1, "錯誤", JOptionPane.INFORMATION_MESSAGE);
}
}
}
}
});
this.setResizable(false);
new Thread(new RepaintThread()).start(); //啟動重畫線程
}
/**
* 畫出主界面組件元素
*/
public void paint(Graphics g) {
Color c = g.getColor();
g.setColor(Color.black);
g.setFont(new Font("微軟雅黑", Font.BOLD, 25));
g.drawString(" 溫度: ", 45, 130);
g.setColor(Color.black);
g.setFont(new Font("微軟雅黑", Font.BOLD, 25));
g.drawString(" 濕度: ", 425, 130);
g.setColor(Color.black);
g.setFont(new Font("微軟雅黑", Font.BOLD, 25));
g.drawString(" 壓強(qiáng): ", 45, 220);
g.setColor(Color.black);
g.setFont(new Font("微軟雅黑", Font.BOLD, 25));
g.drawString(" 雨量: ", 425, 220);
g.setColor(Color.black);
g.setFont(new Font("微軟雅黑", Font.BOLD, 25));
g.drawString(" 風(fēng)速: ", 45, 310);
g.setColor(Color.black);
g.setFont(new Font("微軟雅黑", Font.BOLD, 25));
g.drawString(" 風(fēng)向: ", 425, 310);
g.setColor(Color.gray);
g.setFont(new Font("微軟雅黑", Font.BOLD, 20));
g.drawString(" 串口選擇: ", 45, 410);
g.setColor(Color.gray);
g.setFont(new Font("微軟雅黑", Font.BOLD, 20));
g.drawString(" 波特率: ", 425, 410);
}
/**
* 雙緩沖方式重畫界面各元素組件
*/
public void update(Graphics g) {
if (offScreen == null) offScreen = this.createImage(Client.WIDTH, Client.HEIGHT);
Graphics gOffScreen = offScreen.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.white);
gOffScreen.fillRect(0, 0, Client.WIDTH, Client.HEIGHT); //重畫背景畫布
this.paint(gOffScreen); //重畫界面元素
gOffScreen.setColor(c);
g.drawImage(offScreen, 0, 0, null); //將新畫好的畫布“貼”在原畫布上
}
/*
* 重畫線程(每隔30毫秒重畫一次)
*/
private class RepaintThread implements Runnable {
public void run() {
while(true) {
//調(diào)用重畫方法
repaint();
//掃描可用串口
commList = SerialTool.findPort();
if (commList != null && commList.size()>0) {
//添加新掃描到的可用串口
for (String s : commList) {
//該串口名是否已存在,初始默認(rèn)為不存在(在commList里存在但在commChoice里不存在,則新添加)
boolean commExist = false;
for (int i=0; i<commChoice.getItemCount(); i++) {
if (s.equals(commChoice.getItem(i))) {
//當(dāng)前掃描到的串口名已經(jīng)在初始掃描時存在
commExist = true;
break;
}
}
if (commExist) {
//當(dāng)前掃描到的串口名已經(jīng)在初始掃描時存在,直接進(jìn)入下一次循環(huán)
continue;
}
else {
//若不存在則添加新串口名至可用串口下拉列表
commChoice.add(s);
}
}
//移除已經(jīng)不可用的串口
for (int i=0; i<commChoice.getItemCount(); i++) {
//該串口是否已失效,初始默認(rèn)為已經(jīng)失效(在commChoice里存在但在commList里不存在,則已經(jīng)失效)
boolean commNotExist = true;
for (String s : commList) {
if (s.equals(commChoice.getItem(i))) {
commNotExist = false;
break;
}
}
if (commNotExist) {
//System.out.println("remove" + commChoice.getItem(i));
commChoice.remove(i);
}
else {
continue;
}
}
}
else {
//如果掃描到的commList為空,則移除所有已有串口
commChoice.removeAll();
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
String err = ExceptionWriter.getErrorInfoFromException(e);
JOptionPane.showMessageDialog(null, err, "錯誤", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
}
}
}
/**
* 以內(nèi)部類形式創(chuàng)建一個串口監(jiān)聽類
* @author zhong
*
*/
private class SerialListener implements SerialPortEventListener {
/**
* 處理監(jiān)控到的串口事件
*/
public void serialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()) {
case SerialPortEvent.BI: // 10 通訊中斷
JOptionPane.showMessageDialog(null, "與串口設(shè)備通訊中斷", "錯誤", JOptionPane.INFORMATION_MESSAGE);
break;
case SerialPortEvent.OE: // 7 溢位(溢出)錯誤
case SerialPortEvent.FE: // 9 幀錯誤
case SerialPortEvent.PE: // 8 奇偶校驗(yàn)錯誤
case SerialPortEvent.CD: // 6 載波檢測
case SerialPortEvent.CTS: // 3 清除待發(fā)送數(shù)據(jù)
case SerialPortEvent.DSR: // 4 待發(fā)送數(shù)據(jù)準(zhǔn)備好了
case SerialPortEvent.RI: // 5 振鈴指示
case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 輸出緩沖區(qū)已清空
break;
case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用數(shù)據(jù)
//System.out.println("found data");
byte[] data = null;
try {
if (serialPort == null) {
JOptionPane.showMessageDialog(null, "串口對象為空!監(jiān)聽失??!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
}
else {
data = SerialTool.readFromPort(serialPort); //讀取數(shù)據(jù),存入字節(jié)數(shù)組
//System.out.println(new String(data));
// 自定義解析過程,你在實(shí)際使用過程中可以按照自己的需求在接收到數(shù)據(jù)后對數(shù)據(jù)進(jìn)行解析
if (data == null || data.length < 1) { //檢查數(shù)據(jù)是否讀取正確
JOptionPane.showMessageDialog(null, "讀取數(shù)據(jù)過程中未獲取到有效數(shù)據(jù)!請檢查設(shè)備或程序!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
else {
String dataOriginal = new String(data); //將字節(jié)數(shù)組數(shù)據(jù)轉(zhuǎn)換位為保存了原始數(shù)據(jù)的字符串
String dataValid = ""; //有效數(shù)據(jù)(用來保存原始數(shù)據(jù)字符串去除最開頭*號以后的字符串)
String[] elements = null; //用來保存按空格拆分原始字符串后得到的字符串?dāng)?shù)組
//解析數(shù)據(jù)
if (dataOriginal.charAt(0) == '*') { //當(dāng)數(shù)據(jù)的第一個字符是*號時表示數(shù)據(jù)接收完成,開始解析
dataValid = dataOriginal.substring(1);
elements = dataValid.split(" ");
if (elements == null || elements.length < 1) { //檢查數(shù)據(jù)是否解析正確
JOptionPane.showMessageDialog(null, "數(shù)據(jù)解析過程出錯,請檢查設(shè)備或程序!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
else {
try {
//更新界面Label值
/*for (int i=0; i<elements.length; i++) {
System.out.println(elements[i]);
}*/
//System.out.println("win_dir: " + elements[5]);
tem.setText(elements[0] + " ℃");
hum.setText(elements[1] + " %");
pa.setText(elements[2] + " hPa");
rain.setText(elements[3] + " mm");
win_sp.setText(elements[4] + " m/s");
win_dir.setText(elements[5] + " °");
} catch (ArrayIndexOutOfBoundsException e) {
JOptionPane.showMessageDialog(null, "數(shù)據(jù)解析過程出錯,更新界面數(shù)據(jù)失??!請檢查設(shè)備或程序!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
}
}
}
}
}
} catch (ReadDataFromSerialPortFailure | SerialPortInputStreamCloseFailure e) {
JOptionPane.showMessageDialog(null, e, "錯誤", JOptionPane.INFORMATION_MESSAGE);
System.exit(0); //發(fā)生讀取錯誤時顯示錯誤信息后退出系統(tǒng)
}
break;
}
}
}
}
運(yùn)行截圖:


整個項(xiàng)目源碼打包下載:http://xiazai.jb51.net/201612/yuanma/javaserialMonitor(jb51.net).rar
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(20)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你2021-07-07
Java中do-while循環(huán)的使用方法及注意事項(xiàng)詳解
這篇文章主要介紹了Java中do-while循環(huán)的使用方法及注意事項(xiàng)的相關(guān)資料,在Java編程中,do-while循環(huán)是一種基本的循環(huán)控制結(jié)構(gòu),它至少執(zhí)行一次循環(huán)體,然后根據(jù)條件判斷是否繼續(xù),文中將用法介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
Mybatis中ResultMap解決屬性名和數(shù)據(jù)庫字段名不一致問題
我們Pojo類的屬性名和數(shù)據(jù)庫中的字段名不一致的現(xiàn)象時有發(fā)生,本文就詳細(xì)的介紹一下Mybatis中ResultMap解決屬性名和數(shù)據(jù)庫字段名不一致問題,感興趣的可以了解一下2021-10-10
Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫
這篇文章主要介紹了Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter)
本文主要介紹了Spring Security6配置方法(廢棄WebSecurityConfigurerAdapter),就像文章標(biāo)題所說的,SpringSecurity已經(jīng)廢棄了繼承WebSecurityConfigurerAdapter的配置方式,下面就來詳細(xì)的介紹一下,感興趣的可以了解一下2023-12-12
mybatis中的擴(kuò)展實(shí)現(xiàn)源碼解析
這篇文章主要介給大家紹了關(guān)于mybatis中擴(kuò)展實(shí)現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01

