ETロボコン Bluetoothログ受信②

こちらの記事は[ケーシーエスキャロット Advent Calendar 2019](https://qiita.com/advent-calendar/2019/kcscarrot)の8日目の記事です。
昨日…、ではなく一昨日はaookiさんの[自作キーボードを作りました](http://aoiro6.hatenablog.com/entry/2019/12/06/220000)でした、自作キーボード、興味はあるのですが、手が出せていません。

さて、1年ほど前、[VB.Net で Bluetooth を経由してロボットからログを取得・表示するツール](https://qiita.com/naokishi/items/bd05e42d3df2c7812fb2)を作成する記事を記載しました。

その記事の[最後](https://qiita.com/naokishi/items/bd05e42d3df2c7812fb2#%E6%9C%80%E5%BE%8C%E3%81%AB)で

- 受信したログをファイルに保存
- ログにキーワードを紐付けて、絞り込みを行う

等を追加すると利便性が高まると記載したのですが、その部分を1年越しに追加してみたいと思います！

### 前提条件

[VB.Net で Bluetooth を経由してロボットからログを取得・表示するツール](https://qiita.com/naokishi/items/bd05e42d3df2c7812fb2)の内容に沿って、既にロボットとbluetooth通信が行えていること。

### フォームの配置

前回から新たに追加するコンポーネントは以下の通り。

![cap2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218385/dfc64530-b005-0169-146c-5473e525dc56.png)

### コード(VB.Net)

コンポーネントを配置したら、コードを記載していきます。

#### データセットの追加

プロジェクト内に、新規にデータセットを追加します。

追加方法は以下の通りです。

1. ソリューションエクスプローラー内のプロジェクト名を右クリック
2. コンテキストメニューで`追加` -> `新しい項目`を選択
3. 新しい項目の追加ダイアログで`データセット`を選択

また、追加したデータセットへ、下図のような名称で DataTable を追加、その DataTable 内に列(DataColumn)を2つ追加します。

![プロパティ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218385/66152d32-96f0-fe39-2aeb-8ef97e3a4428.png)

更に、コードとして以下のクラス・メソッドを追加します。

```vb:EtRoboData.vb
Partial Class EtRoboData

    Public Function AddBtRecvLogRow(ByVal recv As String) As String

        Const DELIMITER As String = ")]"

        Dim row As BtRecvLogRow = Me.BtRecvLog.NewBtRecvLogRow()

        Dim index As Integer = recv.IndexOf(DELIMITER)

        If index < 0 Then

            row.Tag = "notag"
            row.Log = recv

        Else

            row.Tag = recv.Substring(0, index)
            row.Log = recv.Substring(index + DELIMITER.Length)

        End If

        Me.BtRecvLog.AddBtRecvLogRow(row)

        Return row.Tag

    End Function

End Class
```

追加したデータセットをフレーム内にメンバ変数として追加。

```vb:frmMain.vb(追加)
    Private m_EtRoboData As New EtRoboData() '追加
    Private Delegate Sub addRecvLogDataDelegate(recv As String)
```

前回追加した`addRecvLogData`メソッドを以下のように変更。

```vb:frmMain.vb(更新)
    Private Sub addRecvLogData(recv As String)

        Dim lines() As String = Split(recv.Replace(vbCr, String.Empty), vbLf)

        If lines.Length <= 1 Then

            Return

        End If

        For Each line As String In lines

            If String.IsNullOrEmpty(line) Then

                Continue For

            End If

            Console.WriteLine(line)

            addCmbTag(m_EtRoboData.AddBtRecvLogRow(line))
            Me.lstLog.SelectedIndex = Me.lstLog.Items.Count - 1

        Next

    End Sub
```

今回追加したコンポーネント群の初期化処理。

```vb:frmMain.vb(追加)
    Private Sub initDataTable()

        Me.lstLog.DataSource = m_EtRoboData.BtRecvLog
        Me.lstLog.DisplayMember = m_EtRoboData.BtRecvLog.Columns(1).ColumnName

    End Sub

    Private Sub clearCmbTag()

        Me.cmbTag.Items.Clear()
        Me.cmbTag.Items.Add(String.Empty)

    End Sub

    Private Sub addCmbTag(ByVal tag As String)

        If Me.cmbTag.Items.Contains(tag) Then

            Return

        End If

        Me.cmbTag.Items.Add(tag)

    End Sub
```

#### イベントハンドラ

追加した各初期化処理をロード部分へ追加、さらに新しく追加したコンポーネントのイベントハンドラを追加します。

```vb:frmMain.vb(更新)
    Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        initPortName()
        initDataTable() '追加
        clearCmbTag()   '追加

    End Sub
```

```vb:frmMain.vb(追加)
    Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click

        Dim dialog As New SaveFileDialog()

        With dialog

            'ダイアログボックスで表示するファイル名
            .FileName = DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".log"

            'ファイル種類
            .Filter = "ログファイル(*.log)|*.log|すべてのファイル(*.*)|*.*"

            'タイトル
            .Title = "ログ保存先"

            '前回保存先フォルダの保持
            .RestoreDirectory = True

            '既存ファイルのチェック
            .OverwritePrompt = True

            If .ShowDialog() = DialogResult.OK Then

                Using sr As New StreamWriter(.FileName, False, System.Text.Encoding.UTF8)

                    For Each row As EtRoboData.BtRecvLogRow In m_EtRoboData.BtRecvLog.Rows()

                        sr.WriteLine(row.Tag + "," + row.Log)

                    Next

                End Using

            End If

        End With

    End Sub
```

```vb:frmMain.vb(追加)
    Private Sub cmbTag_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbTag.SelectedIndexChanged

        Dim search As String = Me.cmbTag.Text.TrimEnd()

        If String.IsNullOrEmpty(search) Then

            Me.lstLog.DataSource = m_EtRoboData.BtRecvLog

        Else

            Dim query = From row As EtRoboData.BtRecvLogRow In m_EtRoboData.BtRecvLog.AsEnumerable
                        Where row.Tag = search

            Me.lstLog.DataSource = query.AsDataView()

        End If

    End Sub
```

### コード (C)

続いて、ロボット側のログ送信部分を以下のように変更します。

```c
    const char *pTag1 = "hogehoge";
    const char *pTag2 = "test";
    const char *pMsg1 = "Hello EV3!";
    const char *pMsg2 = "Good Bye EV3!";

    FILE* sp;

    sp = ev3_serial_open_file(EV3_SERIAL_DEFAULT)

    fprintf(sp, "%s)],%s\r\n", pTag1, pMsg1);
    fprintf(sp, "%s)],%s\r\n", pTag2, pMsg2);

    fclose(sp);
```

### 動作確認

では、動作確認です。
ツールとロボットを起動すると、以下のようにログが表示されます。

![test1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218385/2d77b972-d19c-a968-e51b-b3a2975e49db.png)

さらにログを受信した状態でコンボボックスをクリックすると、ログに紐付けたタグが表示されます。
補足ですが、タグの部分を未指定(空文字)にすると`notag`として紐づきます。

![test2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218385/bca5c905-b935-c87d-20b7-17697cdfd484.png)

試しに`hogehoge`を選択すると、`Hello EV3!`のみ絞り込まれて表示できます。
(この機能を使えば、PID部分だったり、難所部分のログといった感じで絞り込みが行えます)

![test3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218385/8c24a342-041b-f708-a52b-2c809075a32c.png)

あとは、保存ボタンを押下すると、ログ保存先ダイアログが表示されて保存ができます。

![ログ保存.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218385/da66799e-a750-c740-371d-e906445ef9d3.png)

保存したログを開くと、タグと一緒に保存できています。

![log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218385/bc19853e-63b4-0f38-529e-4db084f90b88.png)

### 最後に

今年も[ETロボコン](https://www.etrobo.jp/)に参加しましたが、昨年同様、大変な半年でした。
そんな中、[リンク情報システム](https://www.lis.co.jp/)さんが開催して頂いた非公式試走会のおかげで、本番に近い環境で色々と試せたことは非常に助かりました。
改めまして、ありがとうございました！

因みに、今回動確をしていて思ったのですが、bluetoothの接続状態等を表示すると、もっと使いやすいかもしれませんね。
※ 来年度の記事への布石ではありません(笑)