リアルタイムグラフの使い勝手UI関係のプログラムを一応完成させました。目的に応じて仕様は全く変わるので、機能をどのコントロールを使ってどのように組み込んであるかを参考にしていただければと存じます。
■仕様(ESP32系USB Driverを使ってます)
①1周期5-10msecで7ch(ASCII37byte)のデータをプロットできます。
=>事前にWIn10のデバイスドライバのCOMのProperty詳細でオプションBM値を16msecから1msecに変更しないとリアルタイムになりません。
②受信ボーレートは460800bpsまでOKでした。
③OPENからCLOSEまで全部CSV自動ログできます。
④グラフの各CHスケール位置調整機能各種が付き
■グラフ設定機能
①X(時間軸)スケール<ComboBox> 単位:dots/data
②Y(電圧軸)位置スライダ<TrackBar>単位:Y軸フルスケール100%
③Y(電圧軸)倍率CH毎設定<ComboBox>単位:dot/mV
④ゼロレベル自動合わせ全CHを中央位置50%にそろえる機能
■データ管理機能
①年月日時刻ファイル名で自動ログ(CSV)
②ダイアログで任意のフォルダー、ファイル名でログ
③グラフ描画速度測定機能(デバッグ中)
※2021年11月9日 流れるリアルタイムグラフを作ってみました。BITMAPに一旦データを書き込んでから、切り取って表示する方式
その2で、グラフ全体から所望の部分を瞬時に閲覧できるので、リアルタイムで測定データの確認作業が出来るように作ります。
【VB.NET】リアルタイムグラフのBITMAP処理化<その1小技在り>
※グラフィック備忘録
①PictureBoxとBitmapを定義してしまう場合
参考LINK PictureBox.Imageプロパティに設定している画像を描き変えた時、すぐに表示に反映する
PictureBoxは、額縁に相当します。額縁に差し込む絵は、紙であるBitmapオブジェクトです。
Bitmapに描画するには、それぞれのBitamap専用のグラフィック処理クラス graphicsを定義します。
Dim bmp as new Bitmap(PictureBox1.Width,PictureBox1.Height)’bmpオブジェクトを定義
Dim g as Graphics =Graphics.FromImage(bmp)’bmpのグラフィクス処理を定義
g.DrawLine(Pens Red,x0,y0,x1,y1)’ bmpビットマップへ線が描画されてPictureBoxに反映
②PictureBoxとは独立させてBitmapを処理するしてからPictureBoxに反映させる場合
(参考https://dobon.net/vb/dotnet/graphics/createimage.html )
Dim bmp as new Bitmap(1000,400)’1000×400にビットマップを定義
Dim g as Graphics =Graphics.FromImage(bmp)’bmpのグラフィクス処理を定義
g.DrawLine(Pens Red,x0,y0,x1,y1)’ bmpビットマップへ線が描画する
PictureBox1.Image=bmp ‘bmpをPictureBox1へ差し込む
この場合ビットマップに各種処理をした後で、PictureBox表示できるので、便利です。
■使っているメソッド(関数)クラス備忘録
ソースはGIT GISTにアップしてあります。
https://gist.github.com/dj1711572002/580544a86669ce72455a3341c030edfe
FORMを手作りするのが大変なので、今回からソリューションをZIPで添付します。
Serial_Graphic_Monitor_rev01
※7ch受信専用に作ってあるので、後日CH指定自由にできるバージョンにして配布します。
=>splitでマイコン側が変な文字列を送ってくるとエラーがでる点を防ぎきれてません。
機能 | メソッド、クラス | 使用例 |
折れ線グラフ
|
【Sub plotC()内】 graphObj.Draw.Line() ※CHARTは使わずにグラフィックで描画 |
Imports System.Drawing g.DrawLine(p6, CSng(px_1), CSng(py_1), CSng(px), CSng(py)) 1個前のデータと今のデータを結び線 |
描画 UNIT |
【Sub plotC()内】 グラフ調整機能をCH単位でUNITにしてある 変数解説 dstep:dot/data rate():dot/mV Mag:時間軸倍率 midvalue():ゼロレベル値 dataAry():6ch電圧データ px_1:1個前のX軸データ py_1:1個前のY軸データ |
‘************Plot Unit 1ch****************************** If Ch_sel(0) = 1 Then px = Int((dstep * dataNo Mod CInt(600 / Mag)) * Mag) px_1 = Int((dstep * (dataNo – 1) Mod CInt(600 / Mag)) * Mag) py = yh – (Int(rate(0) * (dataAry(dataNo, 0) – midvalue(0))) + TBposition) ‘Debug.Print(“——1ch_py:dataAry=” & CStr(dataAry(dataNo, 0)) & “midvalue=” & CStr(midvalue(0))) py_1 = yh – (Int(rate(0) * (dataAry(dataNo – 1, 0) – midvalue(0))) + TBposition) ‘Debug.Print(“1ch_py_1:dataAry=” & CStr(dataAry(dataNo, 0)) & “midvalue=” & CStr(midvalue(0))) g.DrawLine(p1, CSng(px_1), CSng(py_1), CSng(px), CSng(py)) ‘Debug.Print(“——1ch:px_1=” & CStr(px_1) & “px=” & CStr(px) & “py_1=” & CStr(py_1) & “py=” & CStr(py)) End If ‘********************************************************** |
グラフの色
|
【Sub plotC()内】 PENS |
Dim p1, p2, p3, p4, p5, p6 As New Pen(Color.Black)p1 = Pens.Red p1 = Pens.Red p2 = Pens.Blue P3 = Pens.Green p4 = Pens.Magenta p5 = Pens.Orange p6 = Pens.Black |
グラフの線幅
|
【Sub plotC()内】 PEN |
p1.Width = 0.1 |
画面クリア | 【Sub plotC()内】
P.image=Nothing |
PictureBox1.Image = Nothing ‘1スクロール毎に画面クリア |
画面の定義 | 【Sub plotC()内】 P.image=New Bitmap() |
’初回だけBITMAPを定義する Picture1.imageという名称をつかうこと
If PictureBox1.Image Is Nothing Then |
多コントロールの 配列指定 |
【Sub Form1_Load()内】 Me.ComboBox1 |
Dim adjustComboBox() As ComboBox adjustComboBox = {Me.ComboBox1, Me.ComboBox2, Me.ComboBox3, Me.ComboBox4, Me.ComboBox5, Me.ComboBox6, Me.ComboBox7} |
日付ファイル名と任意ファイル名をダイアログ指定 | File指定ボタン 【Sub Button7_Click()内】 時刻: Nowダイアログ: SaveFileDialog |
‘——FILE SAVE準備———————- Dim sfd As New SaveFileDialog() Dim fname As String fname = Format(Now, “yyyyMMdd_HHmmss”) Debug.Print(“fname=” & fname) sfd.FileName = fname sfd.Filter = “TXTファイル|*.txt|CSVファイル|*.csv|すべてのファイル|*.*” sfd.InitialDirectory = “C:\vb_LOG ” sfd.FilterIndex = 2 sfd.Title = “保存先のファイルを選択してください” If sfd.ShowDialog() = DialogResult.OK Then ‘OKボタンがクリックされたとき、選択されたファイル名を表示する Console.WriteLine(sfd.FileName) TextBox21.Text = sfd.FileName End If |
CSVファイル 宣言 |
【Sub PrintData()内】
使いまわすので |
‘========First CSV File Declaration====================== If CheckBox8.Checked = True And fname = “” Then fname = Format(Now, “yyyyMMdd_HHmmss”) fname = “C:\vb_LOG\” & fname & “.csv” TextBox21.Text = fname ‘—MS Example—————————— ‘Dim file As System.IO.StreamWriter ‘file = My.Computer.FileSystem.OpenTextFileWriter(“c:\test.txt”, True) ‘file.WriteLine(“Here is the first string.”) ‘file.Close() ‘—————————————— Dim enc As System.Text.Encoding = System.Text.Encoding.GetEncoding(“Shift_JIS”) file = My.Computer.FileSystem.OpenTextFileWriter(fname, False, enc) End If |
ファイル書き込み | 【Sub PrintData()内】
File.write(data) |
ファイルが存在するときに書き込む
‘========sdata File writing==================== |
■使い方
1)SerialPort1のPropertyでCOM文字とボーレートを設定
2)OPENボタンを押すと接続して、受信データが各CHのTextBoxに表示されます
3)CSV自動ログするには、右上のAutoSavingLogにチェックが入っていること
AutoSavingLogは、ファイル名は年月日時刻名で”¥C¥vb_Log”フォルダーに保存
4)グラフの開始は、左上のSTARTボタン、STOPボタンで表示ONOFFができるが、ログはグラフに関係なく継続している
5)シリアルポートを閉じるとプログラムがハングする現象があるので、それを防ぐためにCLOSEボタンは、Recevingチェックボックスのチェックを外さないと押せません。
これは、受信中にシリアルポートをCLOSEすると、マルチスレッドがハングすることで発生するため、対策として、Receivingを完全に停止してからCLOSEする操作を確実にするためのUI仕様です。
参考にさせていただいた記事は、こちらです。
SerialPortをCloseするとフリーズする
6)シリアルをCLOSEすると同時にFILE LOGGINGもCLOSEになります。
■以後
EXEの配布版を作って配布します。
あくまでも動作確認用でご自分でお使いになるには、ソースをいじって改良されることをお勧めします。
配布版以降は、自分の測定用にシステム開発をしますので、多分力センサテーマで扱います。
何かご質問がある場合は、ich48397@wd5.so-net.jpへどうぞ
コメント欄の記入されても海外からのスパムが大量にきているため
正規のコメントを探すのに時間がかかります。
■必須項目:Win10の設定
USBシリアルFTDIドライバのLatencyTimer機能が働いていて、64byte以下の少量データの連続転送時に遅延が発生するようになってるそうです。
詳細はこちらのページです。https://www.hdl.co.jp/USB/FTDI/lt/
この設定をBM設定といいます。これが、デフォルトだと16msecとなってるとバッファ遅延でリアルタイムになりませんので、BM=1msecに設定しなおせば、リアルタイムになります。
●変更方法:
デバイスマネージャ>ポート(COMxx)>Property>ポートの設定タブ
>詳細設定>BMオプション待ち時間を1msecにします。
QIITAのこちらの記事に感謝です。
※2021年6月30日追記
本記事から10か月経過して、グラフ描画プログラムは、BITAMAPを切り取って高速表示するテクニックを身に着けて劇的に向上しました。グラフデータアニメーションのように動かせるので、過去データを一瞬で表示したり、回転、移動、拡大、縮小、リアルタイムで現在のプロットを中心固定してグラフ全体を動かしたり自由自在です。
データ数が数千行から数万行になると高速で処理しないと全体を目視できないのでBITMAPとGraphicsを使ってグラフを描画する方法が一番いいです。参考記事をご覧ください。初めてのグラフィクスからこの方法でプロットしたほうがVB学習の効率は良くなります。