16 January 2016

(Java) rxtxを用いたシリアルポート送受信のCPU負荷比較

Javaのシリアルポート送受信を、イベントを用いた場合と、ユーザが無限ループを組んで行った場合、それぞれのCPU負荷を比較してみた。

検証環境

・Ubuntu 14.0 4LTS
・Java 8
・rxtxライブラリ version 2.1

CPU負荷測定結果

20160116-rxtx-event.jpg
イベント serialEvent を用いた場合のCPU負荷

20160116-rxtx-loop01.jpg
無限ループを用いた場合で、Readlineを用いた場合のCPU負荷

20160116-rxtx-loop02.jpg
無限ループを用いた場合で、Readを用いた場合のCPU負荷

やはり、イベントを用いたほうが遥かにCPU負荷が低く、好ましい結果となっている

イベント serialEvent を用いた場合

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
 
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
 
/**
 * シリアルポート送受信のテスト・クラス。受信イベントを用いてCPU負荷を低減した版
 *
 */
public class main_class implements SerialPortEventListener {
 
    private static CommPortIdentifier comID;
    private static SerialPort commPort;
    BufferedReader in;
    private static BufferedReader br;
    Boolean flagDispDisable = false;
 
    /**
     * このmain_classのコンストラクタ。 シリアルポートのデバイス名と通信速度のユーザ入力と、シリアルポートを開く処理
     */
    public main_class() {
 
        // シリアルポートのデバイス名と通信速度のユーザ入力
        String commDeviceName = "/dev/ttyUSB0";
        int commSpeed = 9600;
        br = new BufferedReader(new InputStreamReader(System.in));
        try {
            System.out.print("comデバイス名 (/dev/ttyUSB0) : ");
            commDeviceName = br.readLine();
            if (commDeviceName.length() < 1)
                commDeviceName = "/dev/ttyUSB0";
            System.out.print("通信速度 bps (9600) : ");
            String strTemp = br.readLine();
            if (strTemp.length() < 1)
                commSpeed = 9600;
            else
                commSpeed = Integer.parseInt(strTemp);
            if (commSpeed != 2400 && commSpeed != 4800 && commSpeed != 9600
                    && commSpeed != 14400 && commSpeed != 19200)
                commSpeed = 9600;
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("\nkeyboard input error");
            return;
        }
 
        // シリアルポートを開き、通信速度等を設定する
        try {
            comID = CommPortIdentifier.getPortIdentifier(commDeviceName);
            commPort = (SerialPort) comID.open("dummy_app_name", 2000);
            commPort.setSerialPortParams(commSpeed, SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
            commPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.print("\ncomm port open error\n");
            if (commPort != null)
                commPort.close();
            return;
        }
 
        // シリアルポートで発生するイベント処理の初期設定
        try {
            commPort.addEventListener(this);
            commPort.notifyOnDataAvailable(true);
            commPort.notifyOnBreakInterrupt(true);
            // 指定したバイト以上受信したらEventを発生させる
            commPort.enableReceiveThreshold(5);
            // 指定したミリ秒が経過すれば強制的にEventを発生させる
            commPort.enableReceiveTimeout(500);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
        // シリアルポート受信側ストリームを開く
        try {
            in = new BufferedReader(new InputStreamReader(
                    commPort.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * ユーザのキーボード入力でシリアルポートに送信するループ
     */
    private void main_loop() {
        OutputStream outStream;
        try {
            // シリアルポート送信側ストリームを開く
            outStream = commPort.getOutputStream();
            while (true) {
                // 端末で1行入力
                String tmpStr = br.readLine();
                // このプログラムを終了する
                if (tmpStr.equalsIgnoreCase("quit")
                        || tmpStr.equalsIgnoreCase("exit")
                        || tmpStr.equalsIgnoreCase("q"))
                    break;
                else if (tmpStr.length() < 1) {
                    // シリアルポートで受信したデータを表示するかどうかのトグル
                    flagDispDisable = !flagDispDisable;
                    if (flagDispDisable)
                        System.out
                                .println("(disp off) command -> quit, spdNNNN");
                } else if (tmpStr.equalsIgnoreCase("spd4800")) {
                    // シリアルポートへの送信(通信速度設定)
                    outStream.write("spd4800\r".getBytes());
                    System.out.println("Send command \"speed 4800 bps\"");
                } else if (tmpStr.equalsIgnoreCase("spd9600")) {
                    outStream.write("spd9600\r".getBytes());
                    System.out.println("Send command \"speed 9600 bps\"");
                } else if (tmpStr.equalsIgnoreCase("spd19200")) {
                    outStream.write("spd19200\r".getBytes());
                    System.out.println("Send command \"speed 19200 bps\"");
                } else if (tmpStr.equalsIgnoreCase("stop")) {
                    // シリアルポートへの送信(一時停止コマンド)
                    outStream.write("stop\r".getBytes());
                    System.out.println("Send command \"stop\"");
                } else
                    System.out.println("command error");
            }
            System.out.print("exit read loop\n");
            // シリアルポートの受信側ストリームを閉じる
            in.close();
            System.out.print("commPort BufferedReader is closed\n");
            // シリアルポートの送信側ストリームを閉じる
            outStream.close();
            System.out.print("commPort OutputStream is closed\n");
 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // シリアルポートを閉じる
                commPort.close();
                System.out.print("commPort is closed\n");
                // キーボード入力を閉じる
                br.close();
                System.out.print("Keyboard input stream is closed\n");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     * Javaプログラムの開始関数 main
     * 
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("シリアル通信(送受信) イベント利用版");
 
        // クラスを初期化(シリアルポートを開く)
        main_class serial_main = new main_class();
        // ユーザのキーボード入力でシリアルポートに送信するループ
        serial_main.main_loop();
    }
 
    /*
     * シリアルポートで発生するイベント受信
     * 
     * @see gnu.io.SerialPortEventListener#serialEvent(gnu.io.SerialPortEvent)
     */
    @Override
    public void serialEvent(SerialPortEvent arg0) {
        if (arg0.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            try {
                // シリアルポートから1行(末尾改行)テキストを読み込み画面表示
                String inputLine;
                if ((in.ready()) && (inputLine = in.readLine()) != null) {
                    if (!flagDispDisable)
                        System.out.println(inputLine);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

無限ループを用いた場合

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
 
/**
 * シリアルポート送受信のテスト・クラス。受信イベントを用いず、ループ処理でCPU負荷が高い版
 *
 */
public class main_class {
 
    private static CommPortIdentifier comID;
    private static SerialPort commPort;
    private static BufferedReader br;
 
    /**
     * Javaプログラムの開始関数 main
     * 
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("シリアル通信(送受信) 受信無限ループ待ち版");
 
        // シリアルポートのデバイス名と通信速度のユーザ入力
        String commDeviceName = "/dev/ttyUSB0";
        int commSpeed = 9600;
        br = new BufferedReader(new InputStreamReader(System.in));
        try {
            System.out.print("comデバイス名 (/dev/ttyUSB0) : ");
            commDeviceName = br.readLine();
            if (commDeviceName.length() < 1)
                commDeviceName = "/dev/ttyUSB0";
            System.out.print("通信速度 bps (9600) : ");
            String strTemp = br.readLine();
            if (strTemp.length() < 1)
                commSpeed = 9600;
            else
                commSpeed = Integer.parseInt(strTemp);
            if (commSpeed != 2400 && commSpeed != 4800 && commSpeed != 9600
                    && commSpeed != 14400 && commSpeed != 19200)
                commSpeed = 9600;
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("\nkeyboard input error");
            return;
        }
 
        // シリアルポートを開き、通信速度等を設定する
        try {
            comID = CommPortIdentifier.getPortIdentifier(commDeviceName);
            commPort = (SerialPort) comID.open("dummy_app_name", 2000);
            commPort.setSerialPortParams(commSpeed, SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
            commPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.print("\ncomm port open error\n");
            if (commPort != null)
                commPort.close();
            return;
        }
 
        // シリアルポートからの受信を1行毎(Readline関数)を用いる場合
        Receive_Readline();
        // シリアルポートからの受信を1文字毎(Read関数)を用いる場合
//        Receive_Read();
    }
 
    /**
     * シリアルポートからの受信を1行毎(Readline関数)に画面表示し、 端末キーボードからの入力をシリアルポート側に送信する。
     */
    private static void Receive_Readline() {
        BufferedReader in;
        OutputStream outStream;
        Boolean flagDispDisable = false;
        try {
            // シリアルポート受信側ストリームを開く
            in = new BufferedReader(new InputStreamReader(
                    commPort.getInputStream()));
            // シリアルポート送信側ストリームを開く
            outStream = commPort.getOutputStream();
 
            // シリアルポート受信、端末キーボード入力の無限ループによる処理
            String inputLine;
            while (true) {
                // シリアルポートから1行(末尾改行)テキストを読み込み画面表示
                if ((in.ready()) && (inputLine = in.readLine()) != null) {
                    if (!flagDispDisable)
                        System.out.println(inputLine);
                }
                // 端末のキーボード入力を待ち、シリアルポート側へ送信する
                if (br.ready()) {
                    String tmpStr = br.readLine();
                    // このプログラムを終了する
                    if (tmpStr.equalsIgnoreCase("quit")
                            || tmpStr.equalsIgnoreCase("exit")
                            || tmpStr.equalsIgnoreCase("q"))
                        break;
                    else if (tmpStr.length() < 1) {
                        // シリアルポートで受信したデータを表示するかどうかのトグル
                        flagDispDisable = !flagDispDisable;
                        if (flagDispDisable)
                            System.out
                                    .println("(disp off) command -> quit, spdNNNN");
                    } else if (tmpStr.equalsIgnoreCase("spd4800")) {
                        // シリアルポートへの送信(通信速度設定)
                        outStream.write("spd4800\r".getBytes());
                        System.out.println("Send command \"speed 4800 bps\"");
                    } else if (tmpStr.equalsIgnoreCase("spd9600")) {
                        outStream.write("spd9600\r".getBytes());
                        System.out.println("Send command \"speed 9600 bps\"");
                    } else if (tmpStr.equalsIgnoreCase("spd19200")) {
                        outStream.write("spd19200\r".getBytes());
                        System.out.println("Send command \"speed 19200 bps\"");
                    } else if (tmpStr.equalsIgnoreCase("stop")) {
                        // シリアルポートへの送信(一時停止コマンド)
                        outStream.write("stop\r".getBytes());
                        System.out.println("Send command \"stop\"");
                    } else
                        System.out.println("command error");
                }
 
            }
            System.out.print("exit read loop\n");
            // シリアルポートの受信側ストリームを閉じる
            in.close();
            System.out.print("commPort BufferedReader is closed\n");
            // シリアルポートの送信側ストリームを閉じる
            outStream.close();
            System.out.print("commPort OutputStream is closed\n");
 
        } catch (IOException e) {
            System.out.print("read error\n");
            e.printStackTrace();
        } finally {
            try {
                // シリアルポートを閉じる
                commPort.close();
                System.out.print("commPort is closed\n");
                // キーボード入力を閉じる
                br.close();
                System.out.print("Keyboard input stream is closed\n");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     * シリアルポートからの受信を1文字毎(Read関数)に画面表示し、 端末キーボードからの入力をシリアルポート側に送信する。
     */
    private static void Receive_Read() {
        BufferedReader in;
        OutputStream outStream;
        Boolean flagDispDisable = false;
        try {
            // シリアルポート受信側ストリームを開く
            in = new BufferedReader(new InputStreamReader(
                    commPort.getInputStream()));
            // シリアルポート送信側ストリームを開く
            outStream = commPort.getOutputStream();
 
            // シリアルポート受信、端末キーボード入力の無限ループによる処理
            String inputLine = "";
            int inputChar;
            while (true) {
                // シリアルポートから1行(末尾改行)テキストを読み込み画面表示
                if ((in.ready()) && (inputChar = in.read()) != -1) {
                    // 受信すべき文字の範囲はASCII文字のみ
                    if (0x20 <= inputChar && inputChar <= 0x7e)
                        inputLine += (char) inputChar;
                    // 改行文字が来たら、また1行が256文字を超えれば画面表示
                    else if (inputChar == 0x0d || inputLine.length() >= 256) {
                        if (!flagDispDisable)
                            System.out.println(inputLine);
                        inputLine = "";
                    }
                }
                // 端末のキーボード入力を待ち、シリアルポート側へ送信する
                if (br.ready()) {
                    String tmpStr = br.readLine();
                    // このプログラムを終了する
                    if (tmpStr.equalsIgnoreCase("quit")
                            || tmpStr.equalsIgnoreCase("exit")
                            || tmpStr.equalsIgnoreCase("q"))
                        break;
                    else if (tmpStr.length() < 1) {
                        // シリアルポートで受信したデータを表示するかどうかのトグル
                        flagDispDisable = !flagDispDisable;
                        if (flagDispDisable)
                            System.out
                                    .println("(disp off) command -> quit, spdNNNN");
                    } else if (tmpStr.equalsIgnoreCase("spd4800")) {
                        // シリアルポートへの送信(通信速度設定)
                        outStream.write("spd4800\r".getBytes());
                        System.out.println("Send command \"speed 4800 bps\"");
                    } else if (tmpStr.equalsIgnoreCase("spd9600")) {
                        outStream.write("spd9600\r".getBytes());
                        System.out.println("Send command \"speed 9600 bps\"");
                    } else if (tmpStr.equalsIgnoreCase("spd19200")) {
                        outStream.write("spd19200\r".getBytes());
                        System.out.println("Send command \"speed 19200 bps\"");
                    } else if (tmpStr.equalsIgnoreCase("stop")) {
                        // シリアルポートへの送信(一時停止コマンド)
                        outStream.write("stop\r".getBytes());
                        System.out.println("Send command \"stop\"");
                    } else
                        System.out.println("command error");
                }
 
            }
            System.out.print("exit read loop\n");
            // シリアルポートの受信側ストリームを閉じる
            in.close();
            System.out.print("commPort BufferedReader is closed\n");
            // シリアルポートの送信側ストリームを閉じる
            outStream.close();
            System.out.print("commPort OutputStream is closed\n");
 
        } catch (IOException e) {
            System.out.print("read error\n");
            e.printStackTrace();
        } finally {
            try {
                // シリアルポートを閉じる
                commPort.close();
                System.out.print("commPort is closed\n");
                // キーボード入力を閉じる
                br.close();
                System.out.print("Keyboard input stream is closed\n");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}