C((ハ)シリアル接続の読み取りと送信の詳細


一、シリアル接続の開閉
シリアルとは、COMポートであり、NETではSerialPort類を用いて操作します。シリアルオンとオフは、スローハードウェアに関わるIO動作であり、頻繁にオンまたはオフすると全体の処理速度に影響し、シリアルポートのオンまたはオフに失敗することもあります。特別な場合ではなく、クロストークを一度に開けたら、プログラムを終了する時にクロストークをオフにすればいいです。シリアルポートを開く前に、いくつかの一般的なパラメータを設定することができます。よく使うパラメータは以下の通りです。
 (1)シリアルの受信/送信のタイムアウト時間:ReadTimeout/WriteTimeout。
   (2)  シリアルの受信/送信キャッシュエリアサイズ:ReadBufferSize/WriteBufferSize。
具体的なコードは以下の通りです。

// Open Com
   _serialPort = new SerialPort(com, baud);
   if (_serialPort.IsOpen) _serialPort.Close();

   // Set the read / write timeouts
   _serialPort.ReadTimeout = 500;
   _serialPort.WriteTimeout = 500;

   // Set read / write buffer Size,the default of value is 1MB
   _serialPort.ReadBufferSize = 1024 * 1024;
   _serialPort.WriteBufferSize = 1024 * 1024;

   _serialPort.Open();

   // Discard Buffer
   _serialPort.DiscardInBuffer();
   _serialPort.DiscardOutBuffer();
    注意したいのは、バッファを超えた部分はそのまま廃棄されます。したがって、大きなファイルをシリアルポートで転送する必要がある場合、受信者と送信者は、それぞれのバッファ領域を十分に大きく設定して、大きいファイルのバイナリ配列を一度に記憶できるようにする必要がある。条件が制限されていて、バッファ領域が大きすぎて設定できない場合は、大きなファイルを送信する際には、送信バッファサイズによってパケットを分けて送信し、受信者は順番にこの配列を組み合わせて受信ファイルのバイナリ配列を形成する必要があります。
二、シリアル送信
SerialPort類の送信はバイナリ送信とテキスト送信に対応していますが、テキスト送信の際、変換のルールを知る必要があります。一般的にはASCII、UTF 7、UTF-8、UNIcode、UTF 32が一般的です。具体的なコードは以下の通りです。

#region Send
    /// <summary>
    ///     (byte  )
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    public void Send(byte[] buffer, int offset, int count)
    {
      lock (_mux)
      {
        _serialPort.Write(buffer, offset, count);
        _sendCount += (count - offset);
      }
    }

    /// <summary>
    ///     (   )
    /// </summary>
    /// <param name="encoding">       ,     <see cref="Encoding"/></param>
    /// <param name="message"></param>
    public void Send(Encoding encoding , string message)
    {
      lock (_mux)
      {
        var buffer = encoding.GetBytes(message);
        _serialPort.Write(buffer, 0, buffer.Length);
        _sendCount += buffer.Length;
      }
    }
    #endregion
三、シリアル受付
クロストーク受信には、メッセージ受信とメッセージ処理のコード分離が必要です。プロセス処理のコードを情報受付に入れてはいけません。メッセージ処理に多少の時間がかかります。これは送信者が速すぎると、受信側のバッファが複数のメッセージをキャッシュします。受信したメッセージをキューに入れて、外部スレッドでメッセージを取り出して消費することができます。「生産-消費」モードを採用しています。具体的なコードは以下の通りです。

#region Receive
    private void PushMessage()
    {
      _serialPort.DataReceived += (sender, e) =>
      {
        lock (_mux)
        {
          if (_serialPort.IsOpen == false) return;
          int length = _serialPort.BytesToRead;
          byte[] buffer = new byte[length];
          _serialPort.Read(buffer, 0, length);
          _receiveCount += length;
          _messageQueue.Enqueue(buffer);
          _messageWaitHandle.Set();
        }
      };
    }

    /// <summary>
    ///           
    /// </summary>
    /// <param name="millisecondsToTimeout">        </param>
    /// <returns>  byte  </returns>
    public byte[] TryMessage(int millisecondsToTimeout = -1)
    {
      if (_messageQueue.TryDequeue(out var message))
      {
        return message;
      }

      if (_messageWaitHandle.WaitOne(millisecondsToTimeout))
      {
        if (_messageQueue.TryDequeue(out message))
        {
          return message;
        }
      }
      return default;
    }
    #endregion
四、完全コードとテスト結果
シリアルツールの完全なコードは以下の通りです。

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SerialportDemo
{
  public class SSerialPort
  {
    private SerialPort _serialPort;
    private readonly ConcurrentQueue<byte[]> _messageQueue;
    private readonly EventWaitHandle _messageWaitHandle;
    private int _receiveCount, _sendCount;
    private readonly object _mux;
    public int ReceiveCount
    {
      get => _receiveCount;
    }
    public  int SendCount
    {
      get => _sendCount;
    }
    public SSerialPort(string com, int baud )
    {
      // initialized
      _mux=new object();
      _receiveCount = 0;
      _sendCount = 0;
      _messageQueue = new ConcurrentQueue<byte[]>();
      _messageWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

      // Open Com
      OpenCom(com.ToUpper(),baud);

      // Receive byte
      PushMessage();
    }

    private void OpenCom(string com, int baud)
    {
      // Open Com
      _serialPort = new SerialPort(com, baud);
      if (_serialPort.IsOpen) _serialPort.Close();

      // Set the read / write timeouts
      _serialPort.ReadTimeout = 500;
      _serialPort.WriteTimeout = 500;

      // Set read / write buffer Size,the default of value is 1MB
      _serialPort.ReadBufferSize = 1024 * 1024;
      _serialPort.WriteBufferSize = 1024 * 1024;

      _serialPort.Open();

      // Discard Buffer
      _serialPort.DiscardInBuffer();
      _serialPort.DiscardOutBuffer();
    }


    #region Static
    /// <summary>
    ///                 
    /// </summary>
    /// <returns></returns>
    public static string[] GetPortNames()
    {
      return SerialPort.GetPortNames();
    }
    #endregion

    #region Receive
    private void PushMessage()
    {
      _serialPort.DataReceived += (sender, e) =>
      {
        lock (_mux)
        {
          if (_serialPort.IsOpen == false) return;
          int length = _serialPort.BytesToRead;
          byte[] buffer = new byte[length];
          _serialPort.Read(buffer, 0, length);
          _receiveCount += length;
          _messageQueue.Enqueue(buffer);
          _messageWaitHandle.Set();
        }
      };
    }

    /// <summary>
    ///           
    /// </summary>
    /// <param name="millisecondsToTimeout">        </param>
    /// <returns>  byte  </returns>
    public byte[] TryMessage(int millisecondsToTimeout = -1)
    {
      if (_messageQueue.TryDequeue(out var message))
      {
        return message;
      }

      if (_messageWaitHandle.WaitOne(millisecondsToTimeout))
      {
        if (_messageQueue.TryDequeue(out message))
        {
          return message;
        }
      }
      return default;
    }
    #endregion


    #region Send
    /// <summary>
    ///     (byte  )
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    public void Send(byte[] buffer, int offset, int count)
    {
      lock (_mux)
      {
        _serialPort.Write(buffer, offset, count);
        _sendCount += (count - offset);
      }
    }

    /// <summary>
    ///     (   )
    /// </summary>
    /// <param name="encoding">       ,     <see cref="Encoding"/></param>
    /// <param name="message"></param>
    public void Send(Encoding encoding , string message)
    {
      lock (_mux)
      {
        var buffer = encoding.GetBytes(message);
        _serialPort.Write(buffer, 0, buffer.Length);
        _sendCount += buffer.Length;
      }
    }
    #endregion

    /// <summary>
    ///     /      
    /// </summary>
    public void ClearCount()
    {
      lock (_mux)
      {
        _sendCount = 0;
        _receiveCount = 0;
      }
    }

    /// <summary>
    ///     
    /// </summary>
    public void Close()
    {
      _serialPort.Close();
    }
  }
}
テストコードは以下の通りです。

class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine($"            :{string.Join(",", SSerialPort.GetPortNames())}");

      Console.Write("          :");
      string port = Console.ReadLine();
      SSerialPort com = new SSerialPort(port, 57600);
      Console.WriteLine($"   {port}     ...");

      Console.Write("               :");
      string text = Console.ReadLine();

      while (true)
      {
        com.Send(Encoding.Default, text);
        Console.WriteLine($"     {com.SendCount}");
        var message = com.TryMessage();
        if (message != null)
        {
          Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss fff")} {Encoding.Default.GetString(message)}");

          //// TEST:          ,                  。                  ,             。       ,          
          //System.Threading.Thread.Sleep(100*1);
        }
        Console.WriteLine($"     {com.ReceiveCount}");
      }


      Console.ReadKey();
    }
  }
以下のようにシリアルツールを用いて試験したが、シリアルポートの受け入れはシルクのように滑らかであった。メッセージにテストの遅延を追加すると、シリアルツールが一時的に速く送信し続けた後に送信を停止し、キューを使用した後も送信者からのメッセージが失われていないことが分かります。
締め括りをつける
ここでは、C萼口接続の読み取りと送信に関する記事を紹介します。これに関連して、より多くのC菗口接続読み取りと送信内容を紹介します。私たちの以前の文章を検索してください。または下記の関連記事を引き続き閲覧してください。これからもよろしくお願いします。