「C Sharpの基礎 - シリアル通信」の版間の差分

351行目: 351行目:


== その他のシリアル通信の機能 ==
== その他のシリアル通信の機能 ==
==== 送信 ====
以下の例では、バッファリング、再接続機能を使用して、非同期でデータを送信している。<br>
これにより、大量のデータを扱う場合や不安定な接続環境での使用に適している。<br>
<br>
* バッファリング
*: ConcurrentQueue<string>を使用して、送信データをバッファリングする。
*: また、バッファの最大サイズを制限して、オーバーフローを防ぐ。
* 再接続機能
*: 接続が失敗した場合に複数回試行する。
*: また、接続状態を監視して、切断された場合に再接続を試みる。
* 非同期処理
*: バッファリングされたデータを非同期に送信する。
<br>
バッファリング機能により、一時的な接続問題や送信の遅延がある場合でもデータ損失のリスクを軽減する。<br>
<br>
<syntaxhighlight lang="c#">
using System;
using System.Text;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
class AdvancedAsyncSender
{
    private static SerialPort _serialPort;
    private static ConcurrentQueue<string> _sendBuffer = new ConcurrentQueue<string>();
    private static int _maxBufferSize    = 100;  // 送信バッファの最大サイズ
    private static int _reconnectAttempts = 5;    // 再接続の試行回数
    private static int _reconnectDelay    = 5000;  // 再接続の待機時間 (ミリ秒)
    static async Task Main(string[] args)
    {
      string portName  = "/dev/ttyS0";  // ポート名を適切に設定
      int baudRate      = 9600;          // ボーレート 9600[bps]
      Parity parity    = Parity.None;  // パリティ無し
      int dataBits      = 8;            // データ長は8ビット
      StopBits stopBits = StopBits.One;  // ストップビットは1ビット
      try
      {
          _serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
          _serialPort.Handshake = Handshake.None;  // フロー制御の設定
          _serialPort.ReadTimeout = 500;          // 読み取りタイムアウトの設定 (ミリ秒)
          _serialPort.WriteTimeout = 500;          // 書き込みタイムアウトの設定 (ミリ秒)
          await ConnectWithRetry();
          Console.WriteLine("シリアルポートをオープン");
          Console.WriteLine("終了するには 'exit' と入力");
          using (var cts = new CancellationTokenSource())
          {
            var sendTask = SendDataAsync(cts.Token);
            var monitorTask = MonitorConnectionAsync(cts.Token);
            while (true)
            {
                Console.Write("送信するメッセージを入力: ");
                string message = Console.ReadLine();
                if (message.ToLower() == "exit")
                {
                  cts.Cancel();
                  break;
                }
                if (_sendBuffer.Count < _maxBufferSize)
                {
                  _sendBuffer.Enqueue(message);
                }
                else
                {
                  Console.WriteLine("警告: 送信バッファのオーバーフローが発生 (データを破棄)");
                }
            }
            await Task.WhenAll(sendTask, monitorTask);
          }
      }
      catch (Exception ex)
      {
          Console.WriteLine($"エラーが発生: {ex.Message}");
      }
      finally
      {
          _serialPort?.Close();
      }
    }
    private static async Task ConnectWithRetry()
    {
      for (int i = 0; i < _reconnectAttempts; i++)
      {
          try
          {
            _serialPort.Open();
            return;
          }
          catch (Exception ex)
          {
            Console.WriteLine($"接続試行 {i + 1} 失敗: {ex.Message}");
            if (i < _reconnectAttempts - 1)
            {
                await Task.Delay(_reconnectDelay);
            }
          }
      }
      throw new Exception("接続に失敗しました。");
    }
    private static async Task SendDataAsync(CancellationToken cancellationToken)
    {
      while (!cancellationToken.IsCancellationRequested)
      {
          if (_sendBuffer.TryDequeue(out string message))
          {
            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(message + Environment.NewLine);
                await _serialPort.BaseStream.WriteAsync(buffer, 0, buffer.Length, cancellationToken);
                await _serialPort.BaseStream.FlushAsync(cancellationToken);
                Console.WriteLine("メッセージが非同期で送信完了");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"送信中にエラーが発生: {ex.Message}");
                _sendBuffer.Enqueue(message);  // 送信失敗したメッセージを再度キューに追加
            }
          }
          else
          {
            await Task.Delay(100, cancellationToken);  // バッファが空の場合は100[ミリ秒]待機
          }
      }
    }
    private static async Task MonitorConnectionAsync(CancellationToken cancellationToken)
    {
      while (!cancellationToken.IsCancellationRequested)
      {
          if (!_serialPort.IsOpen)
          {
            Console.WriteLine("接続が切断されたので再接続を試行...");
            await ConnectWithRetry();
            Console.WriteLine("再接続に成功");
          }
          await Task.Delay(1000, cancellationToken); // 1秒ごとに接続状態をチェック
      }
    }
}
</syntaxhighlight>
<br>
==== 受信 ====
==== 受信 ====
以下の例では、バッファリング、再接続機能、イベントベースの受信を行っている。<br>
以下の例では、バッファリング、再接続機能、イベントベースの受信を行っている。<br>
502行目: 656行目:
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>
<br>
==== 送信 ====
<br><br>
<br><br>