No.8 Appendix C Windowsプログラミング(5)

| | トラックバック(0)
4 Visual C#、Basic アプリケーションの作成
つづきです。

アプリケーション設定の使用
 「アプリケーション設定(設定ファイル)」を利用すると、ユーザがアプリケーションの実行中に変更した設定値を、簡単に保存/復元させることができます。
 この「アプリケーション設定」は、アプリケーションのプロパティとして扱われ、設定値はXML形式のファイルで保存され、復元されます。ユーザが定義した項目は、ソフトウェア上ではアプリケーションのプロパティとして参照、設定ができるため、とくにファイルのI/Oを意識する必要はありません。
 今回は、シリアル・ポート番号(COM番号)を記憶/復元するのに使用しています。アプリケーション設定を追加する手順の一つを次に示します。
(1)ソリューション・エクスプローラのプロジェクト名(太字で表示されている)上でマウスを右クリックして、ポップアップ・メニュから「プロパティ」をクリックする。この操作でIDE中央にアプリケーションのプロパティ画面が表示される。
(2)左側にあるタブの「設定」をクリックすると、図C-5のような入力テーブルが表示される。
(3)「名前」に"ComPort"、「値」に"COM1"と入力してキーボードから"Enter"キーを押すと、"ComPort"というプロパティが追加され、デフォルト値が"COM1"に設定される。なお、アプリケーション内から書き換える可能性があるため、「スコープ」は"ユーザー"にしておく必要がある。
 この「アプリケーション設定」は、次のようにして使用します。
<C#>
    Properties.Settings.Default.ComPort = "COM5";            // 設定
    string comname = Properties.Settings.Default.ComPort;    // 参照

<VB>
    My.Settings.ComPort = "COM5"                            ' 設定
    Dim comname As String;
    comname = My.Settings.ComPort                            ' 参照

 また、アプリケーション設定を永続化(設定ファイルに保存)するには、次のようにします。
<C#>
    Properties.Settings.Default.Save();                        // 永続化

<VB>
    My.Settings.Save()                                        ' 永続化

 VBの場合はアプリケーション終了時に自動的に保存されるため、"Saev()"メソッドは実行しなくてもかまいませんが、念のために入れておきます。

シリアル通信の処理
 シリアルで通信するには、あらかじめシリアル・ポートをオープンしておかなければなりません。オープンのタイミングは、アプリケーションが起動してフォームがロードされたときが適当です。メイン・フォーム・ロードのイベント・ハンドラのコードを次に示します。
