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

ナビゲーションに移動 検索に移動
306行目: 306行目:
             Console.WriteLine($"受信中にエラーが発生: {ex.Message}");
             Console.WriteLine($"受信中にエラーが発生: {ex.Message}");
           }
           }
      }
    }
}
</syntaxhighlight>
<br><br>
== その他のシリアル通信の機能 ==
以下の例では、バッファリング、再接続機能、イベントベースの受信を行っている。<br>
これにより、大量のデータを扱う場合や不安定な接続環境での使用に適している。<br>
<br>
* バッファリング
*: ConcurrentQueue<byte[]>を使用して、受信データをバッファリングする。
*: また、バッファの最大サイズを制限して、オーバーフローを防ぐ。
* 再接続機能
*: 接続が失敗した場合に複数回試行する。
*: また、接続状態を監視して、切断された場合に再接続を試みる。
* イベントベースの受信
*: SerialPort_DataReceivedイベントハンドラを使用して、データ受信時の処理を行う。
* 非同期処理
*: バッファリングされたデータを非同期に処理する。
<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 AdvancedAsyncReceiver
{
    private static SerialPort _serialPort;
    private static ConcurrentQueue<byte[]> _dataBuffer = new ConcurrentQueue<byte[]>();
    private static int _maxBufferSize    = 10;    // 受信バッファの最大サイズ
    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;          // 書き込みタイムアウトの設定 (ミリ秒)
          _serialPort.DataReceived += SerialPort_DataReceived; // イベントハンドラの登録
          await ConnectWithRetry();
          Console.WriteLine("シリアルポートをオープン");
          Console.WriteLine("終了するには [Ctrl] + [C]キーを押下");
          using (var cts = new CancellationTokenSource())
          {
            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                cts.Cancel();
            };
            await Task.WhenAll(
                      ProcessBufferedDataAsync(cts.Token),
                      MonitorConnectionAsync(cts.Token)
            );
          }
      }
      catch (OperationCanceledException)
      {
          Console.WriteLine("エラー: 受信が中断");
      }
      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 void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
      int bytesToRead = _serialPort.BytesToRead;
      byte[] buffer = new byte[bytesToRead];
      _serialPort.Read(buffer, 0, bytesToRead);
      if (_dataBuffer.Count < _maxBufferSize)
      {
          _dataBuffer.Enqueue(buffer);
      }
      else
      {
          Console.WriteLine("警告: バッファオーバーフロー");
          Console.WriteLine("データが破棄されました");
      }
    }
    private static async Task ProcessBufferedDataAsync(CancellationToken cancellationToken)
    {
      while (!cancellationToken.IsCancellationRequested)
      {
          if (_dataBuffer.TryDequeue(out byte[] data))
          {
            string receivedData = Encoding.UTF8.GetString(data);
            Console.WriteLine($"受信したデータ: {receivedData.Trim()}");
          }
          else
          {
            await Task.Delay(100, cancellationToken);  // バッファが空の場合は、100[mS]待機
          }
      }
    }
    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[秒]ごとに接続状態を確認
       }
       }
     }
     }

案内メニュー