トップページ > dsPIC入門 > Javaでハイパーターミナル(11)
最後に、データを受信したときの処理をデータ受信イベントとして記述していきます。これで、データを 勝手に受信してテキストエリアに表示する…ということができるようになります。 これから記述するクラスは、いま作っているSerial_Classの内部クラスとして付け足すことになります。
シリアル受信イベントの実装方法はボタンの時と似ています。今回アクションクラスは使わないのですが、 まずシリアル通信クラス側で受信イベント処理メソッドを記述します。これが割り込み関数のようなイメージですね。 (…と言った方が分かりやすいのはマイコン使う人だけなんでしょうけど) そして、イベントリスナへこのイベント処理クラスを登録して実際にイベントが受け付けられる…という流れです。
イベント・リスナのためのクラスはたいてい書式が決まっています。今回のクラスは“SerialPortListener” という名前で、“SerialPortEventListener”インターフェースを実装します。 このクラス内に定義する割り込み処理関数(イベント処理関数…)の名前は決められていて、 “public void SerialPortListener(SerialPortEvent hoge)”です。引数のSerialPortEvent型のフィールドの 名前は自分で決められます。 よって、このクラスの外枠は
class SerialPortListener implements SerialPortEventListener { public void serialEvent(SerialPortEvent Serial_event) { //ここにイベント発生時の処理を書く。 } }
…という感じになります。
…特筆することは無いと思います。コメントをたくさんつけておいたので、 後々忘れてしまった場合はコメントをたよりにして思い出す感じで。。。
//***************************************************************************** //内部クラスとして、イベントリスナを記述します。 class SerialPortListener implements SerialPortEventListener { //============================================================================ //受信イベント発生時に呼び出されるメソッド public void serialEvent(SerialPortEvent Serial_event) { //受信データ1文字分を置いておくための変数です。 int received_data = 0; //バッファーです。ここに受信データをためていきます。 StringBuffer buffer = new StringBuffer(); //受信完了の合図があれば・・・ if(Serial_event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { //-------------------------------------------------------------------------- //breakが来るまで回し続ける部分。 while(true) { try { received_data = in.read();// 入力ストリームから読み込み //文字無しの場合はすぐに抜けます。 if(received_data == -1) { break; } //文字列の終わりではない場合は、バッファーに付け加えていきます。 if((char)received_data != '\r') { buffer.append((char)received_data); } //文字列の終わりなら、最後に改行コードを足して、テキストエリアへ表示。 else { //テキストエリア改行。 buffer.append('\n'); //バッファの内容を文字列にして、テキストエリアへ追加。 tx.append(buffer.toString()); //バッファを一応、空にしておきます。 buffer.delete(0,buffer.length()-1); //これをやらないと、受信イベントごとに自動でテキストエリアがスクロールしてくれません。 tx.setCaretPosition(tx.getText().length()); break; } } catch(IOException ex){} } //while()文ここまで。 //-------------------------------------------------------------------------- } } } //内部クラスここまで。 //*****************************************************************************
以上で作ったクラスをSerial_Classの内部クラスとしてやればいいのですが、それにくわえて、 このクラスをイベント・リスナとして登録する作業も必要となります。これを忘れると イベントが発生でメソッドが呼ばれません。
これらのメソッドの呼び出しは、ポートを開いたときに行うことにします。 ポートを開く関数open()に、イベントリスナに関する記述を付け足しておきます。(if文の中の部分です。)
//============================================================================= //ポートオープンの関数 void open() { //正常にオープンできたかを調べるのに使うflagです。 boolean flag; //コンボボックスから、選択されているCOMポート名を取得します。文字列にしておきます。 String COM_Name = (com_combo.getSelectedItem()).toString(); //コンボボックスから、選択されているボー・レートを取得します。int型に直しておきます。 int Baud_Rate = Integer.parseInt((baud_combo.getSelectedItem()).toString()); //開く。 flag = Serial_open(COM_Name,Baud_Rate); //正常にオープンできたなら… if(flag) { try { //************************************************************************** //今回の追加部分です。 //受信イベントを登録します。 port.addEventListener(new SerialPortListener()); //このメソッドを呼んでおかないと、イベントが監視されません…。 port.notifyOnDataAvailable(true); //************************************************************************** } catch(Exception e){} tx.append("接続しました。\n\n"); } }
シリアル受信イベントメソッドの中では、受信した文字列をどんどんテキストエリアに追加していきます。 しかし、ただ追加するだけだと新しい行が画面外へはみ出てしまった場合に、なぜか自動でスクロールして くれません。(キーボードから入力するときは自動でスクロールしてくれるのですが、append()メソッドで 文字列を追加する場合は別扱いのようです…。)よって、最後尾の行へカーソルを動かす記述をして、 常に最新の行(=最後尾の行)が画面内に表示されるようにしてやらなければなりません。 これは、1つメソッドを呼ぶだけで済みます。
この“setCaretPosition()”メソッドは、引数として取った文字数の位置へカーソルをセットするメソッドです。 最後尾へ飛ばすために、“getText().length()”と記述して、現在入力されている文字列の数を取得します。 これで最後尾の位置が分かります。この文の追加位置は、受信メソッドの中の、テキストエリアに 文字列を追加した所となります。
これで、ハイパーターミナルのためのシリアル通信クラスは完成となります。 このクラスは前に作っていたMainFrameクラスが記述されているファイルに付け足しても、 新たに独立したファイルを作って(“Serial_Class.java”とか名前をつけます)そこにこの クラスだけを保存してもかまいません。 ファイルを分けた方がデバッグの時などは分かりやすいのですが、コンパイルの際はファイルを 1つ1つコンパイルする必要があるのでその点では面倒かもしれません。 (コマンドプロンプトで“javac *.java”と打てば、そのフォルダ内のすべてのjavaファイルが すべてコンパイルされるので便利です。)
//****************************************************************************** //シリアル通信担当のクラス。Serial_Baseクラスを継承。 //****************************************************************************** import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.io.*; import gnu.io.*; class Serial_Class extends Serial_Base { //============================================================================= //クラスのフィールド JComboBox com_combo; JComboBox baud_combo; JTextArea tx; JScrollPane scrollPane; //============================================================================= //コンストラクタ Serial_Class() { //テキストエリアを作成 tx = new JTextArea(); //スクロールペインを作成して、その上にテキストエリアを貼り付けておきます。 scrollPane = new JScrollPane(tx); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); //COMポート用のコンボボックスを作成して、アイテムを入れておきます。 com_combo = new JComboBox(); com_combo.addItem("COM1"); com_combo.addItem("COM2"); com_combo.addItem("COM3"); com_combo.addItem("COM4"); com_combo.addItem("COM5"); com_combo.addItem("COM6"); com_combo.addItem("COM7"); com_combo.addItem("COM8"); //デフォルト設定 com_combo.setSelectedItem("COM5"); //ボー・レート用のコンボボックスを作成して、アイテムを入れておきます。 baud_combo = new JComboBox(); baud_combo.addItem("4800"); baud_combo.addItem("9600"); baud_combo.addItem("19200"); baud_combo.addItem("38400"); baud_combo.addItem("57600"); baud_combo.addItem("115200"); baud_combo.addItem("230400"); //デフォルト設定 baud_combo.setSelectedItem("115200"); } //============================================================================= //ポートオープンの関数 void open() { //正常にオープンできたかを調べるのに使うflagです。 boolean flag; //コンボボックスから、選択されているCOMポート名を取得します。文字列にしておきます。 String COM_Name = (com_combo.getSelectedItem()).toString(); //コンボボックスから、選択されているボー・レートを取得します。int型に直しておきます。 int Baud_Rate = Integer.parseInt((baud_combo.getSelectedItem()).toString()); //開く。 flag = Serial_open(COM_Name,Baud_Rate); //正常にオープンできたなら… if(flag) { try { //受信イベントを登録します。 port.addEventListener(new SerialPortListener()); //このメソッドを呼んでおかないと、イベントが監視されません…。 port.notifyOnDataAvailable(true); } catch(Exception e){} tx.append("接続しました。\n\n"); } } //============================================================================= //ポートクローズの関数 void close() { //正常にクローズできたかを調べるのに使うflagです。 boolean flag; //閉じる。 flag = Serial_close(); //正常にクローズできたなら… if(flag) { tx.append("切断しました。\n\n"); } } //***************************************************************************** //内部クラスとして、イベントリスナを記述します。 class SerialPortListener implements SerialPortEventListener { //============================================================================ //受信イベント発生時に呼び出されるメソッド public void serialEvent(SerialPortEvent Serial_event) { //受信データ1文字分を置いておくための変数です。 int received_data = 0; //バッファーです。ここに受信データをためていきます。 StringBuffer buffer = new StringBuffer(); //受信完了の合図があれば・・・ if(Serial_event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { //-------------------------------------------------------------------------- //breakが来るまで回し続ける部分。 while(true) { try { received_data = in.read();// 入力ストリームから読み込み //文字無しの場合はすぐに抜けます。 if(received_data == -1) { break; } //文字列の終わりではない場合は、バッファーに付け加えていきます。 if((char)received_data != '\r') { buffer.append((char)received_data); } //文字列の終わりなら、最後に改行コードを足して、テキストエリアへ表示。 else { //テキストエリア改行。 buffer.append('\n'); //バッファの内容を文字列にして、テキストエリアへ追加。 tx.append(buffer.toString()); //バッファを一応、空にしておきます。 buffer.delete(0,buffer.length()-1); //これをやらないと、受信イベントごとに自動でテキストエリアがスクロールしてくれません。 tx.setCaretPosition(tx.getText().length()); break; } } catch(IOException ex){} } //while()文ここまで。 //-------------------------------------------------------------------------- } } } //内部クラスここまで。 //***************************************************************************** }