C#シリアルポートマルチスレッド送受信衝突 -- c# フィールド と multithreading フィールド と .net-core フィールド と serial-port フィールド 関連 問題

C# SerialPort multithread send/receive collisions












1
vote

問題

日本語

C#コマンドラインアプリケーション(State Machine)とSerialPortを介して接続されている外部デバイスとの間で通信しようとしています。私は.NET Core 3.1.0アプリケーションとSytem.io.ports(4.7.0)を使用しています。私はFTDI USBシリアルアダプタを使用しています。

私はデータで示されたデバイスの状態をマイコンピュータに送信し、デバイスの状態に応じてコマンドで返信しなければならない。

Puttyを使って届く問題なく

キーボードからの入力としてコマンドラインを使用して出力する場合は、問題なくになります。

残念ながら、別のスレッドからのデータをデバイスに送信するとき、両方のサイトが同時に送信され、ターゲットがクラッシュする collision が発生します。

<事前> <コード> Initialization: _port = new SerialPort( Parameters.TelnetCOMport, Parameters.TelnetBaudrate, Parameters.TelnetParity, Parameters.TelnetDataBits, Parameters.TelnetStopBits); _port.DataReceived += Port_DataReceived; _port.ErrorReceived += Port_ErrorReceived; _port.Handshake = Handshake.RequestToSend; _port.RtsEnable = true; _port.DtrEnable = true; _port.Encoding = Encoding.ASCII; _port.Open(); private void Send_Command(string command) { lock (_portLock) { _port.Write(command + " "); } } private string _dataReceived; private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) { lock (_portLock) { byte[] data = new byte[_port.BytesToRead]; _port.Read(data, 0, data.Length); _dataReceived += Encoding.UTF8.GetString(data); ProcessDataReceived(); } }

私はこの状況を鍵のままにしないようにしましたが、それは助けませんでした。新しいコマンドを送信する前にデバイスによって何もしないことを確認するために、1つのステータスインジケータを受信した後に非常に長い遅延を追加するのに役立つ唯一のものは非常に長い遅延を追加することでした。

だから私の質問は: マルチスレッドアプリケーションからシリアルポートを介してデータを送信しながら衝突を回避する方法?

英語

I am trying to communicate between a C# commandline application (State machine) and an external device connected via SerialPort. I have a .Net Core 3.1.0 application and am using Sytem.IO.Ports (4.7.0). I am using a FTDI USB-serial adapter.

I have to monitor the device state indicated by data send to my computer and reply with commands depending on the device state to get to the next state.

When using Putty send and receive works without problem.

When using the command line as input from keyboard and output it works without problem.

Unfortunately when sending data from another thread to the device it seems that both sites send at the same time and a collision occurs that crashes my target.

    Initialization:     _port = new SerialPort(                     Parameters.TelnetCOMport,                     Parameters.TelnetBaudrate,                     Parameters.TelnetParity,                     Parameters.TelnetDataBits,                     Parameters.TelnetStopBits);                 _port.DataReceived += Port_DataReceived;                 _port.ErrorReceived += Port_ErrorReceived;                 _port.Handshake = Handshake.RequestToSend;                 _port.RtsEnable = true;                 _port.DtrEnable = true;                 _port.Encoding = Encoding.ASCII;                 _port.Open();     private void Send_Command(string command)     {         lock (_portLock)         {             _port.Write(command + " ");         }     }      private string _dataReceived;      private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)     {         lock (_portLock)         {             byte[] data = new byte[_port.BytesToRead];             _port.Read(data, 0, data.Length);             _dataReceived += Encoding.UTF8.GetString(data);             ProcessDataReceived();         }     } 

I tried to avoid this situation with a lock but it did not help. The only thing that helped was to add a very long delay after receiving one status indicator to make sure that definetly nothing will be send by the device before sending a new command.

So my question is: How to avoid a collision while sending data over SerialPort from a multithread application?

</div
           
     
     

回答リスト

0
 
vote
vote
ベストアンサー
 

私の質問をコメントしていただきありがとうございます!

.NET FrameworkのSystem.IO.Portsクラスの品質が非常に悪いためにエラーが発生することがわかりました。

System.IO.Portsクラスが正しく送信(CTS)を正しく処理しないため、送信/受信が発生する可能性があります。

私はベンのvoigtsが非常に良い記事:

