使用Java實現簡單串口通信


最近一門課要求編寫一個上位機串口通信工具,我基於Java編寫了一個帶有圖形界面的簡單串口通信工具,下面詳述一下過程,供大家參考 ^_^

一:

首先,你需要下載一個額外的支持Java串口通信操作的jar包,由於java.comm比較老了,而且不支持64位系統,這里推薦Rxtx這個jar包(32位/64位均支持)。

官方下載地址:http://fizzed.com/oss/rxtx-for-java (注:可能需要翻牆才能下載)

不能翻牆的童鞋,可以在這里下載:

http://files.cnblogs.com/files/Dreamer-1/mfz-rxtx-2.2-20081207-win-x86.zip (32位)

http://files.cnblogs.com/files/Dreamer-1/mfz-rxtx-2.2-20081207-win-x64.zip (64位)

 

二:

下載解壓jar包並在 Java Build Path 下引入:

捕獲

 

注:如果運行過程中拋出 java.lang.UnsatisfiedLinkError 錯誤或 gnu.io 下的類找不到,請將rxtx解壓包中的 rxtxParallel.dll,rxtxSerial.dll 這兩個文件復制到 C:\Windows\System32 目錄下即可解決該錯誤。

 

三:

關於該jar包的使用,我寫了一個SerialTool.java類,該類提供關於串口通信的各簡單服務,代碼如下(注意該類位於 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.*;

/**
 * 串口服務類,提供打開、關閉串口,讀取、發送串口數據等服務(采用單例設計模式)
 * @author zhong
 *
 */
public class SerialTool {
    
    private static SerialTool serialTool = null;
    
    static {
        //在該類被ClassLoader加載時就初始化一個SerialTool對象
        if (serialTool == null) {
            serialTool = new SerialTool();
        }
    }
    
    //私有化SerialTool類的構造方法,不允許其他類生成SerialTool對象
    private SerialTool() {}    
    
    /**
     * 獲取提供服務的SerialTool對象
     * @return serialTool
     */
    public static SerialTool getSerialTool() {
        if (serialTool == null) {
            serialTool = new SerialTool();
        }
        return serialTool;
    }


    /**
     * 查找所有可用端口
     * @return 可用端口名稱列表
     */
    public static final ArrayList<String> findPort() {

        //獲得當前所有可用串口
        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 設置串口參數失敗
     * @throws NotASerialPort 端口指向設備不是串口類型
     * @throws NoSuchPort 沒有該端口對應的串口設備
     * @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 {                        
                    //設置一下串口的波特率等參數
                    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();
        }
    }
    
    /**
     * 關閉串口
     * @param serialport 待關閉的串口對象
     */
    public static void closePort(SerialPort serialPort) {
        if (serialPort != null) {
            serialPort.close();
            serialPort = null;
        }
    }
    
    /**
     * 往串口發送數據
     * @param serialPort 串口對象
     * @param order    待發送數據
     * @throws SendDataToSerialPortFailure 向串口發送數據失敗
     * @throws SerialPortOutputStreamCloseFailure 關閉串口對象的輸出流出錯
     */
    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();
            }
        }
        
    }
    
    /**
     * 從串口讀取數據
     * @param serialPort 當前已建立連接的SerialPort對象
     * @return 讀取到的數據
     * @throws ReadDataFromSerialPortFailure 從串口讀取數據時出錯
     * @throws SerialPortInputStreamCloseFailure 關閉串口對象輸入流出錯
     */
    public static byte[] readFromPort(SerialPort serialPort) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure {

        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 serialException;

public class SerialPortParameterFailure extends Exception {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public SerialPortParameterFailure() {}

    @Override
    public String toString() {
        return "設置串口參數失敗!打開串口操作未完成!";
    }
    
}

每個自定義的Exception類我都重寫了它的 toString() 方法,便於主程序捕捉到該Exception后打印對應的錯誤信息

其中在serialException包里還有一個專門將接收到的Exception對象內的錯誤信息提取出來轉換成字符串並返回的類,代碼如下:

package serialException;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 負責將傳入的Exception中的錯誤信息提取出來並轉換成字符串;
 * @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方法),它的作用是顯示一個歡迎界面並調用DataView.java這個類進行實際的串口數據顯示。

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;
    
    /**
     * 程序界面出現位置(橫坐標)
     */
    public static final int LOC_X = 200;
    
    /**
     * 程序界面出現位置(縱坐標)
     */
    public static final int LOC_Y = 70;

    Color color = Color.WHITE; 
    Image offScreen = null;    //用於雙緩沖
    
    //設置window的icon(這里我自定義了一下Windows窗口的icon圖標,因為實在覺得哪個小咖啡圖標不好看 = =)
    Toolkit toolKit = getToolkit();
    Image icon = toolKit.getImage(Client.class.getResource("computer.png"));
    
    //持有其他類
    DataView dataview = new DataView(this);    //主界面類(顯示監控數據主面板)

    /**
     * 主方法
     * @param args    //
     */
    public static void main(String[] args) {
        new Client().launchFrame();    
    }
    
    /**
     * 顯示主界面
     */
    public void launchFrame() {
        this.setBounds(LOC_X, LOC_Y, WIDTH, HEIGHT);    //設定程序在桌面出現的位置
        this.setTitle("CDIO工程項目");    //設置程序標題
        this.setIconImage(icon);
        this.setBackground(Color.white);    //設置背景色
        
        this.addWindowListener(new WindowAdapter() {
            //添加對窗口狀態的監聽
            public void windowClosing(WindowEvent arg0) {
                //當窗口關閉時
                System.exit(0);    //退出程序
            }
            
        });

        this.addKeyListener(new KeyMonitor());    //添加鍵盤監聽器
        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("歡迎使用上位機實時監控系統", 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("————點擊Enter鍵進入主界面————", 100, 480);
        //使文字 "————點擊Enter鍵進入主界面————" 黑白閃爍
        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);    //將新畫好的畫布“貼”在原畫布上
    }
    
    /*
     * 內部類形式實現對鍵盤事件的監聽
     */
    private class KeyMonitor extends KeyAdapter {

        public void keyReleased(KeyEvent e) {
            int keyCode = e.getKeyCode();
            if (keyCode == KeyEvent.VK_ENTER) {    //當監聽到用戶敲擊鍵盤enter鍵后執行下面的操作
                setVisible(false);    //隱去歡迎界面
                dataview.setVisible(true);    //顯示監測界面
                dataview.dataFrame();    //初始化監測界面
            }
        }
        
    }
    
    
    /*
     * 重畫線程(每隔250毫秒重畫一次)
     */
    private class RepaintThread implements Runnable {
        public void run() {
            while(true) {
                repaint();
                try {
                    Thread.sleep(250);
                } catch (InterruptedException e) {
                    //重畫線程出錯拋出異常時創建一個Dialog並顯示異常詳細信息
                    String err = ExceptionWriter.getErrorInfoFromException(e);
                    JOptionPane.showMessageDialog(null, err, "錯誤", JOptionPane.INFORMATION_MESSAGE);
                    System.exit(0);
                }
            }
        }
        
    }
    
}