<C#>
    // フォーム・ロード イベント・ハンドラ
    private void Form1_Load(object sender, EventArgs e) {
        // アプリケーション設定からポート番号を復帰
        serialPort1.PortName = Properties.Settings.Default.ComPort;
                        // シリアル・ポート名を設定
        try {
            serialPort1.Open();        // シリアル・ポートをオープン
        } catch {
            // オープンできなかったとき
            // シリアル・ポート名を"COM1"に設定
            serialPort1.PortName = "COM1";
            serialPort1.Open();                 // 再オープン
            // 設定ファイルを更新
            Properties.Settings.Default.ComPort = "COM1";
            // アプリケーション設定に保存
            Properties.Settings.Default.Save();

            // 変更した旨のメッセージを表示
            MessageBox.Show("COM1に変更しました", "確認",
                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
    }

<VB>
    ' フォーム・ロード イベント・ハンドラ
    Private Sub Form1_Load(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles MyBase.Load
        ' アプリケーション設定からポート番号を復帰
        SerialPort1.PortName = My.Settings.ComPort

        Try
            SerialPort1.Open()
        Catch
            ' オープンできなかったとき
            ' シリアル・ポート名を"COM1"に設定
            SerialPort1.PortName = "COM1"
            SerialPort1.Open()                  ' 再オープン
            ' アプリケーション設定に保存
            My.Settings.ComPort = "COM1"        ' 設定ファイルを更新
            My.Settings.Save()                  ' 設定ファイルを保存

            ' 変更した旨のメッセージを表示
            MessageBox.Show("COM1に設定を変更しました", "確認", _
                MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End Try
    End Sub

 まず、アプリケーション設定(設定ファイル)からポート番号を取り出してシリアル・ポート・コントロールの"PortName"プロパティに設定します。
 次にシリアル・ポートを"Open()"メソッドでオープンします。ここでは、例外処理を使っています。"try(Try)"ブロックに例外が発生する可能性のある処理を記述します。ここでは、シリアル・ポートがオープンできない可能性があるので、オープン処理を記述しています。
 オープンに失敗すると、"catch(Catch)"ブロックの処理が実行されます。ここでは、シリアル・ポートをデフォルトの"COM1"に再設定して、再オープンし、変更した旨のメッセージを表示します。
 2回目のオープンでも例外が発生した場合の例外処理はありませんが、この場合は、ハンドリングされていない旨の例外メッセージが表示され、アプリケーションを続行するか終了するかを選択できます。
 アプリケーション終了時にはシリアル・ポートをクローズしなければなりません。終了時に呼び出される、フォーム・クローズ(Form1の場合はアプリケーション終了と同意)のハンドラで処理します。そのコードを次に示します。
<C#>
    // フォーム・クローズ(アプリケーション終了)イベント・ハンドラ
    private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
        serialPort1.Close();      // シリアル・ポートをクローズ
    }

<VB>
    ' フォーム・クローズ(アプリケーション終了)イベント・ハンドラ
    Private Sub Form1_FormClosed(ByVal sender As System.Object, _
                ByVal e As System.Windows.Forms.FormClosedEventArgs) _
                    Handles MyBase.FormClosed
        SerialPort1.Close()
    End Sub

シリアル受信イベント
 シリアル・データを1行分受信すると、シリアル受信のイベント・ハンドラが呼び出されます。ここでは受信した文字列を取り出して、"SetText()"(後述)のデリゲートをインスタンス化します。受信データをテキスト・ボックスへ表示する処理は"SetText()"で行われます。
 ここでは、誤受信データでの処理を避けるため、文字数が3文字以上(デリミタ含む)のときだけ処理します。
 シリアル受信のイベント・ハンドラのコードは、次のようになります。
<C#>
    // シリアル受信イベント・ハンドラ
    private void serialPort1_DataReceived(object sender,
                        System.IO.Ports.SerialDataReceivedEventArgs e) {
        if(serialPort1.BytesToRead > 2) {
            // 3文字以上受信バッファにデータがある場合
            string str = serialPort1.ReadLine();                   // 1行読み出し
            // デリゲートのインスタンス化
            SetTextCallback d = new SetTextCallback(SetText);
            textBox1.Invoke(d, new object[] { str });
        }
    }

<VB>
    ' シリアル受信イベント・ハンドラ
    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, _
                 ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
                        Handles SerialPort1.DataReceived
        If (SerialPort1.BytesToRead > 2) Then
            Dim cmdstr As String = SerialPort1.ReadLine()       '1行読み出し
            ' デリゲートのインスタンス化
            Dim add As New SetTextCallback(AddressOf SetText)
            TextBox1.Invoke(add, cmdstr)
        End If
    End Sub

デリゲートの定義
 シリアル通信の受信に関係するものとして、デリゲートの定義とデリゲートに設定するメソッド(以下、デリゲート・メソッド)の記述が必要です。これらのコードは手入力が必要です。
 デリゲート・オブジェクトとメソッドのコードは次のようになります。

<C#>
    // デリゲート型のオブジェクトを定義
    delegate void SetTextCallback(string text);

    // 受信メソッド
    private void SetText(String str) {
        if(str.Substring(0, 1) == ".") {
            //  コマンド応答
            string cmd = str.Substring(1, 1).ToUpper();
            if(cmd == "M") {
                // Mコマンドの応答のとき
                str = str.Substring(3);
                double temp = (double)int.Parse(str, NumberStyles.Integer);
                temp = temp / 10.0;
                textBox1.Text = temp.ToString("0.0℃ ");
            }
        }
    }

<VB>
    ' デリゲート型のオブジェクトを定義
    Delegate Sub SetTextDelegate(ByVal str As String)

    ' 受信メソッド
    Private Sub SetText(ByVal str As String)
        If (str.Substring(0, 1) = ".") Then
            ' コマンド応答
            Dim cmd As String
            cmd = str.Substring(1, 1).ToUpper()
            If (cmd = "M") Then
                ' Mコマンドの応答のとき
                Dim temp As Double
                temp = str.Substring(3)
                temp = temp / 10.0
                TextBox1.Text = temp.ToString("0.0℃ ")
            End If
        End If
    End Sub

 デリゲート["SetText()"]の引数"str"で得られる文字列は、コマンド応答として受信した文字列です。数値化のために、先頭の数値に関係ない部分を取り除いて、純粋な10進数と符号の文字列だけにします。この数値は摂氏温度を10倍したものになっています。
 C#の場合、数値の文字列を"int.Parse()"メソッドでいったん整数値に変換し、それをdouble型の変数に代入して実数化します。引数の"NumberStyles.Integer"は文字列が整数を表現していることを指定しています。なお、"NumberStyles"を利用するためには、ソース・ファイルの先頭に次のような名前空間の定義が必要です。
    using System.Globalization;
 VBの場合は、数値を表す文字列は自動的に数値に変換されるため、単純にdouble型の変数へ代入するだけで済みます。
 実数化した温度の10倍値を10.0で割り、本来の単位に戻します。この実数値を"ToString()"メソッドでフォーマットし、テキスト・ボックスへ表示します。このメソッドは、引数でカスタム書式を指定できます。ここでは"0.0"と指定することで、小数点以下第一位までを求めています。"℃"は書式子ではないため、通常の文字としてそのまま出力されます。

ダイアログ・ボックスのオープン
 メイン・フォーム(Form1)からダイアログ・ボックス(Form2)をモーダルで開く方法を、「設定」ボタンのクリック・イベント・ハンドラのコードで示します。
<C#>
    // 設定ボタン
    private void button3_Click(object sender, EventArgs e) {
        Form2 form2 = new Form2();
        form2.ShowDialog();
    }

 C#の場合は、Form2のインスタンス化してそのインスタンスの"ShowDialog()"メソッドをコールします。Form2がクローズされると、インスタンスは破棄されます。
<VB>
    ' 設定ボタン
    Private Sub Button3_Click(ByVal sender As System.Object, _
                    ByVal e As System.EventArgs) Handles Button3.Click
        Form2.ShowDialog()
    End Sub

 VBの場合は、Form2の"ShowDialog()"メソッドをコールするだけです。

タイマ・イベント
 "Timer"コントロールを使用すると、ms単位の遅延イベントが得られます。タイマを停止させなければ、周期的なイベントを得ることもできます。"Timer"を起動するには、"Enabled"プロパティを"True"に設定するだけです。起動部分のコードは次のようになります。
<C#>
    // 測定インターバル・タイマ起動
    timer1.Enabled = true;                  // タイマ起動

<VB>
    ' 測定インターバル・タイマ起動
    Timer1.Enabled = True;                  ' タイマ起動

 タイマを停止させたいときは、"Enable"プロパティを"Falese"に設定します。次のコードは「測定終了」ボタンのクリック・イベントのハンドラです。
<C#>
    // 測定終了ボタン
    private void button2_Click(object sender, EventArgs e) {
        timer1.Enabled = false;                 // タイマ停止
    }

<VB>
    ' 測定終了ボタン
    Private Sub Button2_Click(ByVal sender As System.Object, _
                     ByVal e As System.EventArgs) Handles Button2.Click
        Timer1.Enabled = False
    End Sub

 タイマ・コントロールの"Interval"プロパティで指定された時間が経過すると、タイマ・イベントが発生し、ハンドラがコールされます。今回のプログラムでは、マイコン側に「温度測定開始コマンド(M)」を周期的に送信するのに使っています。このハンドラは、"Enabled"プロパティを"False"に設定するまで、周期的に呼び出されます。コードを次に示します。
<C#>
    // タイマ・イベント(測定インターバル)
    private void timer1_Tick(object sender, EventArgs e) {
        // 温度測定コマンドを送信
        serialPort1.WriteLine("M\r\n");
    }

<VB>
    ' タイマ・イベント(測定インターバル)
    Private Sub Timer1_Tick(ByVal sender As System.Object, _
                        ByVal e As System.EventArgs) Handles Timer1.Tick
        ' 温度測定コマンドを送信(2回目以降)
        SerialPort1.WriteLine("M" + vbCrLf)
    End Sub

その他のイベント・ハンドラのコーディング
 「測定開始」ボタンのイベント・ハンドラのコードを次に示します。ボタンをクリックしたときにコールされるハンドラです。
<C#>
    // 測定開始ボタン
    private void button1_Click(object sender, EventArgs e) {
        int interval;

        try {
            interval = int.Parse(comboBox1.Text, NumberStyles.Integer);

        } catch {
            // 変更した旨のメッセージを表示
            MessageBox.Show("整数を入力してください。", "入力値に誤り",
                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            interval = 1;
            comboBox1.Text = "1";
            return;
        }

        // 第1回目の温度測定コマンドを送信
        serialPort1.WriteLine("M\r\n");

        // 測定インターバル・タイマ起動
        timer1.Interval = interval * 1000;      // ms
        timer1.Enabled = true;                  // タイマ起動
    }

<VB>
    ' 測定開始ボタン
    Private Sub Button1_Click(ByVal sender As System.Object, _
                     ByVal e As System.EventArgs) Handles Button1.Click
        Dim interval As Integer

        Try
            interval = ComboBox1.Text
        Catch
            ' 変更した旨のメッセージを表示
            MessageBox.Show("整数を入力してください。", "入力値に誤り", _
                MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            interval = 1
            ComboBox1.Text = "1"
        End Try

        ' 第1回目の温度測定コマンドを送信
        SerialPort1.WriteLine("M" + vbCrLf)

        ' 測定インターバル・タイマ起動
        Timer1.Interval = interval * 1000      ' ms
        Timer1.Enabled = True                  ' タイマ起動
    End Sub

 コマンドをシリアル送信する前に、コンボ・ボックスのテキスト(秒単位の測定周期)を数値に変換する必要があります。
 C#の場合、数値に変換するのに"int.Parse()"メソッドを使っています。VBの場合は、文字列を整数の変数へ代入するだけで自動的に数値に変換されます。
 数値化の処理では例外が発生する可能性があるため、それをキャッチできるようにしてあります。コンボ・ボックスに数字以外の文字が入力されたときに例外が発生し、その入力をリジェクトして測定周期を強制的に1秒に設定します。
 測定周期が確定したら、第1回目の測定コマンドを送信して、タイマを起動します。

コンパイル、実行
 IDE画面のメニュのすぐ下にあるツール・バーの「デバッグ実行」ボタンをクリックすると、コンパイルが始まり、エラーがなければアプリケーションが起動します。
 エラーがある場合は、IDE下側にエラー・メッセージが表示されるので、それを頼りにコードを修正して再コンパイルします。なお、コンパイル(ビルド)だけ実行したい場合はメニュの「ビルド」-「ソリューションのビルド」を実行するか、キーボードのF6キーを押します。「ソリューションのビルド」を実行すると、リリース版のオブジェクトがプロジェクト・フォルダ下の"bin\Release"フォルダに生成されます。
 実行してフォームが表示されれば、とりあえずは動作しています。通信の確認は実際にシリアル・ポートにマイコン・ボードを接続して行います。

中尾 司

カテゴリ

,

トラックバック(0)

このブログ記事を参照しているブログ一覧: No.8 Appendix C Windowsプログラミング(5)

このブログ記事に対するトラックバックURL: http://www.eleki-jack.com/mt/mt-tb.cgi/1938


エレキジャック
エレキジャックのトップに戻る


雑誌 No.