Files
ModbusDemo4CS/Program.cs
2026-02-03 11:14:27 +08:00

1271 lines
48 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Resources;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using ;
using .Data;
namespace P_RTU_TEST_WinForm
{
public partial class MainForm : Form
{
// 设备通信相关
private IModbusMaster _master;
private TcpClient _client = new TcpClient();
// 在类开头部分添加
private string _productModel = ""; // 存储产品批号
private TextBox txtProductModel; // 产品批号输入框
// 在类开头部分添加
private int _calculationCount = 1; // 默认计算1次
private TextBox txtCalcCount; // 计算次数输入框
private Button btnStartCalc; // 开始计算按钮
private List<List<double>> _allTestResults = new List<List<double>>(); // 存储所有测试结果
private TcpClient _tcpClient => ModbusResourceManager.Instance.TcpClient;
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
Function ma;
DataChange c;
// 测试相关
private Thread _testThread;
private bool _isTesting = false;
private List<double> _distanceValues = new List<double>();
private Label label5;
private DateTime _testStartTime;
// UI更新委托
delegate void UpdateUIDelegate(string message);
delegate void UpdateStatsDelegate();
public MainForm()
{
InitializeComponent();
// 添加产品批号标签和输入框
Label lblProductModel = new Label()
{
Text = "产品批号:",
Location = new Point(366, 15),
Size = new Size(68, 15)
};
txtProductModel = new TextBox()
{
Location = new Point(366, 31),
Size = new Size(100, 25),
TabIndex = 6
};
// 添加计算次数标签和输入框
Label lblCalcCount = new Label()
{
Text = "计算次数:",
Location = new Point(472, 15),
Size = new Size(68, 15)
};
txtCalcCount = new TextBox()
{
Location = new Point(472, 31),
Size = new Size(60, 25),
TabIndex = 7,
Text = "1" // 默认值
};
// 添加开始计算按钮
btnStartCalc = new Button()
{
Text = "开始计算",
Location = new Point(538, 29),
Size = new Size(75, 23),
TabIndex = 8,
Enabled = true
};
// 添加到窗体
this.Controls.Add(lblProductModel);
this.Controls.Add(txtProductModel);
this.Controls.Add(lblCalcCount);
this.Controls.Add(txtCalcCount);
this.Controls.Add(btnStartCalc);
InitializeDataGridView();
SetupEventHandlers();
// 设置默认值
txtIP.Text = "192.168.1.210";
txtPort.Text = "502";
txtTestTime.Text = "10";
}
private void InitializeDataGridView()
{
// 设置DataGridView的列
dataGridView1.Columns.Clear();
dataGridView1.Columns.Add("Timestamp", "时间戳");
dataGridView1.Columns.Add("speed", "转速");
dataGridView1.Columns.Add("Distance", "距离值 (mm)");
// 设置列宽度
dataGridView1.Columns["Timestamp"].Width = 150;
dataGridView1.Columns["speed"].Width = 130;
dataGridView1.Columns["Distance"].Width = 140;
// 设置只读
dataGridView1.ReadOnly = true;
// 允许排序
dataGridView1.AllowUserToOrderColumns = false;
// 设置选择模式
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.MultiSelect = false;
}
private void SetupEventHandlers()
{
btnConnect.Click += BtnConnect_Click;
btnDisconnect.Click += BtnDisconnect_Click;
btnStartTest.Click += BtnStartTest_Click;
btnStopTest.Click += BtnStopTest_Click;
btnExportData.Click += BtnExportData_Click;
btnClearData.Click += BtnClearData_Click;
btnStartCalc.Click += BtnStartCalc_Click; // 添加开始计算事件
// 窗体关闭事件
this.FormClosing += MainForm_FormClosing;
}
private void BtnStartCalc_Click(object sender, EventArgs e)
{
if (!_client.Connected || _master == null)
{
MessageBox.Show("请先连接设备!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int testTime;
if (!int.TryParse(txtTestTime.Text, out testTime) || testTime <= 0)
{
MessageBox.Show("请输入有效的测试时间(秒)!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 获取计算次数
if (!int.TryParse(txtCalcCount.Text, out _calculationCount) || _calculationCount <= 0)
{
MessageBox.Show("请输入有效的计算次数!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 清空之前的数据
ClearData();
_allTestResults.Clear(); // 清空历史结果
// 开始计算
StartMultipleTests(testTime);
}
private void StartMultipleTests(int testTimeSeconds)
{
if (_isTesting) return;
_isTesting = true;
_testStartTime = DateTime.Now;
// 设置控件状态
btnStartTest.Enabled = false;
btnStartCalc.Enabled = false;
btnStopTest.Enabled = true;
btnExportData.Enabled = false;
btnClearData.Enabled = false;
txtTestTime.Enabled = false;
txtCalcCount.Enabled = false;
LogMessage($"开始计算,总共 {_calculationCount} 次测试");
// 启动计算线程
_testThread = new Thread(() => RunMultipleTests(testTimeSeconds));
_testThread.Start();
}
private void RunMultipleTests(int testTimeSeconds)
{
try
{
for (int i = 0; i < _calculationCount && _isTesting; i++)
{
LogMessage($"第 {i + 1}/{_calculationCount} 次测试开始...");
// 执行单次测试
List<double> singleTestResults = RunSingleTest(testTimeSeconds, i + 1);
if (singleTestResults.Count > 0)
{
_allTestResults.Add(singleTestResults);
// 显示本次测试的统计结果
double min = GetMinDistanceFromList(singleTestResults);
double max = GetMaxDistanceFromList(singleTestResults);
double jitter = max - min;
LogMessage($"第 {i + 1} 次测试结果: 最小值={min:F6}mm, 最大值={max:F6}mm, 抖动值={jitter:F6}mm");
}
// 如果不是最后一次,弹出确认对话框,用户选择“是”继续下一次,“否”停止后续测试
if (i < _calculationCount - 1 && _isTesting)
{
bool userWantsContinue = true;
// 在UI线程上显示对话框并获取选择
if (this.InvokeRequired)
{
this.Invoke(new Action(() =>
{
var dr = MessageBox.Show($"第 {i + 1} 次测试已完成。是否继续进行第 {i + 2} 次测试?", "继续测试确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
userWantsContinue = (dr == DialogResult.Yes);
}));
}
else
{
var dr = MessageBox.Show($"第 {i + 1} 次测试已完成。是否继续进行第 {i + 2} 次测试?", "继续测试确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
userWantsContinue = (dr == DialogResult.Yes);
}
if (!userWantsContinue)
{
LogMessage("用户选择停止后续测试");
_isTesting = false;
break;
}
// 可选短暂停顿
Thread.Sleep(1000); // 测试间隔1秒
}
}
// 所有测试完成后,在主线程中停止测试并生成报表
if (InvokeRequired)
{
Invoke(new Action(() =>
{
StopTest();
GenerateMultipleTestReport(); // 生成多次测试报表
}));
}
else
{
StopTest();
GenerateMultipleTestReport();
}
}
catch (Exception ex)
{
LogMessage($"多次测试过程中发生错误: {ex.Message}");
}
}
private List<double> RunSingleTest(int testTimeSeconds, int testNumber)
{
List<double> testDistances = new List<double>();
DateTime endTime = DateTime.Now.AddSeconds(testTimeSeconds);
try
{
// 配置设备为输出位置1
_master.WriteSingleRegister(1, 24, 0);
while (_isTesting && DateTime.Now < endTime)
{
// 读取距离数据
ushort[] data = _master.ReadInputRegisters(1, 0, 2);
// PLC连接和读取转速
try
{
string plcIp = "192.168.1.5";
bool initSuccess = ModbusResourceManager.Instance.Init(plcIp, 502);
if (initSuccess && _tcpClient != null && _tcpClient.Connected)
{
c = new DataChange();
var registers = _modbusMaster.ReadHoldingRegisters(1, 118, 2);
float floatValue1 = c.UshortToFloat(registers[1], registers[0]);
this.Invoke(new Action(() =>
{
label5.Text = $"第{testNumber}次转速: {floatValue1:F2} RPM";
}));
}
}
catch { }
if (data.Length >= 2)
{
double distance = MergeUShortToInt(data[0], data[1]) / 1000.0 / 1000;
testDistances.Add(distance);
// 更新UI
UpdateUIWithDistance(DateTime.Now, distance, $"第{testNumber}次测试");
}
Thread.Sleep(10);
}
}
catch (Exception ex)
{
LogMessage($"第{testNumber}次测试错误: {ex.Message}");
}
return testDistances;
}
private double GetMinDistanceFromList(List<double> distances)
{
if (distances.Count == 0) return 0;
double min = distances[0];
foreach (double value in distances)
{
if (value < min) min = value;
}
return min;
}
private double GetMaxDistanceFromList(List<double> distances)
{
if (distances.Count == 0) return 0;
double max = distances[0];
foreach (double value in distances)
{
if (value > max) max = value;
}
return max;
}
private void BtnConnect_Click(object sender, EventArgs e)
{
try
{
string ipAddress = txtIP.Text.Trim();
int port;
if (!int.TryParse(txtPort.Text, out port))
{
MessageBox.Show("端口号格式不正确!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
LogMessage($"正在连接设备 {ipAddress}:{port}...");
// 连接设备
_client = new TcpClient();
_client.Connect(ipAddress, port);
_master = ModbusIpMaster.CreateIp(_client);
LogMessage("设备连接成功!");
// 设置控件状态
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
btnStartTest.Enabled = true;
txtIP.Enabled = false;
txtPort.Enabled = false;
}
catch (Exception ex)
{
LogMessage($"连接失败: {ex.Message}");
MessageBox.Show($"连接失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BtnDisconnect_Click(object sender, EventArgs e)
{
try
{
if (_isTesting)
{
StopTest();
}
if (_client != null && _client.Connected)
{
_client.Close();
LogMessage("设备已断开连接");
}
// 重置控件状态
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
btnStartTest.Enabled = false;
txtIP.Enabled = true;
txtPort.Enabled = true;
}
catch (Exception ex)
{
LogMessage($"断开连接时发生错误: {ex.Message}");
}
}
private void BtnStartTest_Click(object sender, EventArgs e)
{
if (!_client.Connected || _master == null)
{
MessageBox.Show("请先连接设备!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int testTime;
if (!int.TryParse(txtTestTime.Text, out testTime) || testTime <= 0)
{
MessageBox.Show("请输入有效的测试时间(秒)!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 清空之前的数据
ClearData();
// 开始测试
StartTest(testTime);
}
private void BtnStopTest_Click(object sender, EventArgs e)
{
StopTest();
}
private void BtnExportData_Click(object sender, EventArgs e)
{
if (_distanceValues.Count == 0 || dataGridView1.Rows.Count == 0)
{
MessageBox.Show("没有数据可以导出!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = "CSV文件 (*.csv)|*.csv|文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*",
Title = "导出测试数据",
FileName = $"距离测试数据_{DateTime.Now:yyyyMMdd_HHmmss}.csv"
};
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
using (StreamWriter sw = new StreamWriter(saveFileDialog.FileName, false, System.Text.Encoding.UTF8))
{
// 写入标题行 - 修改为包含转速
sw.WriteLine("序号,时间戳,转速(RPM),距离值(mm)");
// 写入数据 - 修改这里
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
if (i < _distanceValues.Count)
{
string timestamp = dataGridView1.Rows[i].Cells["Timestamp"].Value?.ToString() ?? "";
string speed = dataGridView1.Rows[i].Cells["speed"].Value?.ToString() ?? "--";
// 只写入数据,不需要其他修改
sw.WriteLine($"{i + 1},{timestamp},{speed},{_distanceValues[i]:F6}");
}
}
// 写入统计信息 - 这部分可以保持原样,或者选择性添加
sw.WriteLine();
sw.WriteLine("统计信息");
sw.WriteLine($"测试开始时间,{_testStartTime:yyyy-MM-dd HH:mm:ss}");
sw.WriteLine($"测试持续时间,{txtTestTime.Text}秒");
sw.WriteLine($"数据总数,{_distanceValues.Count}");
sw.WriteLine($"最小距离,{GetMinDistance():F6} mm");
sw.WriteLine($"最大距离,{GetMaxDistance():F6} mm");
sw.WriteLine($"距离抖动值,{GetJitter():F6} mm");
}
LogMessage($"数据已成功导出到: {saveFileDialog.FileName}");
MessageBox.Show("数据导出成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"导出失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void BtnClearData_Click(object sender, EventArgs e)
{
if (_isTesting)
{
MessageBox.Show("请先停止测试!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
ClearData();
LogMessage("数据已清空");
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
StopTest();
if (_client != null && _client.Connected)
{
_client.Close();
}
}
private void StartTest(int testTimeSeconds)
{
if (_isTesting) return;
_isTesting = true;
_testStartTime = DateTime.Now;
// 设置控件状态
btnStartTest.Enabled = false;
btnStopTest.Enabled = true;
btnExportData.Enabled = false;
btnClearData.Enabled = false;
txtTestTime.Enabled = false;
// 启动测试线程
_testThread = new Thread(() => RunTest(testTimeSeconds));
_testThread.Start();
LogMessage($"测试开始,持续时间: {testTimeSeconds}秒");
}
private void StopTest()
{
if (!_isTesting) return;
_isTesting = false;
if (_testThread != null && _testThread.IsAlive)
{
_testThread.Join(1000); // 等待线程结束
}
ModbusResourceManager.Instance.ReleaseResource();
// 计算并显示统计信息
CalculateAndDisplayStats();
// 重置控件状态
btnStartTest.Enabled = true;
btnStartCalc.Enabled = true;
btnStopTest.Enabled = false;
btnExportData.Enabled = true;
btnClearData.Enabled = true;
txtTestTime.Enabled = true;
txtCalcCount.Enabled = true;
LogMessage("测试已停止");
}
private void AutoExportReport()
{
// 如果是多次测试不调用自动报表由GenerateMultipleTestReport生成
if (_calculationCount > 1)
{
LogMessage("多次测试已完成,将生成综合报表");
return;
}
// 以下是原有的单次测试报表代码
try
{
if (_distanceValues.Count == 0)
{
LogMessage("没有测试数据,跳过自动导出");
return;
}
// 获取产品批号
string productModel = string.IsNullOrWhiteSpace(txtProductModel.Text)
? "未命名产品"
: txtProductModel.Text.Trim();
// 获取转速
string speedValue = "--";
if (dataGridView1.Rows.Count > 0)
{
var lastRow = dataGridView1.Rows[dataGridView1.Rows.Count - 1];
speedValue = lastRow.Cells["speed"].Value?.ToString() ?? "--";
}
// 计算统计数据
double minDistance = GetMinDistance();
double maxDistance = GetMaxDistance();
double jitter = GetJitter();
// 生成文件名
string fileName = $"测试报表_{productModel}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
string filePath = Path.Combine(Application.StartupPath, "测试报表", fileName);
// 确保目录存在
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
// 写入报表
using (StreamWriter sw = new StreamWriter(filePath, false, System.Text.Encoding.UTF8))
{
sw.WriteLine("=== 测试报表 ===");
sw.WriteLine($"生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
sw.WriteLine($"产品批号: {productModel}");
sw.WriteLine($"测试时间: {txtTestTime.Text} 秒");
sw.WriteLine($"计算次数: 1 次");
sw.WriteLine($"数据总数: {_distanceValues.Count}");
sw.WriteLine("");
sw.WriteLine("=== 测试结果 ===");
sw.WriteLine($"距离最大值: {maxDistance:F6} mm");
sw.WriteLine($"距离最小值: {minDistance:F6} mm");
sw.WriteLine($"抖动值: {jitter:F6} mm");
sw.WriteLine($"转速: {speedValue}");
}
LogMessage($"报表已自动导出到: {filePath}");
}
catch (Exception ex)
{
LogMessage($"自动导出报表失败: {ex.Message}");
}
}
private void GenerateMultipleTestReport()
{
try
{
if (_allTestResults.Count == 0)
{
LogMessage("没有测试数据,无法生成报表");
return;
}
// 获取产品批号
string productModel = string.IsNullOrWhiteSpace(txtProductModel.Text)
? "未命名产品"
: txtProductModel.Text.Trim();
// 生成文件名
string fileName = $"多次测试报表_{productModel}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
string filePath = Path.Combine(Application.StartupPath, "测试报表", fileName);
// 确保目录存在
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
// 获取转速 - 从label5获取最后显示的转速值
string speedValue = label5.Text;
// 如果转速显示包含"转速:",提取纯数值
if (speedValue.Contains("转速:"))
{
speedValue = speedValue.Replace("转速:", "").Replace("RPM", "").Trim();
}
// 写入报表
using (StreamWriter sw = new StreamWriter(filePath, false, System.Text.Encoding.UTF8))
{
sw.WriteLine("=== 多次测试报表 ===");
sw.WriteLine($"生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
sw.WriteLine($"产品批号: {productModel}");
sw.WriteLine($"单次测试时间: {txtTestTime.Text} 秒");
sw.WriteLine($"计算次数: {_calculationCount} 次");
sw.WriteLine($"总数据量: {_allTestResults.Sum(list => list.Count)}");
sw.WriteLine($"转速: {label5.Text}");
sw.WriteLine("");
// 写入每次测试的详细结果
for (int i = 0; i < _allTestResults.Count; i++)
{
var testData = _allTestResults[i];
if (testData.Count == 0) continue;
double min = GetMinDistanceFromList(testData);
double max = GetMaxDistanceFromList(testData);
double jitter = max - min;
sw.WriteLine($"第 {i + 1} 次测试:");
sw.WriteLine($" 距离最大值: {max:F6} mm");
sw.WriteLine($" 距离最小值: {min:F6} mm");
sw.WriteLine($" 抖动值: {jitter:F6} mm");
sw.WriteLine($" 数据量: {testData.Count}");
sw.WriteLine();
}
// 写入综合统计信息
sw.WriteLine("=== 综合统计结果 ===");
// 计算所有测试的最大值、最小值、平均值
List<double> allMins = new List<double>();
List<double> allMaxs = new List<double>();
List<double> allJitters = new List<double>();
foreach (var testData in _allTestResults)
{
if (testData.Count == 0) continue;
double min = GetMinDistanceFromList(testData);
double max = GetMaxDistanceFromList(testData);
double jitter = max - min;
allMins.Add(min);
allMaxs.Add(max);
allJitters.Add(jitter);
}
if (allMins.Count > 0)
{
sw.WriteLine($"所有测试距离最大值: {allMaxs.Max():F6} mm");
sw.WriteLine($"所有测试距离最小值: {allMins.Min():F6} mm");
sw.WriteLine($"距离最大值平均值: {allMaxs.Average():F6} mm");
sw.WriteLine($"距离最小值平均值: {allMins.Average():F6} mm");
sw.WriteLine($"抖动值平均值: {allJitters.Average():F6} mm");
sw.WriteLine($"抖动值最大偏差: {allJitters.Max() - allJitters.Min():F6} mm");
}
}
LogMessage($"多次测试报表已生成: {filePath}");
// 弹出提示
if (MessageBox.Show($"计算完成,报表已导出到:\n{filePath}\n\n是否要打开报表文件",
"计算完成", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
System.Diagnostics.Process.Start(filePath);
}
}
catch (Exception ex)
{
LogMessage($"生成多次测试报表失败: {ex.Message}");
}
}
private void RunTest(int testTimeSeconds)
{
DateTime endTime = DateTime.Now.AddSeconds(testTimeSeconds);
try
{
// 配置设备为输出位置1
_master.WriteSingleRegister(1, 24, 0);
while (_isTesting && DateTime.Now < endTime)
{
// 读取距离数据距离1地址0开始的两个寄存器
ushort[] data = _master.ReadInputRegisters(1, 0, 2);
string plcIp = "192.168.1.5";
//string plcIp = "127.0.0.1";
bool initSuccess = ModbusResourceManager.Instance.Init(plcIp, 502);
if (!initSuccess)
{
MessageBox.Show("连接Modbus服务器失败", "错误");
this.Close();
return;
}
// 检查连接状态
if (_tcpClient == null || !_tcpClient.Connected)
{
MessageBox.Show("Modbus连接异常", "错误");
this.Close();
return;
}
c = new DataChange();
var registers = _modbusMaster.ReadHoldingRegisters(1, 118, 2);
this.Invoke(new Action(() =>
{
float floatValue1 = c.UshortToFloat(registers[1], registers[0]);
// 方式2: 直接合并为32位整数
int intValue = (registers[0] << 16) | registers[1];
int intValue2 = (registers[1] << 16) | registers[0]; // 交换顺序
// 显示所有可能的结果
label5.Text = $@"转速: {floatValue1:F2}";
}));
if (data.Length >= 2)
{
// 合并两个寄存器的数据并转换为距离值单位mm
double distance = MergeUShortToInt(data[0], data[1]) / 1000.0 / 1000;
// 添加到数据列表
_distanceValues.Add(distance);
// 更新UI
UpdateUIWithDistance(DateTime.Now, distance);
}
// 控制读取频率例如10ms一次
Thread.Sleep(10);
}
}
catch (Exception ex)
{
LogMessage($"测试过程中发生错误: {ex.Message}");
}
finally
{
// 测试结束后,在主线程中停止测试
if (InvokeRequired)
{
Invoke(new Action(StopTest));
}
else
{
StopTest();
}
}
}
private int MergeUShortToInt(ushort hData, ushort lData)
{
return (hData << 16) | lData;
}
private void UpdateUIWithDistance(DateTime timestamp, double distance, string testInfo = "")
{
if (dataGridView1.InvokeRequired)
{
AddDataRowDelegate d = new AddDataRowDelegate(UpdateUIWithDistance);
dataGridView1.Invoke(d, new object[] { timestamp, distance, testInfo });
}
else
{
// 添加新行到DataGridView
int rowIndex = dataGridView1.Rows.Add();
dataGridView1.Rows[rowIndex].Cells["Timestamp"].Value = timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff");
dataGridView1.Rows[rowIndex].Cells["speed"].Value = label5.Text + " " + testInfo;
dataGridView1.Rows[rowIndex].Cells["Distance"].Value = distance.ToString("F6", CultureInfo.InvariantCulture);
// 自动滚动到最后一行
dataGridView1.FirstDisplayedScrollingRowIndex = rowIndex;
}
}
// 同时修改委托定义
delegate void AddDataRowDelegate(DateTime timestamp, double distance, string testInfo);
private void CalculateAndDisplayStats()
{
if (_distanceValues.Count == 0) return;
double min = GetMinDistance();
double max = GetMaxDistance();
double jitter = GetJitter();
if (lblMin.InvokeRequired)
{
UpdateStatsDelegate d = new UpdateStatsDelegate(CalculateAndDisplayStats);
lblMin.Invoke(d);
}
else
{
lblMin.Text = $"最小值: {min:F6} mm";
lblMax.Text = $"最大值: {max:F6} mm";
lblJitter.Text = $"抖动值: {jitter:F6} mm";
// 在日志中显示统计信息
LogMessage($"测试完成 - 数据总数: {_distanceValues.Count}");
LogMessage($"最小值: {min:F6} mm, 最大值: {max:F6} mm, 抖动值: {jitter:F6} mm");
}
}
private double GetMinDistance()
{
if (_distanceValues.Count == 0) return 0;
double min = _distanceValues[0];
foreach (double value in _distanceValues)
{
if (value < min) min = value;
}
return min;
}
private double GetMaxDistance()
{
if (_distanceValues.Count == 0) return 0;
double max = _distanceValues[0];
foreach (double value in _distanceValues)
{
if (value > max) max = value;
}
return max;
}
private double GetJitter()
{
if (_distanceValues.Count == 0) return 0;
return GetMaxDistance() - GetMinDistance();
}
private void ClearData()
{
_distanceValues.Clear();
_allTestResults.Clear();
dataGridView1.Rows.Clear();
lblMin.Text = "最小值: --";
lblMax.Text = "最大值: --";
lblJitter.Text = "抖动值: --";
}
private void LogMessage(string message)
{
if (txtLog.InvokeRequired)
{
UpdateUIDelegate d = new UpdateUIDelegate(LogMessage);
txtLog.Invoke(d, new object[] { message });
}
else
{
string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
txtLog.AppendText($"[{timestamp}] {message}{Environment.NewLine}");
// 自动滚动到最后一行
txtLog.SelectionStart = txtLog.TextLength;
txtLog.ScrollToCaret();
}
}
// 以下是窗体设计器生成的代码
private System.ComponentModel.IContainer components = null;
private TextBox txtIP;
private Label label1;
private Label label2;
private TextBox txtPort;
private Button btnConnect;
private Button btnDisconnect;
private GroupBox groupBox1;
private DataGridView dataGridView1;
private GroupBox groupBox2;
private Label label3;
private TextBox txtTestTime;
private Button btnStartTest;
private Button btnStopTest;
private GroupBox groupBox3;
private Label lblMin;
private Label lblMax;
private Label lblJitter;
private Button btnExportData;
private Button btnClearData;
private TextBox txtLog;
private Label label4;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.txtIP = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.txtPort = new System.Windows.Forms.TextBox();
this.btnConnect = new System.Windows.Forms.Button();
this.btnDisconnect = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.btnClearData = new System.Windows.Forms.Button();
this.btnExportData = new System.Windows.Forms.Button();
this.btnStopTest = new System.Windows.Forms.Button();
this.btnStartTest = new System.Windows.Forms.Button();
this.txtTestTime = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.label5 = new System.Windows.Forms.Label();
this.lblJitter = new System.Windows.Forms.Label();
this.lblMax = new System.Windows.Forms.Label();
this.lblMin = new System.Windows.Forms.Label();
this.txtLog = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.SuspendLayout();
//
// txtIP
//
this.txtIP.Location = new System.Drawing.Point(12, 31);
this.txtIP.Name = "txtIP";
this.txtIP.Size = new System.Drawing.Size(120, 25);
this.txtIP.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 15);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(68, 15);
this.label1.TabIndex = 1;
this.label1.Text = "IP地址";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(138, 15);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(67, 15);
this.label2.TabIndex = 3;
this.label2.Text = "端口号:";
//
// txtPort
//
this.txtPort.Location = new System.Drawing.Point(138, 31);
this.txtPort.Name = "txtPort";
this.txtPort.Size = new System.Drawing.Size(60, 25);
this.txtPort.TabIndex = 2;
//
// btnConnect
//
this.btnConnect.Location = new System.Drawing.Point(204, 29);
this.btnConnect.Name = "btnConnect";
this.btnConnect.Size = new System.Drawing.Size(75, 23);
this.btnConnect.TabIndex = 4;
this.btnConnect.Text = "连接";
this.btnConnect.UseVisualStyleBackColor = true;
//
// btnDisconnect
//
this.btnDisconnect.Enabled = false;
this.btnDisconnect.Location = new System.Drawing.Point(285, 29);
this.btnDisconnect.Name = "btnDisconnect";
this.btnDisconnect.Size = new System.Drawing.Size(75, 23);
this.btnDisconnect.TabIndex = 5;
this.btnDisconnect.Text = "断开";
this.btnDisconnect.UseVisualStyleBackColor = true;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.dataGridView1);
this.groupBox1.Location = new System.Drawing.Point(12, 70);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(534, 300);
this.groupBox1.TabIndex = 6;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "实时数据";
//
// dataGridView1
//
this.dataGridView1.AllowUserToAddRows = false;
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Location = new System.Drawing.Point(3, 21);
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowHeadersWidth = 51;
this.dataGridView1.Size = new System.Drawing.Size(525, 276);
this.dataGridView1.TabIndex = 0;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.btnClearData);
this.groupBox2.Controls.Add(this.btnExportData);
this.groupBox2.Controls.Add(this.btnStopTest);
this.groupBox2.Controls.Add(this.btnStartTest);
this.groupBox2.Controls.Add(this.txtTestTime);
this.groupBox2.Controls.Add(this.label3);
this.groupBox2.Location = new System.Drawing.Point(552, 69);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(200, 150);
this.groupBox2.TabIndex = 7;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "测试控制";
//
// btnClearData
//
this.btnClearData.Location = new System.Drawing.Point(105, 115);
this.btnClearData.Name = "btnClearData";
this.btnClearData.Size = new System.Drawing.Size(75, 23);
this.btnClearData.TabIndex = 5;
this.btnClearData.Text = "清空数据";
this.btnClearData.UseVisualStyleBackColor = true;
//
// btnExportData
//
this.btnExportData.Location = new System.Drawing.Point(24, 115);
this.btnExportData.Name = "btnExportData";
this.btnExportData.Size = new System.Drawing.Size(75, 23);
this.btnExportData.TabIndex = 4;
this.btnExportData.Text = "导出数据";
this.btnExportData.UseVisualStyleBackColor = true;
//
// btnStopTest
//
this.btnStopTest.Enabled = false;
this.btnStopTest.Location = new System.Drawing.Point(105, 75);
this.btnStopTest.Name = "btnStopTest";
this.btnStopTest.Size = new System.Drawing.Size(75, 23);
this.btnStopTest.TabIndex = 3;
this.btnStopTest.Text = "停止测试";
this.btnStopTest.UseVisualStyleBackColor = true;
//
// btnStartTest
//
this.btnStartTest.Enabled = false;
this.btnStartTest.Location = new System.Drawing.Point(24, 75);
this.btnStartTest.Name = "btnStartTest";
this.btnStartTest.Size = new System.Drawing.Size(75, 23);
this.btnStartTest.TabIndex = 2;
this.btnStartTest.Text = "开始测试";
this.btnStartTest.UseVisualStyleBackColor = true;
//
// txtTestTime
//
this.txtTestTime.Location = new System.Drawing.Point(65, 35);
this.txtTestTime.Name = "txtTestTime";
this.txtTestTime.Size = new System.Drawing.Size(60, 25);
this.txtTestTime.TabIndex = 1;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(21, 38);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(61, 15);
this.label3.TabIndex = 0;
this.label3.Text = "时间(s)";
//
// groupBox3
//
this.groupBox3.Controls.Add(this.label5);
this.groupBox3.Controls.Add(this.lblJitter);
this.groupBox3.Controls.Add(this.lblMax);
this.groupBox3.Controls.Add(this.lblMin);
this.groupBox3.Location = new System.Drawing.Point(552, 225);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(200, 144);
this.groupBox3.TabIndex = 8;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "统计信息";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(21, 30);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(69, 15);
this.label5.TabIndex = 3;
this.label5.Text = "转速: --";
//
// lblJitter
//
this.lblJitter.AutoSize = true;
this.lblJitter.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lblJitter.ForeColor = System.Drawing.Color.Red;
this.lblJitter.Location = new System.Drawing.Point(21, 111);
this.lblJitter.Name = "lblJitter";
this.lblJitter.Size = new System.Drawing.Size(78, 18);
this.lblJitter.TabIndex = 2;
this.lblJitter.Text = "抖动值: --";
//
// lblMax
//
this.lblMax.AutoSize = true;
this.lblMax.Location = new System.Drawing.Point(21, 86);
this.lblMax.Name = "lblMax";
this.lblMax.Size = new System.Drawing.Size(84, 15);
this.lblMax.TabIndex = 1;
this.lblMax.Text = "最大值: --";
//
// lblMin
//
this.lblMin.AutoSize = true;
this.lblMin.Location = new System.Drawing.Point(21, 58);
this.lblMin.Name = "lblMin";
this.lblMin.Size = new System.Drawing.Size(84, 15);
this.lblMin.TabIndex = 0;
this.lblMin.Text = "最小值: --";
//
// txtLog
//
this.txtLog.Location = new System.Drawing.Point(12, 400);
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtLog.Size = new System.Drawing.Size(554, 150);
this.txtLog.TabIndex = 9;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(12, 384);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(52, 15);
this.label4.TabIndex = 10;
this.label4.Text = "日志:";
//
// MainForm
//
this.ClientSize = new System.Drawing.Size(776, 561);
this.Controls.Add(this.label4);
this.Controls.Add(this.txtLog);
this.Controls.Add(this.groupBox3);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.btnDisconnect);
this.Controls.Add(this.btnConnect);
this.Controls.Add(this.label2);
this.Controls.Add(this.txtPort);
this.Controls.Add(this.label1);
this.Controls.Add(this.txtIP);
this.Name = "MainForm";
this.Text = "激光位移传感器测试程序";
this.Load += new System.EventHandler(this.MainForm_Load);
this.groupBox1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
private void MainForm_Load(object sender, EventArgs e)
{
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}