投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/10/3 12:04:40
> 描画終了までの時間が長くなっているということは、
グラフへの描画は、データの受信処理に比べると遥かに低速なので、
それを考慮して設計してみてください。


> Chart1.Series(0).Points.AddXY(time, xdata)

各 Series には名前がセットされているのですから、
 Chart1.Series("X-Axis").Points.AddXY(time , xdata)
の方が良いでしょう。

もしくは、毎回名前やインデックスで検索するのではなく、
「Dim xaxis As Series」な『ローカル変数』を削除して、かわりに
「Private xaxis As Series」な『フィールド変数』へと変更し、
 xaxis = Chart1.Series.Add("X-Axis")
で設定しておいた Series オブジェクトを
 xaxis.Points.AddXY(time, xdata)
のように利用するようにします。


>(a) 受信処理:シリアル通信で送られてきたデータをフィールド変数に蓄えていく
DataReceived のスレッドがフィールド変数に書き込んでいる最中に、
メインスレッドがフィールド変数を同時に読み書きすることが無い様、
フィールド変数が「スレッドセーフ」であることを保証しておく必要もあります。
https://msdn.microsoft.com/ja-jp/library/dd997305.aspx

たとえば、スレッドセーフなコレクションとして、.NET Framework 4 以上で利用可能な
「ConcurrentQueue クラス」を使うといった方法があります。

(a) では、フィールド変数として
 Private qx As New System.Collections.Concurrent.ConcurrentQueue(Of Tuple(Of Double, UInteger))
 Private qy As New System.Collections.Concurrent.ConcurrentQueue(Of Tuple(Of Double, UInteger))
 Private qz As New System.Collections.Concurrent.ConcurrentQueue(Of Tuple(Of Double, UInteger))
などを用意しておき、DataReceived が呼ばれたときに、
  Dim dtBegin As Date = [SerialPortをOpenした時の時刻]
  Dim dtCurrent As Date = DateTime.Now    'データを受信した時刻
  Dim span As TimeSpan = dtCurrent - dtBegin  '経過時間
  Dim totalSeconds As Double = span.TotalSeconds '何秒経過したのか
  'Enqueue メソッドの引数に指定したデータは、コレクションの末尾に追加される
  qx.Enqueue(Tuple.Create(totalSeconds, xdata))
  qy.Enqueue(Tuple.Create(totalSeconds, ydata))
  qz.Enqueue(Tuple.Create(totalSeconds, zdata))
のようにします。


一方、プロットする側(c)においては、任意のタイミング(たとえば Timer1_Tick)で、
下記のようにしてみます。
 Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
  '今回の場合、plot の Item1 As Double が「経過秒数」、Item2 As UInteger が「受信データ」です
  Dim plot As Tuple(Of Double, UInteger) = Nothing
  'TryDequeue メソッドは、先頭にあるデータを 1 つ取り出し、それをコレクションから取り除きます。
  Do While qx.TryDequeue(plot)
   xaxis.Points.AddXY(plot.Item1, CDbl(plot.Item2))
  Loop
  Do While qy.TryDequeue(plot)
   yaxis.Points.AddXY(plot.Item1, CDbl(plot.Item2))
  Loop
  Do While qz.TryDequeue(plot)
   zaxis.Points.AddXY(plot.Item1, CDbl(plot.Item2))
  Loop
 End Sub

このプロット作業は、データの受信タイミングとは無関係に、並列的に行われます。
データがまだ受信されていなければ、上記の処理では何もプロットされませんし、
データが複数件受信されていれば、それらすべてがまとめてプロットされます。


ただし上記の例は、「受信したデータすべて」をプロットするためのものです。

画面の解像度は、精々 1000ドット~2000ドットぐらいしかありませんので、
数千個のデータを受信してしまうと、グラフでは表現しきれないかもしれません。
その場合は、プロットするデータを減らすようにする工夫も必要です。

たとえば直近のn件のみが描画されるようにするため、
一定の件数を超えたら、xaxis.Points.RemoveAt(0) などで古いデータを削除するとか、
あるいは ConcurrentQueue から取り出したデータすべてを AddXY するのではなく、
適当な時間軸単位に「平均を取る」か「間引く」などしてデータを減らすなど。