前提
Windows10のUSB仮想シリアルドライバを利用してUSBフルスピード機器から測定データ受信をさせようとしています。
バルク転送で理論ボーは約2.1Mbps相当となります。
USB機器は停止要求か通信エラーが発生するまで連続で絶えずデータを送ってきます。
実装としては、
- C#のSerialPortクラスDatarecievedイベントを利用して、Serialポートバッファにデータが溜まったら通知を受けてアプリのバッファに格納する処理を行っています。
- system.Timers.Timerで100ms間隔でアプリバッファの内容を解釈させております。
現状受信イベントが遅れることがあり、Serialバッファが溢れる問題に直面しております。
実現したいこと
目標として3h程度は安定して受信したい。(現状0分~30分で問題発生)
-> 他の処理があっても、Serial受信->バッファへ格納を最優先に処理させたいです。
発生している問題・エラーメッセージ
受信イベント発生が遅れSerrialバッファが溢れて、取りこぼしが発生します。
バッファ満タンの状態になると、USB機器が送信を停止し、一定時間以内に解消されなければエラーを出して送信を止めてしまいます。
serialPort.ReadBufferSizeを大きくしても内部的にはサイズが変わらないようです。(usbser.sysの制約?)
現状は以下がわかっています。
① 受信イベント発生が遅れ、急に大きなデータがSerialバッファ溜まる。大抵この時点でバッファが溢れる。
②windowsの他の操作をすると処理が遅れる・・・気がする。(word等のソフト立ち上がる、フォーカスを変える)
③100msタイマーを切ると、イベントの遅れが少なくなり、溢れるまでの時間が長くなる・・・気がする。
問題発生時のログ
No.26449:受信イベント6,サイズ2294,前回からの経過時間7.247ms
スレッド6:バッファ格納時間0.041ms,格納サイズ2294
No.26450:受信イベント6,サイズ2046,前回からの経過時間6.5969ms
スレッド6:バッファ格納時間0.1644ms,格納サイズ2046
No.26451:受信イベント17,サイズ1984,前回からの経過時間6.2961ms
スレッド17:バッファ格納時間0.3198ms,格納サイズ1984
No.26452:受信イベント21,サイズ1612,前回からの経過時間5.16ms
スレッド21:バッファ格納時間0.0484ms,格納サイズ1612
No.26453:受信イベント6,サイズ12338,前回からの経過時間156.9381ms
※↑ここで問題が発生。通信が終了する。通常の10倍以上時間がかり、理論ボーからするとバッファが溢れているようです。
該当のソースコード
現状GUIは受信開始、停止ボタンのみのため、受信とタイマー関係のみ抜粋しています。
(定義が見えないものはstaticで宣言しております。)
C#
1//form.cs 2 3public form() 4{ 5 // タイマ準備(100ms) 6 timer1 = new System.Timers.Timer(time_interval); 7 this.timer1.Elapsed += (sender, e) => Timer1_Tick(sender, e) 8} 9 10private void Timer1_Tick(object sender, EventArgs e) 11 { 12 timer1.Stop(); 13 time_cnt += 1; 14 if (timerThread != null && !timerThread.IsCompleted) 15 return; 16 timerThread = Task.Run(() => 17 { 18 // 解析 19 data_analysis(); 20 } 21 timer1.Start(); 22 } 23 24/* 動作開始*/ 25private void start_button_Click(object sender, EventArgs e) 26{ 27 28 // USB機器へ送信要求 29 usb_start(); 30 timer1.Start(); 31} 32
C#
1// com.cs 2 3private void serialPort_DataReceivedobject sender, SerialDataReceivedEventArgs e) 4 { 5 // イベント停止(あってもなくても結果は変わらない模様) 6 serialPort.DataReceived -= new SerialDataReceivedEventHandler(serialPort_DataReceived); 7 lock (lockObject) 8 { 9 int id = 0; 10 int worker = 0; 11 int io = 0; 12 13 // ポートチェック 14 if (serialPort != null && serialPort.IsOpen) 15 { 16 SerialPort sp = (SerialPort)sender; 17 id = System.Threading.Thread.CurrentThread.ManagedThreadId; 18 19 try 20 { 21 // バッファサイズ確認 22 int rs = sp.BytesToRead; 23 TimeSpan span = sw.Elapsed; 24 sw.Stop(); 25 Console.WriteLine($"No.{cnt}:受信イベント{id},サイズ{rs},前回からの経過時間{span.TotalMilliseconds}ms"); 26 sw.Restart(); 27 28 // データがあれば処理 29 if (0 != rs) 30 { 31 // 解析を考慮してパケットサイズで格納(あってもなくても結果は変わらない模様) 32 rs = (rs / Constants.PACKET_SIZE) * Constants.PACKET_SIZE; // 33 34 // バッファ格納(関数で飛ばさず、ここでSerialReadして読み捨てても結果は変わらない模様。) 35 set_buffer(ref rs); 36 37 //Console.WriteLine($"No.{cnt}:スレッド{id},終了:経過時間{span.TotalMilliseconds}ms"); 38 cnt++; 39 40 } 41 else 42 { 43 Console.WriteLine("受信なしで抜けている"); 44 } 45 } 46 catch { 47 Console.WriteLine("異常発生") 48 } 49 } 50 } 51 // イベント許可(あってもなくても結果は変わらない模様) 52 serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived); 53 } 54 55
C#
1// rec.cs 2public void set_buffer(ref Int32 size) 3{ 4 Int32 id; 5 id = System.Threading.Thread.CurrentThread.ManagedThreadId; 6 sw.Restart(); 7 // バッファ格納実体 8 sp.Read(log_buffer, wp, size); 9 wp += (Int32)size; 10 if (wp >= max_index) 11 { 12 wp = 0; 13 } 14 TimeSpan span = sw.Elapsed; 15 Console.WriteLine($"スレッド{id}:バッファ格納時間{span.TotalMilliseconds}ms,格納サイズ{size}"); 16}
試したこと
最小コードとして以下のみを実行しました。
発生までの時間は伸びた気がしますが、限定的な改善でした。
C#
1//form.cs 2 3private void start_button_Click(object sender, EventArgs e) 4{ 5 6 // USB機器へ送信要求 7 usb_start(); 8 // タイマ動作なし 9} 10
C#
1private void serialPort_DataReceivedobject sender, SerialDataReceivedEventArgs e) 2 { 3 int id = 0; 4 int worker = 0; 5 int io = 0; 6 7 SerialPort sp = (SerialPort)sender; 8 id = System.Threading.Thread.CurrentThread.ManagedThreadId; 9 10 // バッファサイズ確認 11 Int rs = sp.BytesToRead; 12 TimeSpan span = sw.Elapsed; 13 sw.Stop(); 14 Console.WriteLine($"No.{cnt}:受信イベント{id},サイズ{rs},前回からの経過時間{span.TotalMilliseconds}ms"); 15 sw.Restart(); 16 17 // データがあれば処理 18 if (0 != rs) 19 { 20 21 // 読み捨て 22 sp.read(buf,0,rs); 23 24 Console.WriteLine($"No.{cnt}:スレッド{id},終了:経過時間{span.TotalMilliseconds}ms"); 25 cnt++; 26 27 } 28 else{ } 29 30 return; 31 } 32 33
補足情報(FW/ツールのバージョンなど)
VisualStudioExpress 2017
C#
ここにより詳細な情報を記載してください。

回答4件
あなたの回答
tips
プレビュー