概要
リアルタイムで受信データをグラフ表示するプログラムを、ScottPlot 4.1とC# WPF(.NET Framewok)により作成しました。
Scatter plot(散布図)を使用しています。x軸はデータの受信時刻、y軸は受信データとなります。1秒間隔で、USBシリアル変換ICにデータを送信し、エコーバックしたデータを受信しグラフにしています。
処理概要
・ボタンとチェックボックス
Serial Port: USBシリアル通信ポートの設定用ダイアログを表示します。
Start: 定周期送信タイマー(1秒)を起動して、1秒間隔で2バイトのデータを送信します。
Stop: 定周期送信タイマを停止して、送信を終了します。
Clear: 送受信情報表示をクリアします。
Auto Axis: X軸、Y軸の範囲をグラフが表示される範囲に再設定します。(マウスのMiddle-clickでも可能です。)
PV(ch1): trend_data0 のグラフの表示/非表示を選択します。
SV:trend_data1のグラフの表示/非表示を選択します。
・Scatter Plot(散布図)の表示
散布図を使用する場合にはdouble型のX軸用の配列データとY軸用の配列データを用意します。AddScatterによりグラフが表示されます。
・データ送信/受信処理
8月の「シリアル通信プログラムによるCH340Eの動作確認」(https://vabc.hatenadiary.jp/entry/2021/08/29/175717 )と同様です。ここでは、0~9までの値と10(0x0A)の値を1秒毎に2バイトを送信しています。また受信監視タイマ(送信から200msec以内に受信しないとTime Out)が追加されています。
グラフに最新の受信データを表示するため、受信時に配列の一つ後のデータを一つ前に移動させ、配列の最後に受信データを格納します。その後グラフを更新します。
・通信ポートの設定ダイアログ
通信ポートの設定は、メインウィンドウとは別にダイアログにしています。
このダイアログは次の手順で、WPFウィンドウを追加して作成します。
プロジェクト名(本例では PlotTest)を右クリックして「追加」を選択。その後「ウィンドウ(WPF)」を選択すると、新しい xaml(例:ConfSerial)が追加できます。
プログラム
ScottPlotを使用するためには、NuGetによるインストールが必要です。新規ソリューション(例:PlotTest)作成後、NuGetによりScottPlot.WPFをインストールします。
・メインウィンドウ XAML部分 (MainWindow.xaml)
<Window x:Class="PlotTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:PlotTest" mc:Ignorable="d" ResizeMode="CanResizeWithGrip" Title="PlotTest" Height="600" Width="800" FontSize="14" FontFamily="MS UI Gothic" Background="White" Foreground="#333333"> <Grid ShowGridLines="False"> <!-- Gridで使用するボタンの大きさ、色を定義--> <Grid.Resources> <Style TargetType="Button"> <Setter Property="Height" Value="30" /> <Setter Property="Width" Value="100"/> <Setter Property="Margin" Value="10" /> <Setter Property="BorderBrush" Value="#a6a6a6" /> <Setter Property="Foreground" Value="#333333" /> <Setter Property="Background" Value="#fcfcfc"/> </Style> </Grid.Resources> <!-- カラム Grid 横方向の大きさ指定。 "AUTO"は、横幅を変更するGridSplitterの部分 --> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" MinWidth="100"/> <ColumnDefinition Width="AUTO"/> <ColumnDefinition Width="2*" MinWidth="100" /> <ColumnDefinition Width="0.6*" MinWidth="100" /> </Grid.ColumnDefinitions> <!-- Grid 行方向の大きさ指定 "AUTO"は、高さを変更する GridSplitterの部分--> <Grid.RowDefinitions> <RowDefinition Height="4*" MinHeight="100" /> <RowDefinition Height="AUTO" /> <RowDefinition Height="1*" MinHeight="100" /> </Grid.RowDefinitions> <!--横幅を変更する GridSplitter--> <GridSplitter Grid.Row="0" Grid.Column="1" Grid.RowSpan="3" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Gainsboro"/> <!--高さを変更する GridSplitter--> <GridSplitter Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Gainsboro"/> <!-- スタックパネル (row=0,col=0) 送信ボタンを配置--> <!-- row= 1 には、Gridsplitterが配置されている --> <StackPanel Grid.Row="0" Grid.Column="0" Margin="10"> <StackPanel Orientation="Vertical" Margin="0,20,0,0"> <Button Content="Serial Port" Click="Serial_Button_Click"/> <Button Content="Start" Click="Start_Button_Click"/> <Button Content="Stop" Click="Stop_Button_Click"/> <Button Content="Clear" Click="Clear_Button_Click" /> </StackPanel> <StackPanel Orientation="Horizontal" Margin="0,20,0,0"> <TextBlock Text="Status:"/> <TextBlock x:Name="StatusTextBlock" Margin ="10,0,0,0" Text =""/> </StackPanel> </StackPanel> <WpfPlot Name="wpfPlot_PV" Grid.Row="0" Grid.Column="2" /> <!-- ドックパネル (row=0,col=2) TextBlockを配置--> <!-- col= 1 には、Gridsplitterが配置されている --> <DockPanel Grid.Row="2" Grid.Column="0" Margin="10"> <TextBlock DockPanel.Dock="Top" Text="Send/Receive Info."/> <ScrollViewer x:Name ="LogTextScroll" VerticalScrollBarVisibility="Auto"> <TextBlock x:Name="SendRcvTextBlock" Margin ="10" Text ="" /> </ScrollViewer> </DockPanel> <!-- スタックパネル (row=0,col=3) チェックボックスを配置--> <StackPanel Grid.Row="0" Grid.Column="3" Margin="10"> <StackPanel Orientation="Horizontal"> <CheckBox x:Name="PV_CheckBox" Margin="8" IsChecked="True" Checked="PV_X_Show" Unchecked="PV_X_Hide"/> <Label Content ="PV(ch1)" Margin="0,4,0,0"/> </StackPanel> <StackPanel Orientation="Horizontal"> <CheckBox x:Name="SV_CheckBox" Margin="8" IsChecked="True" Checked="PV_X_Show" Unchecked="PV_X_Hide"/> <Label Content ="SV" Margin="0,4,0,0"/> </StackPanel> <Button Content="Axis Auto" Margin="0,40,0,0" Click="Axis_Auto_Button_Click" /> </StackPanel> </Grid> </Window>
・メインウィンドウ C#部分 (MainWindow.xaml.cs)
using ScottPlot; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO.Ports; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; namespace PlotTest { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> /// public partial class MainWindow : Window { byte[] sendBuf; // 送信バッファ int sendByteLen; // 送信データのバイト数 byte[] rcvBuf; // 受信バッファ int srcv_pt; // 受信データ格納位置 int data_receive_thread_id; // データ受信ハンドラのスレッドID int data_receive_thread_cnt; // データ受信ハンドラの実施回数 DispatcherTimer SendIntervalTimer; // タイマー モニタ用 電文送信間隔 DispatcherTimer RcvWaitTimer; // タイマー 受信待ち用 UInt32 msg_num; // メッセージ番号 // リアルタイム PV,MVグラフ用 uint trend_data_item_max; // 各リアルタイム トレンドデータの保持数(=10 ) 2秒毎に収集すると、20秒分のデータ double[] trend_data0; // トレンドデータ 0 PV(ch1) double[] trend_data1; // トレンドデータ 1 SV double[] trend_dt; // トレンドデータ 収集日時 byte[] send_data0; byte[] send_data1; ScottPlot.Plottable.ScatterPlot trend_signal_0; // トレンドデータ0 ScottPlot.Plottable.ScatterPlot trend_signal_1; // トレンドデータ1 DateTime receiveDateTime; // 受信完了日時 public MainWindow() { InitializeComponent(); ConfSerial.serialPort = new SerialPort(); // シリアルポートのインスタンス生成 ConfSerial.serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler); // データ受信時のイベント処理 sendBuf = new byte[2048]; // 送信バッファ領域 serialPortのWriteBufferSize =2048 byte(デフォルト) rcvBuf = new byte[4096]; // 受信バッファ領域 SerialPort.ReadBufferSize = 4096 byte (デフォルト SendIntervalTimer = new System.Windows.Threading.DispatcherTimer(); // タイマーの生成 SendIntervalTimer.Tick += new EventHandler(SendIntervalTimer_Tick); // タイマーイベント SendIntervalTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000); // タイマーイベント発生間隔 1sec(送信間隔) RcvWaitTimer = new System.Windows.Threading.DispatcherTimer(); // タイマーの生成(受信待ちタイマ) RcvWaitTimer.Tick += new EventHandler(RcvWaitTimer_Tick); // タイマーイベント RcvWaitTimer.Interval = new TimeSpan(0, 0, 0, 0, 200); // タイマーイベント発生間隔 (受信待ち時間) trend_data_item_max = 10; // 各リアルタイム トレンドデータの保持数(=10 ) Chart_Ini(); // チャート(リアルタイム用)の初期化 send_data0 = new byte[trend_data_item_max]; send_data1 = new byte[trend_data_item_max]; for (int i = 0; i < trend_data_item_max; i++) // 送信データの作成 { send_data0[i] = (byte) i; send_data1[i] = (byte) trend_data_item_max; } } // チャートの初期化(リアルタイム チャート用) // private void Chart_Ini() { trend_data0 = new double[trend_data_item_max]; trend_data1 = new double[trend_data_item_max]; trend_dt = new double[trend_data_item_max]; DateTime datetime = DateTime.Now; // 現在の日時 DateTime[] myDates = new DateTime[trend_data_item_max]; for (int i = 0; i < trend_data_item_max; i++) { trend_data0[i] = i; trend_data1[i] = trend_data_item_max; myDates[i] = datetime + new TimeSpan(0, 0, i); // i秒増やす trend_dt[i] = myDates[i].ToOADate(); // (現在の日時 + i 秒)をdouble型に変換 } wpfPlot_PV.Refresh(); // データ変更後のリフレッシュ trend_signal_0 = wpfPlot_PV.Plot.AddScatter(trend_dt, trend_data0, color: System.Drawing.Color.Orange, label: "PV(ch1)"); // プロット plot the data array only once trend_signal_1 = wpfPlot_PV.Plot.AddScatter(trend_dt, trend_data1, color: System.Drawing.Color.Green, label: "SV"); // PVグラフ wpfPlot_PV.Configuration.Pan = true; // パン(グラフの移動)可 wpfPlot_PV.Configuration.ScrollWheelZoom = true; // ズーム(グラフの拡大、縮小)可 wpfPlot_PV.Plot.AxisAuto(); // X軸、Y軸のオートスケール wpfPlot_PV.Plot.XAxis.Ticks(true, false, true); // X軸の大きい目盛り=表示, X軸の小さい目盛り=非表示, X軸の目盛りのラベル=表示 wpfPlot_PV.Plot.XAxis.TickLabelStyle(fontSize: 14); //X軸 ラベルのフォントサイズ変更 : wpfPlot_PV.Plot.XAxis.TickLabelFormat("HH:mm:ss", dateTimeFormat: true); // X軸 時間の書式(例 12:30:15)、X軸の値は、日時型 wpfPlot_PV.Plot.XLabel("time"); // X軸全体のラベル wpfPlot_PV.Plot.YAxis.TickLabelStyle(fontSize: 14); // Y軸 ラベルのフォントサイズ変更 : wpfPlot_PV.Plot.YAxis.Label(label: "[℃]", color: System.Drawing.Color.Black); // Y軸全体のラベル var legend1 = wpfPlot_PV.Plot.Legend(enable: true, location: Alignment.UpperRight); // 凡例の表示 legend1.FontSize = 14; // 凡例のフォントサイズ } // // Start ボタン // private void Start_Button_Click(object sender, RoutedEventArgs e) { msg_num = 0; // メッセージ番号の初期化 SendIntervalTimer.Start(); // 定周期送信用のタイマー開始 } // // Stop ボタン // private void Stop_Button_Click(object sender, RoutedEventArgs e) { SendIntervalTimer.Stop(); //定周期送信用のタイマー停止 StatusTextBlock.Text = "Stop"; } // // 定周期にデータを送信する。 // private void SendIntervalTimer_Tick(object sender, EventArgs e) { Send_TestData(); // テスト用データの 送信 StatusTextBlock.Text = ""; } // // データの送信 // private void Send_TestData() { if (ConfSerial.serialPort.IsOpen == true) { srcv_pt = 0; // 受信データ格納位置クリア data_receive_thread_cnt = 0; // sendByteLen = 2; // 送信バイト数 sendBuf[0] = send_data0[msg_num]; // 送信データを送信バッファへ格納 sendBuf[1] = send_data1[msg_num]; ConfSerial.serialPort.Write(sendBuf, 0, sendByteLen); // データ送信 RcvWaitTimer.Start(); // 受信監視タイマー 開始 if ( msg_num < (trend_data_item_max - 1) ){ msg_num = msg_num + 1; // メッセージ番号インクリメント } else { msg_num = 0; } SendRcvTextBlock.Text += "Snd: "; // 送信の意味 for (int i = 0; i < sendByteLen; i++) // 送信データの表示 { if ((i > 0) && (i % 16 == 0)) // 16バイト毎に1行空ける { SendRcvTextBlock.Text += "\r\n"; } SendRcvTextBlock.Text += sendBuf[i].ToString("X2") + " "; } SendRcvTextBlock.Text += "(" + DateTime.Now.ToString("HH:mm:ss.fff") + ")" + "\r\n"; // 時刻 } else { StatusTextBlock.Text = "Comm port closed !"; } } // 送信後、200msec以内に受信文が得られないと、受信エラー // private void RcvWaitTimer_Tick(object sender, EventArgs e) { int id = System.Threading.Thread.CurrentThread.ManagedThreadId; RcvWaitTimer.Stop(); // 受信監視タイマー 停止 SendIntervalTimer.Stop(); //定周期送信用のタイマー停止 StatusTextBlock.Text = "Receive time out"; } // デリゲート関数の宣言 private delegate void DelegateFn(); // データ受信時のイベント処理 private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { int rd_num = ConfSerial.serialPort.BytesToRead; // 受信データ数 ConfSerial.serialPort.Read(rcvBuf, srcv_pt, rd_num); // 受信データを読み出して、受信バッファに格納 srcv_pt = srcv_pt + rd_num; // 次回の保存位置 data_receive_thread_cnt++; // データ受信ハンドラの実施回数のインクリメント int id = System.Threading.Thread.CurrentThread.ManagedThreadId; data_receive_thread_id = id; //データ受信スレッドのID格納 if (srcv_pt == sendByteLen) // 最終データ受信済み (受信データ数は、送信バイト数と同一とする) イベント処理の終了 { RcvWaitTimer.Stop(); // 受信監視タイマー 停止 receiveDateTime = DateTime.Now; // 受信完了時刻を得る Dispatcher.BeginInvoke(new DelegateFn(RcvProc)); // Delegateを生成して、RcvProcを開始 (表示は別スレッドのため) } } // // データ受信イベント終了時の処理 // 受信データの表示 // private void RcvProc() { // グラフ(リアルタイム)用表示データの作成 // グラフを左にずらす (一つ前の配列indexへ移動 ) Array.Copy(trend_data0, 1, trend_data0, 0, trend_data_item_max - 1); trend_data0[trend_data_item_max - 1] = (double)rcvBuf[0]; // 受信した最新データを、グラフ用のデータ配列へ格納 Array.Copy(trend_data1, 1, trend_data1, 0, trend_data_item_max - 1); trend_data1[trend_data_item_max - 1] = (double)rcvBuf[1]; Array.Copy(trend_dt, 1, trend_dt, 0, trend_data_item_max - 1); trend_dt[trend_data_item_max - 1] = receiveDateTime.ToOADate(); // 受信日時 double型に変換して、格納 wpfPlot_PV.Refresh(); // データ変更後のリフレッシュ wpfPlot_PV.Render(); // リアルタイム グラフの更新 wpfPlot_PV.Plot.AxisAuto(); // X軸の範囲を更新 rcvmsg_disp(); // 受信データの表示 } // 受信データの表示 // private void rcvmsg_disp() { string rcv_str = ""; SendRcvTextBlock.Text += "Rcv: "; // 受信の意味 for (int i = 0; i < srcv_pt; i++) // 表示用の文字列作成 { if ((i > 0) && (i % 16 == 0)) // 16バイト毎に1行空ける { rcv_str = rcv_str + "\r\n"; } rcv_str = rcv_str + rcvBuf[i].ToString("X2") + " "; } SendRcvTextBlock.Text += rcv_str; // 受信文 SendRcvTextBlock.Text += "(" + receiveDateTime.ToString("HH:mm:ss.fff") + ")(" + srcv_pt.ToString() + " bytes )" + "\r\n" +"\r\n"; LogTextScroll.ScrollToBottom(); // 一番下までスクロール } // クリアボタンを押した時の処理 private void Clear_Button_Click(object sender, RoutedEventArgs e) { msg_num = 0; // メッセージ番号の初期化 SendRcvTextBlock.Text = ""; } // 通信ポートの ダイアログを開く // private void Serial_Button_Click(object sender, RoutedEventArgs e) { new ConfSerial().ShowDialog(); } // チェックボックスによる表示 private void PV_X_Show(object sender, RoutedEventArgs e) { if (trend_signal_0 is null) return; if (trend_signal_1 is null) return; CheckBox checkBox = (CheckBox)sender; if (checkBox.Name == "PV_CheckBox") { trend_signal_0.IsVisible = true; } else if (checkBox.Name == "SV_CheckBox") { trend_signal_1.IsVisible = true; } wpfPlot_PV.Render(); // グラフの更新 } // チェックボックスによる非表示 private void PV_X_Hide(object sender, RoutedEventArgs e) { if (trend_signal_0 is null) return; if (trend_signal_1 is null) return; CheckBox checkBox = (CheckBox)sender; if (checkBox.Name == "PV_CheckBox") { trend_signal_0.IsVisible = false; } else if (checkBox.Name == "SV_CheckBox") { trend_signal_1.IsVisible = false; } wpfPlot_PV.Render(); // グラフの更新 } // Axise Auto ボタンを押した時の処理 // グラフのパン(移動)、ズームで線が見えなくなった時、オートスケールで再表示させる。 private void Axis_Auto_Button_Click(object sender, RoutedEventArgs e) { wpfPlot_PV.Plot.AxisAuto(); // X軸、Y軸のオートスケール wpfPlot_PV.Render(); // グラフの更新 } } }
・通信ポート設定ダイアログ用 XAML部分 (ConfSerial.xaml)
Window x:Class="PlotTest.ConfSerial" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:PlotTest" mc:Ignorable="d" Title="ConfSerial" Height="380" Width="280"> <Grid> <StackPanel Grid.Row="0" Grid.Column="0" > <StackPanel.Resources> <Style TargetType="Button"> <Setter Property="Height" Value="30" /> <Setter Property="Width" Value="100"/> <Setter Property="Margin" Value="10" /> <Setter Property="BorderBrush" Value="#a6a6a6" /> <Setter Property="Foreground" Value="#333333" /> <Setter Property="Background" Value="#fcfcfc"/> </Style> </StackPanel.Resources> <GroupBox Header="Serial communication port" Margin="10"> <StackPanel > <ComboBox x:Name = "ComPortComboBox" TextSearch.TextPath="ComPortName" Height="30" Width="100" Margin="10" BorderBrush="#a6a6a6" Foreground="#333333" Background="#fcfcfc"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" > <TextBlock Text="{Binding ComPortName}" /> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button x:Name= "ComPortOpenButton" Content="Open" Click="ComPortOpenButton_Click"/> <Button x:Name= "ComPortSearchButton" Content="Find" Click="ComPortSearchButton_Click"/> <TextBlock HorizontalAlignment="Center" Margin="10"> ( 76.8 Kbps,1-stop, no-parity) </TextBlock> </StackPanel> </GroupBox> <GroupBox Header="Info." Margin="10"> <TextBox x:Name="OpenInfoTextBox" IsReadOnly="True" BorderThickness="0" Text ="Open/Close infomation."/> </GroupBox> <Button x:Name= "ConfOK" Content="OK" Click="ConfOKButton_Click"/> </StackPanel> </Grid> </Window>
・通信ポート設定ダイアログ用 C#部分 (ConfSerial.xaml.cs)
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace PlotTest { // COMポートの コンボボックス用 public class ComPortNameClass { string _ComPortName; public string ComPortName { get { return _ComPortName; } set { _ComPortName = value; } } } /// <summary> /// ConfSerial.xaml の相互作用ロジック /// </summary> public partial class ConfSerial : Window { public ObservableCollection<ComPortNameClass> ComPortNames; // 通信ポート(COM1,COM2等)のコレクション // データバインドするため、ObservableCollection public static SerialPort serialPort; // シリアルポート public ConfSerial() { InitializeComponent(); ComPortNames = new ObservableCollection<ComPortNameClass>(); // 通信ポートのコレクション インスタンス生成 ComPortComboBox.ItemsSource = ComPortNames; // 通信ポートコンボボックスのアイテムソース指定 SetComPortName(); // 通信ポート名をコンボボックスへ設定 if (ComPortNames.Count > 0) // 通信ポートがある場合 { if (serialPort.IsOpen == true) // new Confserial()実行時に、 既に Openしている場合 { OpenInfoTextBox.Text = "通信ポート(" + serialPort.PortName + ")は、既にオープンしています。"; ComPortOpenButton.Content = "Close"; // ボタン表示 Close } else { OpenInfoTextBox.Text = "通信ポート(" + serialPort.PortName + ")は、クローズしています。"; ComPortOpenButton.Content = "Open"; // ボタン表示 Close } } else { OpenInfoTextBox.Text = "通信ポートが見つかりません。"; } } // 通信ポート名をコンボボックスへ設定 private void SetComPortName() { ComPortNames.Clear(); // 通信ポートのコレクション クリア string[] PortList = SerialPort.GetPortNames(); // 存在するシリアルポート名が配列の要素として得られる。 foreach (string PortName in PortList) { ComPortNames.Add(new ComPortNameClass { ComPortName = PortName }); // シリアルポート名の配列を、コレクションへコピー } if (ComPortNames.Count > 0) { ComPortComboBox.SelectedIndex = 0; // 最初のポートを選択 ComPortOpenButton.IsEnabled = true; // ポートOPENボタンを「有効」にする。 } else { ComPortOpenButton.IsEnabled = false; // ポートOPENボタンを「無効」にする。 } } // 通信ポートの検索 // private void ComPortSearchButton_Click(object sender, RoutedEventArgs e) { SetComPortName(); } // // 通信ポートのオープン // // SerialPort.ReadBufferSize = 4096 byte (デフォルト) // WriteBufferSize =2048 byte // private void ComPortOpenButton_Click(object sender, RoutedEventArgs e) { if (serialPort.IsOpen == true) // 既に Openしている場合 { try { serialPort.Close(); OpenInfoTextBox.Text = "通信ポート(" + serialPort.PortName + ")を、クローズしました。"; ComPortComboBox.IsEnabled = true; // 通信条件等を選択できるようにする。 ComPortSearchButton.IsEnabled = true; // 通信ポート検索ボタンを有効とする。 ComPortOpenButton.Content = "Open"; // ボタン表示を Closeから Openへ } catch (Exception ex) { OpenInfoTextBox.Text = ex.Message; } } else // Close状態からOpenする場合 { serialPort.PortName = ComPortComboBox.Text; // 選択したシリアルポート serialPort.BaudRate = 76800; // ボーレート 76.8[Kbps] serialPort.Parity = Parity.None; // パリティ無し serialPort.StopBits = StopBits.One; // 1 ストップビット serialPort.Open(); // シリアルポートをオープンする serialPort.DiscardInBuffer(); // 受信バッファのクリア ComPortComboBox.IsEnabled = false; // 通信条件等を選択不可にする。 ComPortSearchButton.IsEnabled = false; // 通信ポート検索ボタンを無効とする。 OpenInfoTextBox.Text = "通信ポート(" + serialPort.PortName + ")を、オープンしました。"; ComPortOpenButton.Content = "Close"; // ボタン表示を OpenからCloseへ } } // OKボタン private void ConfOKButton_Click(object sender, RoutedEventArgs e) { this.Close(); } } }
開発環境
ソフト:Win10
Microsoft Visual Studio Community 2022 (64 ビット) - CurrentVersion 17.0.0
C#, .NET Framework 4.8
ScottPlot 4.1.27
ハード:USBシリアル変換IC(CH340E)のRTDとTXDを結線してエコーバックさせています。
ScottPloptについて
サイト(https://scottplot.net/faq/compare/)の情報によればScottPlotの理念は、
・tens of millions のデータが容易に表示できる。
・パン*1とズーム*2がマウスで相互に行え十分に早い
・コンソールアプリまたはサーバ環境での Function
・複雑なオブジェクトを学ぶ必要がなく、
データは単純にdouble[]に入れ、
データは1行のコードでグラフ表示できる。
と書かれています。
また、グラフ上でマウスを右クリックするとメニューが表示されグラフのコピーや保存等が可能です。