用C进行仪器控制系列数字万用表(4)
此为"用C#进行仪器控制系列--数字万用表"的第4篇文章,在此之前已经编写了3篇相关文章了,可参见:
1)用C#进行仪器控制系列——数字万用表(1):该篇文章介绍了如何从零开始创建一个控制台应用程序,实现 Keysight 数字万用表的电压测量,并将结果通过控制台展示;
2)用C#进行仪器控制系列——数字万用表(2):该篇文章在第1篇文章的基础上,以窗体应用程序的形式重写了万用表电压测量程序,并能够自动识别仪器地址,以及能够通过按钮控制测量的开始和停止,电压的显示等。
3)用C#进行仪器控制系列——数字万用表(3):该篇文章在第2篇文章的基础上,通过代码的简单优化,实现了不同测量类型的下拉选择,根据下拉选项执行对应类型的测量功能(电压、电流、电阻、二极管、导通、频率测试等)。
此篇文章是在第3篇文章的基础上,添加波形图表显示功能。优化需求:添加波形图表显示功能,用以观测数据的变化趋势
通过添加波形图表功能,将测量数据实时传递给波形图表,使其能够在界面中显示测量的历史数据,就可以观测到数据的变化趋势了。最终想要实现的效果如下图所示。
需求实现效果优化实现过程
1)窗体界面的优化从工具箱中找到 Visual Studio 自带的 Chart 图表控件,并将其拖拽至窗体面板中。
Chart控件选中新添加的 Chart 控件,在其属性窗口中找到图表的"Series"属性,然后点击该属性右侧的3个小圆点组成的按钮进入"Series"属性设置页,如下图所示
图表的"Series"属性修改数据序列的名称(Name)为"Data",修改图表类型(ChartType)为"Line",即修改图表类型为折线图显示。
修改"Series"属性设置完图表属性后,调整窗体上相关控件的布局,调整后如下图所示。
调整窗体布局
2)代码优化
主要涉及如下3块代码段的修改:在窗体的构造函数中增加曲线图样式的设置语句,包括曲线样式、曲线颜色的设置。public Form1() { InitializeComponent(); // 设置曲线图样式 WaveChart.Series[0].ChartType = SeriesChartType.Line; WaveChart.Series[0].Color = System.Drawing.Color.Blue; }在开始测试的事件响应代码段添加清除曲线代码,实现开始测试时重新绘图。if (btnStart.Text == "开始测试") { btnStart.Text = "停止测试"; // 清空曲线图数据 WaveChart.Series[0].Points.Clear();在测量线程代码段,增加将实时测量数据传递给波形图表的代码。// 循环读取万用表数据 while (true) { // 发送采集命令 messageBasedSession.WriteString(":READ?", true); // 读取返回值 string response = messageBasedSession.ReadString(); // 在控件和图表中输出结果 this.Invoke((MethodInvoker)delegate { resultTextBox.Text = ChangeDataToD(response.Trim()); WaveChart.Series[0].Points.AddY(double.Parse(resultTextBox.Text)); }); }优化效果验证
程序优化后的运行效果如下图所示,可通过下拉选择测量类型,实现不同的测试需求,然后通过Text Box显示控件和Chart波形图表显示数据。
运行效果
通过验证,可以完美的实现测量需求。
附:本文中涉及到的完整源代码using System; using System.Resources; using System.Windows.Forms; using Ivi.Visa.Interop; using System.Threading; using System.Linq.Expressions; using System.Windows.Forms.DataVisualization.Charting; namespace WinForm_ComPorts { public partial class Form1 : Form { private Ivi.Visa.Interop.ResourceManager resourceManager; private FormattedIO488 messageBasedSession; private Thread measurementThread; public Form1() { InitializeComponent(); // 设置曲线图样式 WaveChart.Series[0].ChartType = SeriesChartType.Line; WaveChart.Series[0].Color = System.Drawing.Color.Blue; } #region 端口号下拉选择框下拉事件响应代码 /// /// 端口号下拉选择框下拉事件响应代码 /// /// /// private void cboComPorts_DropDown(object sender, EventArgs e) { try { // 清除下拉列表 this.cboComPorts.Items.Clear(); // 获取设备的所有串口资源 string[] comNames = System.IO.Ports.SerialPort.GetPortNames(); // 添加可用串口号到下拉框中 foreach (string s in comNames) { this.cboComPorts.Items.Add(s); } // 创建VISA资源对象实例 Ivi.Visa.Interop.ResourceManager rm = new Ivi.Visa.Interop.ResourceManager(); // 获取设备的所有仪器资源 string[] usbNames = rm.FindRsrc("?*INSTR"); // 添加可用USB资源到下拉框中 foreach (string s in usbNames) { if (s.Contains("0x")) this.cboComPorts.Items.Add(s); } } catch (Exception) { MessageBox.Show("系统内不存在此类仪器~", "ERROR"); } } #endregion #region 开始测试/停止测试按钮事件响应代码 /// /// 开始测试/停止测试按钮事件响应代码 /// /// /// private void btnStart_Click(object sender, EventArgs e) { try { if (btnStart.Text == "开始测试") { btnStart.Text = "停止测试"; // 清空曲线图数据 WaveChart.Series[0].Points.Clear(); // 创建ResourceManager和MessageBasedSession对象 resourceManager = new Ivi.Visa.Interop.ResourceManager(); messageBasedSession = new FormattedIO488(); // 打开万用表连接,万用表的USB通讯地址通过端口号下拉列表的值传递过来 messageBasedSession.IO = (IMessage)resourceManager.Open(cboComPorts.Text.ToString(), AccessMode.NO_LOCK, 5000, ""); //获取测量类型,根据测量类型选择相应的采集指令 string ConfigCommand = ""; switch (cboMeasType.Text) { case "DC Voltage": ConfigCommand = ":CONF:VOLT:DC AUTO,DEF"; break; case "AC Voltage": ConfigCommand = ":CONF:VOLT:AC AUTO,DEF"; break; case "DC Current": ConfigCommand = ":CONF:CURR:DC AUTO,DEF"; break; case "AC Current": ConfigCommand = ":CONF:CURR:AC AUTO,DEF"; break; case "Resistance 2-wire": ConfigCommand = ":CONF:RES AUTO"; break; case "Resistance 4-wire": ConfigCommand = ":CONF:FRES AUTO"; break; case "Diode": ConfigCommand = ":CONF:DIOD"; break; case "Continuity": ConfigCommand = ":CONF:CONT"; break; case "Frequency": ConfigCommand = ":CONF:FREQ"; break; default: ConfigCommand = ":CONF:VOLT:DC AUTO,DEF"; break; } // 设置采集模式和测量范围 messageBasedSession.WriteString(ConfigCommand, true); // 创建线程执行连续测量 measurementThread = new Thread(new ThreadStart(MeasureThread)); measurementThread.Start(); } else { btnStart.Text = "开始测试"; // 停止测量线程 if (measurementThread != null && measurementThread.IsAlive) { measurementThread.Abort(); measurementThread.Join(); } // 关闭连接 if (messageBasedSession != null) { messageBasedSession.IO.Close(); // messageBasedSession.Dispose(); } if (resourceManager != null) { // resourceManager.Dispose(); // MessageBox.Show("资源释放"); } } } catch (Exception ex) { MessageBox.Show("发生错误:" + ex.Message); } } #endregion #region 测量线程代码 /// /// 测量线程,读取万用表的数据,并将结果反馈至resultTextBox控件 /// private void MeasureThread() { try { // 循环读取万用表数据 while (true) { // 发送采集命令 messageBasedSession.WriteString(":READ?", true); // 读取返回值 string response = messageBasedSession.ReadString(); // 在控件和图表中输出结果 this.Invoke((MethodInvoker)delegate { resultTextBox.Text = ChangeDataToD(response.Trim()); WaveChart.Series[0].Points.AddY(double.Parse(resultTextBox.Text)); }); } } catch (ThreadAbortException) { // 线程被中断 // MessageBox.Show("线程被中断"); // 查询错误 messageBasedSession.WriteString(":SYST:ERR?"); // 读取返回的错误信息 string response = messageBasedSession.ReadString(); // 若仪器返回了"-410"错误(读取万用表读数时被中断导致)或无错误,则忽略 if (response.Contains("-410") || response.Contains("No error")) { } else { MessageBox.Show(response); } } catch (Exception ex) { MessageBox.Show("发生错误:" + ex.Message); } } #endregion #region 数据显示格式转换代码 /// /// 将科学计数法表示的字符串数据转换为double型数据显示格式 /// /// /// 返回double型数据格式的字符串 private string ChangeDataToD(string strData) { Double dData = 0.0; if (strData.Contains("E")) { dData = Double.Parse(strData); } return dData.ToString(); } #endregion } }