1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

.NET MAUIでのBluetooth/USBデータ送信実装:オフライン環境下のデータ同期

Posted at

はじめに:現場のタブレットに課せられた役割

ネットワークから切り離されたタブレットの役割は、「未送信の検査データをローカルDBに一時蓄積し、PCと接続できた瞬間にそれらを一括放流すること」です。

今回は、CommunityToolkit.Mvvm を使用したクリーンなViewModelと、Android固有機能(BluetoothAdapter/TcpListener)を組み合わせた実装を解説します。

1. Android固有のBluetooth送信サービス

Bluetooth通信では、標準的なSPP (Serial Port Profile) のUUIDを使用します。これにより、前回のPC側(WPF)の BluetoothListener と通信が可能になります。

実装のポイント
UUIDの固定:00001101-0000-1000-8000-00805f9b34fbを使用します。

RFCOMMソケット: ソケットを作成し、ConnectAsync() でペアリング済みPCに接続します。

終端文字: 受信側の ReadLine() を成立させるため、JSONの末尾に \n を付与します。

#if ANDROID
public async Task<bool> SendDataAsync(BluetoothDeviceDto deviceDto, string json)
{
    BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
    BluetoothDevice device = adapter.GetRemoteDevice(deviceDto.Address);
    BluetoothSocket socket = null;

    try {
        socket = device.CreateRfcommSocketToServiceRecord(SppUuid);
        await socket.ConnectAsync();

        using var stream = socket.OutputStream;
        byte[] data = Encoding.UTF8.GetBytes(json + "\n"); // 終端文字が肝
        await stream.WriteAsync(data, 0, data.Length);
        await stream.FlushAsync();
        return true;
    }
    catch (Exception) { return false; }
    finally { socket?.Close(); }
}
#endif

2. USB(ADBポートフォワーディング)での送信

Bluetoothが不安定な現場を想定し、USB有線接続もサポートします。ここでは、Android側で TcpListener を立てて待ち受け、PC側(ADB接続)からの要求に対してデータを流し込みます。

public async Task<bool> SendDataAsync(string json)
{
    TcpListener server = new TcpListener(IPAddress.Any, 12345);
    server.Start();
    
    // PC側(ADB)からの接続を30秒間待機
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
    // ... (Pendingを確認しながらAcceptTcpClient)
    
    using var stream = tcpClient.GetStream();
    byte[] data = Encoding.UTF8.GetBytes(json + "\n");
    await stream.WriteAsync(data, 0, data.Length);
}

3. ViewModelでの同期管理

ViewModelでは、「未送信件数の把握」と「同期成功後のステータス更新」を管理します。

同期フローの制御
データのシリアライズ: DBから取得した未送信レコードを JsonSerializer で一行の文字列に。

送信実行: 選択されたモード(BT/USB)で送信。

DB更新: 送信成功時のみ、ローカルDBの「送信済みフラグ」を更新。これにより二重送信を防止します。

[RelayCommand(CanExecute = nameof(CanSync))]
private async Task SyncNow()
{
    IsBusy = true;
    var unsentRecords = await _repository.GetUnsentRecordsAsync();
    string jsonPayload = JsonSerializer.Serialize(unsentRecords);

    bool success = IsBluetoothMode 
        ? await _bluetoothService.SendDataAsync(SelectedDevice, jsonPayload)
        : await _usbService.SendDataAsync(jsonPayload);

    if (success) {
        // 送信成功したレコードのみフラグを立てる
        foreach (var record in unsentRecords) {
            await _repository.UpdateSyncStatusAsync(record.InspectionId);
        }
        await RefreshUnsentCount(); // 画面上の「未送信:0件」へ
    }
}

4. 現場での運用の注意点

① 権限の壁(Android 12以降)
Bluetoothのデバイス検索や接続には、BLUETOOTH_CONNECT や BLUETOOTH_SCAN 権限が必要です。MAUI側で Permissions.RequestAsync を呼び出し、ユーザーに許可を求めるフローが必須となります。

② ペアリング済みリストの活用
現場でスムーズに接続するため、あらかじめPCとタブレットをOS設定でペアリングしておき、アプリ側では GetPairedDevices() で取得したリストからPCを選択するだけの設計にしています。

③ 送信完了のフィードバック
ネットワークがない環境では、「本当に送れたのか」がユーザーにとって最大の不安要素です。送信成功時に DisplayAlert で件数を明示し、ローカルDBの状態を即座にUIに反映させることが重要です。

まとめ

.NET MAUI側の送信コードが完成したことで、PC側と対になる 「オフライン同期システム」 のパズルが完成しました。

MAUI: 未送信データをJSON化してBT/USBソケットに流し込む。

WPF: 受信したJSONをバラして隔離ネットワーク内のDBへ仲介する。

この構成により、Wi-Fiに一切頼らない、堅牢なデータ収集ソリューションが実現できました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?