.NETに出荷するSystem.IO.Ports.SerialPortクラスは、GLARING例外です。それを穏やかにするために、それは彼らのコア能力の分野の外側にあるコンピュータ科学者によって設計されました。それらは、シリアル通信の特性も一般的なユースケースを理解し、それが示す。また、文書化されたインタフェースと文書化されていない行動と共通していない行動の両方を見つけずに、輸送前の実際の世界のシナリオでテストされていても、System.IO.Ports.Serialport(IOPSP)を使用して信頼性の高いコミュニケーションを実現していません。 (スタックオーバーフローに関する証拠は、ハイパーターミナルで動作するが.NET ...

ではなくこれに証明しています。

最悪の問題のあるSystem.IO.Ports.SerialPortメンバー、使用されるべきではなく、深いコードの匂いの兆候、すべてのIOPSPの使用を行う必要性のあるもの:

  • Datereceivedイベント(100%冗長、完全に信頼できない)
  • bytestoreadプロパティ(完全に信頼できない) 読み取り、読み書き方法、readlineメソッド(エラーを完全に間違って、同期している)
  • PinChangedイベント(あなたがそれについて知りたいのかもしれないすべての面白いことに関して順不同で配信されます)

この問題を回避するには、次のようにして自分の serialports クラスを書いています。

  1. 私はこの完璧な記事 MSDNからのシリアル通信について
  2. 私は github
  3. から例MTTTYコードをダウンロードしました。
  4. MTTTYの例を.dllとして再設計して、C#
  5. から呼び出すことができます。
  6. 私は

    リスナースレッドは以下のように呼び出されます。 <事前> <コード> private static Socket _sock; private static IPEndPoint _endPoint; public StartSerialPortListenerThread() { // This socket is used to send Write request to thread in C dll _sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPAddress serverAddr = IPAddress.Parse("127.0.0.1"); _endPoint = new IPEndPoint(serverAddr, 5555); // This starts the listener thread in the C dll Task.Factory.StartNew(() => { ConnectToSerialPort(TelnetCallback, StatusCallback, ErrorCallback); }); }

    送信コマンドは、UDPデータグラム <コード> private void SendCommand(string command) { byte[] sendBuffer = Encoding.ASCII.GetBytes(command + ' '); _sock.SendTo(sendBuffer, _endPoint); }

    私はMTTTYの例を非常に慎重に触れただけで、私のDLLは例(非常に信頼できる!)に非常にシミュラされています。

    私はこれまでにいくつかのクリーンアップをしなければならないので、私はこれまでに利用可能ではありません。あなたができるだけ早く私は要求によって誰かにそれを送ることができます。

 

Thank you for commenting my question!

It turned out, that the error occurs because of the very bad quality of the System.IO.Ports class of the .NET Framework.

As the System.IO.Ports class does not handle the Clear-To-Send (CTS) correctly and therefore colliding send/receive could occur.

I will cite Ben Voigts very good article:

The System.IO.Ports.SerialPort class which ships with .NET is a glaring exception. To put it mildly, it was designed by computer scientists operating far outside their area of core competence. They neither understood the characteristics of serial communication, nor common use cases, and it shows. Nor could it have been tested in any real world scenario prior to shipping, without finding flaws that litter both the documented interface and the undocumented behavior and make reliable communication using System.IO.Ports.SerialPort (henceforth IOPSP) a real nightmare. (Plenty of evidence on StackOverflow attests to this, from devices that work in Hyperterminal but not .NET...

The worst offending System.IO.Ports.SerialPort members, ones that not only should not be used but are signs of a deep code smell and the need to rearchitect all IOPSP usage:

  • The DataReceived event (100% redundant, also completely unreliable)
  • The BytesToRead property (completely unreliable) The Read, ReadExisting, ReadLine methods (handle errors completely wrong, and are synchronous)
  • The PinChanged event (delivered out of order with respect to every interesting thing you might want to know about it)

To work around this issue I have written my own SerialPorts class by doing the following:

  1. I read this perfect article about serial communication from MSDN
  2. I downloaded the example MTTTY code from github
  3. I redesigned the MTTTY example as a .dll so it can be invoked from C#
  4. I learned how to invoke callbacks in C# from C
  5. I have redesigned the MTTTY example to run a ReaderThread that listens continously and a WriterThread that can be invoked via an UDP datagram

The C# code looks as below:

    #region Load C DLL     [UnmanagedFunctionPointer(CallingConvention.StdCall)]     private delegate void ProgressCallback(IntPtr buffer, int length);      [DllImport("mttty.dll")]     static extern void ConnectToSerialPort([MarshalAs(UnmanagedType.FunctionPtr)] ProgressCallback telnetCallbackPointer,         [MarshalAs(UnmanagedType.FunctionPtr)] ProgressCallback statusCallbackPointer,         [MarshalAs(UnmanagedType.FunctionPtr)] ProgressCallback errorCallbackPointer);     #endregion      #region Callbacks      private void TelnetCallback(IntPtr unsafeBuffer, int length)     {         byte[] safeBuffer = new byte[length];         Marshal.Copy(unsafeBuffer, safeBuffer, 0, length);         Console.WriteLine(Encoding.UTF8.GetString(safeBuffer));     }      private void StatusCallback(IntPtr unsafeBuffer, int length)     {         byte[] safeBuffer = new byte[length];         Marshal.Copy(unsafeBuffer, safeBuffer, 0, length);                     Console.WriteLine("Status: "+Encoding.UTF8.GetString(safeBuffer));     }      private void ErrorCallback(IntPtr unsafeBuffer, int length)     {         byte[] safeBuffer = new byte[length];         Marshal.Copy(unsafeBuffer, safeBuffer, 0, length);         Console.WriteLine("Error: "+Encoding.UTF8.GetString(safeBuffer));     }     #endregion 

The listener thread then is invoked as below

    private static Socket _sock;     private static IPEndPoint _endPoint;      public StartSerialPortListenerThread()     {         // This socket is used to send Write request to thread in C dll         _sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);         IPAddress serverAddr = IPAddress.Parse("127.0.0.1");         _endPoint = new IPEndPoint(serverAddr, 5555);                  // This starts the listener thread in the C dll         Task.Factory.StartNew(() =>         {             ConnectToSerialPort(TelnetCallback, StatusCallback, ErrorCallback);         });     } 

The send command is invoked very easily by sending a UDP datagram

 private void SendCommand(string command)     {         byte[] sendBuffer = Encoding.ASCII.GetBytes(command + ' ');         _sock.SendTo(sendBuffer, _endPoint);     } 

I only have touched the MTTTY example very carefully so my DLL works very similiar to the example (very reliable!).

I have not made this code available so far because I have to do some cleanup. If you want to get the status quo asap I can send it to anyone by request.

</div
 
 

関連する質問

0  シリアルポート、「全二重」フロー制御に相当しますか?  ( Serial port equivalent of full duplex flow control ) 
シリアルポートを介してデバイスに接続情報を持つ文書を持っています。フロー制御の設定として「全二重」を指定します。 Windowsでは、フロー制御を設定するための次のオプションがあります。 XON / XOFF ハードウェア なし 上記のリストの「全二重...

3  COMポートのロックを解除する方法  ( How to unlock com port ) 
他のアプリケーションの後に働く必要があるアプリケーション。この2番目のアプリケーションには、特定の状況でCOMポートを閉じないようにするバグがあります。 私のアプリケーションでプログラム的にすべてのCOMポートを閉じて、閉じるポートに関するバグが報告されている...

19  OS XまたはLinuxのシリアルポートとプログラムで話す  ( Programmatically talking to a serial port in os x or linux ) 
私は私がApacheログやその他の楽しい統計からスクロール検索クエリを表示するように設定するのが好きなProlite Ledサインを持っています。問題は、My G5にシリアルポートがないため、USBをシリアルドングルに使用する必要があります。それは/dev/c...

2  シリアルポートを使用したCANopen PDO  ( Canopen pdos using the serial port ) 
CANopenプロトコルを理解しようとしています。 今のところ、私はハードウェアやCanopenスタックを実験することはできません。 RS-232ポートで受信されたCanopenメッセージを単に解釈するためのJavaプログラムの作成方法。 シリアルポート...

0  C#UWPシリアル通信は接続されません  ( C sharp uwp serial communication does not connect ) 
UWPを使用したシリアル通信プログラムを作成しています.. シリアル通信が接続されていません。 'デバイス'はキャプチャされません。 NULLを返します。 コードはここにあります。 <事前> <コード> // Get a device selector f...

0  シリアルプログラミング - TERMIOS。デバイスから0x00バイトを読むときに立ち往生しています  ( Serial programming termios getting stuck when reading 0x00 byte from device ) 
termios APIを読み込ませる/書き込み/書き込みシリアルインタフェースに設定されたデバイス。私が使用しているコードは次のことです: <事前> <コード> // Open serial interface const char *device = ...

8  Windows XP / Win32とのシリアル通信の基本的な例  ( Basic example of serial communication with windows xp win32 ) 
私はシリアルを通して伝達される必要がある周辺機器を使って取り組んでいます。 Hyperterminalを使用してITコマンドを送信できますが、今度はハイパーターミナルなしで行わないプログラムを書く必要があります。誰かが私をウェブサイトに指していることや/または...

1  完全な信頼を伴うWPF XBAPを構築しようとしている、購入した証明書がいくつかのセキュリティ問題を超えて取得できますか?  ( Looking to build a wpf xbap with full trust can a purchased certificate get pas ) 
マシン上のいくつかのハードウェア(シリアルポート)と話す必要があるアプリケーションを開発しており、ほとんどWPFと.NETであるため、XBAPは論理的です。 私の懸念は証明書とのものです。私はアプリケーションを作成して自己署名する方法を示す人々の束を見ましたが...

4  モノラルの「フレンドリーな」シリアルポートの名前を取得し、それをクロスプラットフォームに保つことができますか  ( How do i get the friendly name of serial port in mono and keep it cross platfo ) 
この質問をしました: 複数のプラットフォームのためのモノラルのシリアルポート(RS232)とこれは関連しています: どうやってフレンドリーを得ますかWindowsのCOMポートの名前? しかし、私はWindowsで「フレンドリーな」名を取得でき、そのようなこ...

1  実行中のアプリの通信ポート設定を見つける  ( Finding out comm port settings of a running app ) 
状況は次のとおりです。シリアルポートを介して、Windows Running、PCに接続されているハードウェアがあります。このカスタムハードウェアは、GPIBを介して他のハードウェアをインターフェースするために使用されます。さて、PC側でこの設定を操作するため...




© 2022 cndgn.com All Rights Reserved. Q&Aハウス 全著作権所有