# 气垫导轨实验系统 - 文件组织与实现
在 WinForms 项目中,核心实现代码和界面设计代码通常分别存放在不同的文件中,遵循 C# 的 partial 类设计模式。以下是完整的文件组织方案:
## 文件结构
```
PhysicsExperimentMonitor/
├── MainForm.cs // 核心逻辑代码
├── MainForm.Designer.cs // 界面设计代码
├── Program.cs // 应用程序入口
└── ExperimentData.cs // 实验数据模型(可选)
```
## 1. MainForm.cs - 核心逻辑实现
```csharp
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO.Ports;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
namespace PhysicsExperimentMonitor
{
public partial class MainForm : Form
{
// 实验数据结构
public class ExperimentData
{
// 属性定义(与之前相同)
// ...
}
// 成员变量
private SerialPort serialPort;
private List<ExperimentData> experimentHistory = new List<ExperimentData>();
private int currentExperimentId = 1;
private ExperimentData currentExperiment;
// 当前实验参数
private double currentLightBlockWidth;
private double currentGateDistance;
private double currentSliderMass;
private double currentWeightMass;
// 正则表达式
private Regex timeRegex = new Regex(@"光电门(\d)挡光时间[::]\s*(\d+\.?\d*)\s*s");
private Regex velocityRegex = new Regex(@"通过光电门(\d)速度[::]\s*(\d+\.?\d*)\s*m/s");
private Regex accelRegex = new Regex(@"加速度[::]\s*(\d+\.?\d*)\s*m/s²");
private Regex dataStartRegex = new Regex(@"=+实验数据=+");
public MainForm()
{
InitializeComponent();
InitializeSerialPort();
InitializeChart();
InitializeDataGridView();
// 填充可用串口
cmbPorts.Items.AddRange(SerialPort.GetPortNames());
if (cmbPorts.Items.Count > 0) cmbPorts.SelectedIndex = 0;
// 设置默认波特率
cmbBaudRate.SelectedIndex = 3; // 115200
// 设置默认参数值
txtLightBlockWidth.Text = "0.01";
txtGateDistance.Text = "0.3";
txtSliderMass.Text = "0.2";
txtWeightMass.Text = "0.05";
lblStatus.Text = "就绪";
btnEndExperiment.Enabled = false;
}
private void InitializeSerialPort()
{
serialPort = new SerialPort();
serialPort.DataReceived += SerialPort_DataReceived;
}
private void InitializeChart()
{
// 图表初始化代码(与之前相同)
// ...
}
private void InitializeDataGridView()
{
// 数据表格初始化代码(与之前相同)
// ...
}
// 启动新实验
private void btnStartExperiment_Click(object sender, EventArgs e)
{
// 启动实验逻辑(与之前相同)
// ...
}
// 结束当前实验
private void btnEndExperiment_Click(object sender, EventArgs e)
{
// 结束实验逻辑(与之前相同)
// ...
}
// 数据接收处理
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// 串口数据处理(与之前相同)
// ...
}
// 处理串口数据
private void ProcessSerialData(string data)
{
// 数据解析逻辑(与之前相同)
// ...
}
// 完成当前实验数据的收集
private void CompleteExperiment()
{
// 完成实验逻辑(与之前相同)
// ...
}
// 更新数据显示
private void UpdateDataDisplay(ExperimentData data)
{
// 更新表格显示(与之前相同)
// ...
}
// 更新图表
private void UpdateChart()
{
// 更新图表(与之前相同)
// ...
}
// 连接串口
private void btnConnect_Click(object sender, EventArgs e)
{
// 串口连接逻辑(与之前相同)
// ...
}
// 清空数据
private void btnClearData_Click(object sender, EventArgs e)
{
// 清空数据逻辑(与之前相同)
// ...
}
// 参数输入验证
private void ValidateNumericInput(object sender, EventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
if (!double.TryParse(textBox.Text, out double value) || value <= 0)
{
textBox.BackColor = Color.LightPink;
btnStartExperiment.Enabled = false;
}
else
{
textBox.BackColor = SystemColors.Window;
btnStartExperiment.Enabled =
double.TryParse(txtLightBlockWidth.Text, out _) &&
double.TryParse(txtGateDistance.Text, out _) &&
double.TryParse(txtSliderMass.Text, out _) &&
double.TryParse(txtWeightMass.Text, out _);
}
}
}
// 关闭窗体时断开串口
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort != null && serialPort.IsOpen)
serialPort.Close();
}
}
}
```
## 2. MainForm.Designer.cs - 界面设计代码
```csharp
namespace PhysicsExperimentMonitor
{
partial class MainForm
{
private System.ComponentModel.IContainer components = null;
// 控件声明
private ComboBox cmbPorts;
private ComboBox cmbBaudRate;
private Button btnConnect;
private DataGridView dgvData;
private Button btnExport;
private RichTextBox txtLog;
private Chart chartAcceleration;
private Label label1;
private Label label2;
private TextBox txtLightBlockWidth;
private TextBox txtGateDistance;
private TextBox txtSliderMass;
private TextBox txtWeightMass;
private Label label3;
private Label label4;
private Label label5;
private Label label6;
private Button btnStartExperiment;
private Button btnEndExperiment;
private Label lblStatus;
private Button btnClearData;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series();
System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series();
System.Windows.Forms.DataVisualization.Charting.Series series4 = new System.Windows.Forms.DataVisualization.Charting.Series();
this.cmbPorts = new System.Windows.Forms.ComboBox();
this.cmbBaudRate = new System.Windows.Forms.ComboBox();
this.btnConnect = new System.Windows.Forms.Button();
this.dgvData = new System.Windows.Forms.DataGridView();
this.btnExport = new System.Windows.Forms.Button();
this.txtLog = new System.Windows.Forms.RichTextBox();
this.chartAcceleration = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.txtLightBlockWidth = new System.Windows.Forms.TextBox();
this.txtGateDistance = new System.Windows.Forms.TextBox();
this.txtSliderMass = new System.Windows.Forms.TextBox();
this.txtWeightMass = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.btnStartExperiment = new System.Windows.Forms.Button();
this.btnEndExperiment = new System.Windows.Forms.Button();
this.lblStatus = new System.Windows.Forms.Label();
this.btnClearData = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.dgvData)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.chartAcceleration)).BeginInit();
this.SuspendLayout();
// cmbPorts
this.cmbPorts.FormattingEnabled = true;
this.cmbPorts.Location = new System.Drawing.Point(20, 20);
this.cmbPorts.Name = "cmbPorts";
this.cmbPorts.Size = new System.Drawing.Size(120, 28);
this.cmbPorts.TabIndex = 0;
// cmbBaudRate
this.cmbBaudRate.FormattingEnabled = true;
this.cmbBaudRate.Items.AddRange(new object[] {
"9600",
"19200",
"38400",
"57600",
"115200"});
this.cmbBaudRate.Location = new System.Drawing.Point(150, 20);
this.cmbBaudRate.Name = "cmbBaudRate";
this.cmbBaudRate.Size = new System.Drawing.Size(120, 28);
this.cmbBaudRate.TabIndex = 1;
// btnConnect
this.btnConnect.Location = new System.Drawing.Point(280, 20);
this.btnConnect.Name = "btnConnect";
this.btnConnect.Size = new System.Drawing.Size(120, 30);
this.btnConnect.TabIndex = 2;
this.btnConnect.Text = "连接串口";
this.btnConnect.UseVisualStyleBackColor = true;
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
// dgvData
this.dgvData.AllowUserToAddRows = false;
this.dgvData.AllowUserToDeleteRows = false;
this.dgvData.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dgvData.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dgvData.BackgroundColor = System.Drawing.SystemColors.ControlLightLight;
this.dgvData.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgvData.Location = new System.Drawing.Point(20, 220);
this.dgvData.Name = "dgvData";
this.dgvData.ReadOnly = true;
this.dgvData.RowHeadersWidth = 51;
this.dgvData.RowTemplate.Height = 24;
this.dgvData.Size = new System.Drawing.Size(1200, 250);
this.dgvData.TabIndex = 3;
// btnExport
this.btnExport.Location = new System.Drawing.Point(410, 20);
this.btnExport.Name = "btnExport";
this.btnExport.Size = new System.Drawing.Size(120, 30);
this.btnExport.TabIndex = 4;
this.btnExport.Text = "导出数据";
this.btnExport.UseVisualStyleBackColor = true;
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
// txtLog
this.txtLog.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtLog.BackColor = System.Drawing.Color.Black;
this.txtLog.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.txtLog.ForeColor = System.Drawing.Color.White;
this.txtLog.Location = new System.Drawing.Point(20, 480);
this.txtLog.Name = "txtLog";
this.txtLog.Size = new System.Drawing.Size(1200, 150);
this.txtLog.TabIndex = 5;
this.txtLog.Text = "";
// chartAcceleration
this.chartAcceleration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
chartArea1.Name = "ChartArea1";
this.chartAcceleration.ChartAreas.Add(chartArea1);
legend1.Name = "Legend1";
this.chartAcceleration.Legends.Add(legend1);
this.chartAcceleration.Location = new System.Drawing.Point(430, 60);
this.chartAcceleration.Name = "chartAcceleration";
series1.ChartArea = "ChartArea1";
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series1.Legend = "Legend1";
series1.Name = "实测加速度";
series2.ChartArea = "ChartArea1";
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series2.Legend = "Legend1";
series2.Name = "理论加速度";
series3.ChartArea = "ChartArea1";
series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series3.Legend = "Legend1";
series3.Name = "单片机加速度";
series4.ChartArea = "ChartArea1";
series4.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series4.Legend = "Legend1";
series4.Name = "误差(%)";
this.chartAcceleration.Series.Add(series1);
this.chartAcceleration.Series.Add(series2);
this.chartAcceleration.Series.Add(series3);
this.chartAcceleration.Series.Add(series4);
this.chartAcceleration.Size = new System.Drawing.Size(790, 150);
this.chartAcceleration.TabIndex = 6;
this.chartAcceleration.Text = "chart1";
// label1
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(20, 70);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(117, 20);
this.label1.TabIndex = 7;
this.label1.Text = "挡光片宽度(m):";
// label2
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(20, 100);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(117, 20);
this.label2.TabIndex = 8;
this.label2.Text = "光电门间距(m):";
// txtLightBlockWidth
this.txtLightBlockWidth.Location = new System.Drawing.Point(150, 67);
this.txtLightBlockWidth.Name = "txtLightBlockWidth";
this.txtLightBlockWidth.Size = new System.Drawing.Size(100, 27);
this.txtLightBlockWidth.TabIndex = 9;
this.txtLightBlockWidth.Text = "0.01";
this.txtLightBlockWidth.TextChanged += new System.EventHandler(this.ValidateNumericInput);
// txtGateDistance
this.txtGateDistance.Location = new System.Drawing.Point(150, 97);
this.txtGateDistance.Name = "txtGateDistance";
this.txtGateDistance.Size = new System.Drawing.Size(100, 27);
this.txtGateDistance.TabIndex = 10;
this.txtGateDistance.Text = "0.3";
this.txtGateDistance.TextChanged += new System.EventHandler(this.ValidateNumericInput);
// txtSliderMass
this.txtSliderMass.Location = new System.Drawing.Point(150, 127);
this.txtSliderMass.Name = "txtSliderMass";
this.txtSliderMass.Size = new System.Drawing.Size(100, 27);
this.txtSliderMass.TabIndex = 12;
this.txtSliderMass.Text = "0.2";
this.txtSliderMass.TextChanged += new System.EventHandler(this.ValidateNumericInput);
// txtWeightMass
this.txtWeightMass.Location = new System.Drawing.Point(150, 157);
this.txtWeightMass.Name = "txtWeightMass";
this.txtWeightMass.Size = new System.Drawing.Size(100, 27);
this.txtWeightMass.TabIndex = 13;
this.txtWeightMass.Text = "0.05";
this.txtWeightMass.TextChanged += new System.EventHandler(this.ValidateNumericInput);
// label3
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(20, 130);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(117, 20);
this.label3.TabIndex = 14;
this.label3.Text = "滑块质量(kg):";
// label4
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(20, 160);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(117, 20);
this.label4.TabIndex = 15;
this.label4.Text = "砝码质量(kg):";
// label5
this.label5.AutoSize = true;
this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label5.Location = new System.Drawing.Point(280, 70);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(130, 20);
this.label5.TabIndex = 16;
this.label5.Text = "实验参数设置";
// label6
this.label6.AutoSize = true;
this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label6.Location = new System.Drawing.Point(20, 190);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(130, 20);
this.label6.TabIndex = 17;
this.label6.Text = "实验数据表格";
// btnStartExperiment
this.btnStartExperiment.BackColor = System.Drawing.Color.LightGreen;
this.btnStartExperiment.Location = new System.Drawing.Point(280, 100);
this.btnStartExperiment.Name = "btnStartExperiment";
this.btnStartExperiment.Size = new System.Drawing.Size(120, 40);
this.btnStartExperiment.TabIndex = 18;
this.btnStartExperiment.Text = "开始实验";
this.btnStartExperiment.UseVisualStyleBackColor = false;
this.btnStartExperiment.Click += new System.EventHandler(this.btnStartExperiment_Click);
// btnEndExperiment
this.btnEndExperiment.BackColor = System.Drawing.Color.LightCoral;
this.btnEndExperiment.Location = new System.Drawing.Point(280, 150);
this.btnEndExperiment.Name = "btnEndExperiment";
this.btnEndExperiment.Size = new System.Drawing.Size(120, 40);
this.btnEndExperiment.TabIndex = 19;
this.btnEndExperiment.Text = "结束实验";
this.btnEndExperiment.UseVisualStyleBackColor = false;
this.btnEndExperiment.Click += new System.EventHandler(this.btnEndExperiment_Click);
// lblStatus
this.lblStatus.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.lblStatus.BackColor = System.Drawing.SystemColors.Info;
this.lblStatus.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.lblStatus.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lblStatus.Location = new System.Drawing.Point(430, 20);
this.lblStatus.Name = "lblStatus";
this.lblStatus.Size = new System.Drawing.Size(790, 30);
this.lblStatus.TabIndex = 20;
this.lblStatus.Text = "就绪";
this.lblStatus.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// btnClearData
this.btnClearData.BackColor = System.Drawing.Color.LightSalmon;
this.btnClearData.Location = new System.Drawing.Point(410, 150);
this.btnClearData.Name = "btnClearData";
this.btnClearData.Size = new System.Drawing.Size(120, 40);
this.btnClearData.TabIndex = 21;
this.btnClearData.Text = "清空数据";
this.btnClearData.UseVisualStyleBackColor = false;
this.btnClearData.Click += new System.EventHandler(this.btnClearData_Click);
// MainForm
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1240, 650);
this.Controls.Add(this.btnClearData);
this.Controls.Add(this.lblStatus);
this.Controls.Add(this.btnEndExperiment);
this.Controls.Add(this.btnStartExperiment);
this.Controls.Add(this.label6);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.txtWeightMass);
this.Controls.Add(this.txtSliderMass);
this.Controls.Add(this.txtGateDistance);
this.Controls.Add(this.txtLightBlockWidth);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.chartAcceleration);
this.Controls.Add(this.txtLog);
this.Controls.Add(this.btnExport);
this.Controls.Add(this.dgvData);
this.Controls.Add(this.btnConnect);
this.Controls.Add(this.cmbBaudRate);
this.Controls.Add(this.cmbPorts);
this.Font = new System.Drawing.Font("Microsoft Sans Serif", 10.2F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.Margin = new System.Windows.Forms.Padding(4);
this.Name = "MainForm";
this.Text = "气垫导轨实验系统";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
((System.ComponentModel.ISupportInitialize)(this.dgvData)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.chartAcceleration)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
}
}
```
## 3. Program.cs - 应用程序入口
```csharp
using System;
using System.Windows.Forms;
namespace PhysicsExperimentMonitor
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
```
## 4. ExperimentData.cs - 数据模型(可选)
```csharp
using System;
namespace PhysicsExperimentMonitor
{
public class ExperimentData
{
public int ExperimentID { get; set; }
public DateTime Timestamp { get; set; }
public double LightBlockWidth { get; set; }
public double GateDistance { get; set; }
public double TimeGate1 { get; set; }
public double TimeGate2 { get; set; }
public double SliderMass { get; set; }
public double WeightMass { get; set; }
public double Velocity1 => LightBlockWidth / TimeGate1;
public double Velocity2 => LightBlockWidth / TimeGate2;
public double TotalMass => SliderMass + WeightMass;
public double PullForce => WeightMass * 9.8;
public double Acceleration => (Velocity2 * Velocity2 - Velocity1 * Velocity1) / (2 * GateDistance);
public double ExpectedAcceleration => PullForce / TotalMass;
public double MCU_Velocity1 { get; set; }
public double MCU_Velocity2 { get; set; }
public double MCU_Acceleration { get; set; }
public double ErrorPercentage => Math.Abs(Acceleration - ExpectedAcceleration) / ExpectedAcceleration * 100;
}
}
```
## 系统实现要点
1. **Partial 类设计**:
- `MainForm.cs` 包含业务逻辑和事件处理
- `MainForm.Designer.cs` 包含界面布局和控件初始化
- 两部分通过 `partial class MainForm` 组合
2. **数据流处理**:
- 串口数据接收 → 正则表达式解析 → 数据存储 → UI 更新
3. **参数动态设置**:
- 每次实验前可修改挡光片宽度、光电门间距等参数
- 参数验证确保输入有效性
4. **多行数据解析**:
- 使用正则表达式处理不同格式的数据行
- 自动识别数据开始标记和结束标记
5. **错误处理**:
- 参数验证防止无效输入
- 异常捕获确保程序稳定性
- 状态指示器显示当前系统状态
最新发布