運行截圖:

注:實際運行過程中最下面的“點擊Enter鍵進入主界面”有一個一閃一閃的效果(是通過每隔一段時間重畫一次界面,讓這句話以白黑兩色反復交替出現實現的),雙緩沖方式利於解決重畫時界面閃爍的問題(如果不使用雙緩沖方式的話相當於每次重畫時是在舊界面上一點一點畫上新東西,而雙緩沖實質上是通過先在內存中直接畫好一張新界面圖,然后一次性直接用新界面覆蓋掉舊界面)

捕獲2

 

DataView.java代碼如下:(該類用於實時顯示串口數據)

簡單說明:

硬件設備每隔一段時間通過串口發送一次數據到計算機,該串口工具成功連接至硬件設備並添加監聽后,會在每次接收到數據時解析數據並更新界面;

你在使用時很可能需求跟我不一樣,該類僅供參考,實際使用中你很可能需要重新制作數據顯示界面以及數據解析方式

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.*;

/**
 * 監測數據顯示類
 * @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("暫無數據", Label.CENTER);    //溫度
    private Label hum = new Label("暫無數據", Label.CENTER);    //濕度
    private Label pa = new Label("暫無數據", Label.CENTER);    //壓強
    private Label rain = new Label("暫無數據", Label.CENTER);    //雨量
    private Label win_sp = new Label("暫無數據", Label.CENTER);    //風速
    private Label win_dir = new Label("暫無數據", Label.CENTER);    //風向
    
    private Choice commChoice = new Choice();    //串口選擇(下拉框)
    private Choice bpsChoice = new Choice();    //波特率選擇
    
    private Button openSerialButton = new Button("打開串口");
    
    Image offScreen = null;    //重畫時的畫布
    
    //設置window的icon
    Toolkit toolKit = getToolkit();
    Image icon = toolKit.getImage(DataView.class.getResource("computer.png"));

    /**
     * 類的構造方法
     * @param client
     */
    public DataView(Client client) {
        this.client = client;
        commList = SerialTool.findPort();    //程序初始化時就掃描一次有效串口
    }
    
    /**
     * 主菜單窗口顯示;
     * 添加Label、按鈕、下拉條及相關事件監聽;
     */
    public void dataFrame() {
        this.setBounds(client.LOC_X, client.LOC_Y, client.WIDTH, client.HEIGHT);
        this.setTitle("CDIO工程項目");
        this.setIconImage(icon);
        this.setBackground(Color.white);
        this.setLayout(null);
        
        this.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent arg0) {
                if (serialPort != null) {
                    //程序退出時關閉串口釋放資源
                    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);
        
        //添加串口選擇選項
        commChoice.setBounds(160, 397, 200, 200);
        //檢查是否有可用串口,有則加入選項中
        if (commList == null || commList.size()<1) {
            JOptionPane.showMessageDialog(null, "沒有搜索到有效串口!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
        }
        else {
            for (String s : commList) {
                commChoice.add(s);
            }
        }
        add(commChoice);
        
        //添加波特率選項
        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);
        //添加打開串口按鈕的事件監聽
        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);
                            //在該串口對象上添加監聽器
                            SerialTool.addListener(serialPort, new SerialListener());
                            //監聽成功進行提示
                            JOptionPane.showMessageDialog(null, "監聽成功,稍后將顯示監測數據!", "提示", JOptionPane.INFORMATION_MESSAGE);
                            
                        } catch (SerialPortParameterFailure | NotASerialPort | NoSuchPort | PortInUse | TooManyListeners e1) {
                            //發生錯誤時使用一個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(" 壓強: ", 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(" 風速: ", 45, 310);
        
        g.setColor(Color.black);
        g.setFont(new Font("微軟雅黑", Font.BOLD, 25));
        g.drawString(" 風向: ", 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) {
                //調用重畫方法
                repaint();
                
                
                
                //掃描可用串口
                commList = SerialTool.findPort();
                if (commList != null && commList.size()>0) {
                    
                    //添加新掃描到的可用串口
                    for (String s : commList) {
                        
                        //該串口名是否已存在,初始默認為不存在(在commList里存在但在commChoice里不存在,則新添加)
                        boolean commExist = false;    
                        
                        for (int i=0; i<commChoice.getItemCount(); i++) {
                            if (s.equals(commChoice.getItem(i))) {
                                //當前掃描到的串口名已經在初始掃描時存在
                                commExist = true;
                                break;
                            }                    
                        }
                        
                        if (commExist) {
                            //當前掃描到的串口名已經在初始掃描時存在,直接進入下一次循環
                            continue;
                        }
                        else {
                            //若不存在則添加新串口名至可用串口下拉列表
                            commChoice.add(s);
                        }
                    }
                    
                    //移除已經不可用的串口
                    for (int i=0; i<commChoice.getItemCount(); i++) {
                        
                        //該串口是否已失效,初始默認為已經失效(在commChoice里存在但在commList里不存在,則已經失效)
                        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);
                }
            }
        }
        
    }
    
    /**
     * 以內部類形式創建一個串口監聽類
     * @author zhong
     *
     */
    private class SerialListener implements SerialPortEventListener {
        
        /**
         * 處理監控到的串口事件
         */
        public void serialEvent(SerialPortEvent serialPortEvent) {
            
            switch (serialPortEvent.getEventType()) {

                case SerialPortEvent.BI: // 10 通訊中斷
                    JOptionPane.showMessageDialog(null, "與串口設備通訊中斷", "錯誤", JOptionPane.INFORMATION_MESSAGE);
                    break;

                case SerialPortEvent.OE: // 7 溢位(溢出)錯誤

                case SerialPortEvent.FE: // 9 幀錯誤

                case SerialPortEvent.PE: // 8 奇偶校驗錯誤

                case SerialPortEvent.CD: // 6 載波檢測

                case SerialPortEvent.CTS: // 3 清除待發送數據

                case SerialPortEvent.DSR: // 4 待發送數據准備好了

                case SerialPortEvent.RI: // 5 振鈴指示

                case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 輸出緩沖區已清空
                    break;
                
                case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用數據
                    
                    //System.out.println("found data");
                    byte[] data = null;
                    
                    try {
                        if (serialPort == null) {
                            JOptionPane.showMessageDialog(null, "串口對象為空!監聽失敗!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
                        }
                        else {
                            data = SerialTool.readFromPort(serialPort);    //讀取數據,存入字節數組
                            //System.out.println(new String(data));
                            
                       // 自定義解析過程,你在實際使用過程中可以按照自己的需求在接收到數據后對數據進行解析
                            if (data == null || data.length < 1) {    //檢查數據是否讀取正確    
                                JOptionPane.showMessageDialog(null, "讀取數據過程中未獲取到有效數據!請檢查設備或程序!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
                                System.exit(0);
                            }
                            else {
                                String dataOriginal = new String(data);    //將字節數組數據轉換位為保存了原始數據的字符串
                                String dataValid = "";    //有效數據(用來保存原始數據字符串去除最開頭*號以后的字符串)
                                String[] elements = null;    //用來保存按空格拆分原始字符串后得到的字符串數組    
                                //解析數據
                                if (dataOriginal.charAt(0) == '*') {    //當數據的第一個字符是*號時表示數據接收完成,開始解析                            
                                    dataValid = dataOriginal.substring(1);
                                    elements = dataValid.split(" ");
                                    if (elements == null || elements.length < 1) {    //檢查數據是否解析正確
                                        JOptionPane.showMessageDialog(null, "數據解析過程出錯,請檢查設備或程序!", "錯誤", 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, "數據解析過程出錯,更新界面數據失敗!請檢查設備或程序!", "錯誤", JOptionPane.INFORMATION_MESSAGE);
                                            System.exit(0);
                                        }
                                    }    
                                }
                            }
                            
                        }                        
                        
                    } catch (ReadDataFromSerialPortFailure | SerialPortInputStreamCloseFailure e) {
                        JOptionPane.showMessageDialog(null, e, "錯誤", JOptionPane.INFORMATION_MESSAGE);
                        System.exit(0);    //發生讀取錯誤時顯示錯誤信息后退出系統
                    }    
                    
                    break;
    
            }

        }

    }
    
    
}

運行截圖:

捕獲3

捕獲4

 

整個項目源碼打包下載:http://files.cnblogs.com/files/Dreamer-1/serialMonitor.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM