841 lines
31 KiB
C#
841 lines
31 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.ComponentModel;
|
|||
|
|
using System.Data;
|
|||
|
|
using System.Drawing;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using System.Windows.Forms;
|
|||
|
|
using System.IO;
|
|||
|
|
using NPOI.SS.UserModel;
|
|||
|
|
using NPOI.XSSF.UserModel; // 用于 .xlsx
|
|||
|
|
using NPOI.HSSF.UserModel; // 用于 .xls
|
|||
|
|
using NPOI.SS.Util;
|
|||
|
|
|
|||
|
|
// 使用别名解决命名空间冲突
|
|||
|
|
using NPOIBorderStyle = NPOI.SS.UserModel.BorderStyle;
|
|||
|
|
using NPOIHorizontalAlignment = NPOI.SS.UserModel.HorizontalAlignment;
|
|||
|
|
|
|||
|
|
namespace WindowsFormsApp6
|
|||
|
|
{
|
|||
|
|
public partial class Form3 : Form
|
|||
|
|
{
|
|||
|
|
private System.Windows.Forms.Timer dataTimer;
|
|||
|
|
private DataTable sampleDataTable;
|
|||
|
|
|
|||
|
|
public Form3()
|
|||
|
|
{
|
|||
|
|
InitializeComponent();
|
|||
|
|
InitializeDataTable();
|
|||
|
|
InitializeTimer();
|
|||
|
|
InitializeDataGridView();
|
|||
|
|
InitializeEventHandlers();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化 DataGridView 列 - 实现复杂多级表头
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializeDataGridView()
|
|||
|
|
{
|
|||
|
|
// 禁用自动生成列
|
|||
|
|
dataGridView1.AutoGenerateColumns = false;
|
|||
|
|
|
|||
|
|
// 先解除数据绑定
|
|||
|
|
dataGridView1.DataSource = null;
|
|||
|
|
|
|||
|
|
// 清除现有列
|
|||
|
|
dataGridView1.Columns.Clear();
|
|||
|
|
|
|||
|
|
// 先移除可能存在的事件处理器,避免重复绑定
|
|||
|
|
dataGridView1.CellFormatting -= DataGridView1_CellFormatting;
|
|||
|
|
dataGridView1.CellValueChanged -= DataGridView1_CellValueChanged;
|
|||
|
|
dataGridView1.CellBeginEdit -= DataGridView1_CellBeginEdit;
|
|||
|
|
|
|||
|
|
// 白色背景样式(只读)- 不设置BackColor,让隔行变色生效
|
|||
|
|
DataGridViewCellStyle readonlyStyle = new DataGridViewCellStyle
|
|||
|
|
{
|
|||
|
|
Alignment = DataGridViewContentAlignment.MiddleCenter,
|
|||
|
|
SelectionBackColor = Color.LightGray
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 白色背景样式(可编辑)- 不设置BackColor,让隔行变色生效
|
|||
|
|
DataGridViewCellStyle editableStyle = new DataGridViewCellStyle
|
|||
|
|
{
|
|||
|
|
Alignment = DataGridViewContentAlignment.MiddleCenter,
|
|||
|
|
SelectionBackColor = Color.LightBlue
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 序号列
|
|||
|
|
DataGridViewTextBoxColumn seqCol = new DataGridViewTextBoxColumn
|
|||
|
|
{
|
|||
|
|
Name = "序号",
|
|||
|
|
HeaderText = "序号",
|
|||
|
|
DataPropertyName = "序号",
|
|||
|
|
Width = 120,
|
|||
|
|
ReadOnly = true,
|
|||
|
|
DefaultCellStyle = (DataGridViewCellStyle)readonlyStyle.Clone()
|
|||
|
|
};
|
|||
|
|
dataGridView1.Columns.Add(seqCol);
|
|||
|
|
|
|||
|
|
// 为每个试样添加3列(试样次数:1、2、3)
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
// 列1:试样次数1
|
|||
|
|
DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn
|
|||
|
|
{
|
|||
|
|
Name = $"试样{i}_1",
|
|||
|
|
HeaderText = $"试样{i}\n1",
|
|||
|
|
DataPropertyName = $"试样{i}_1",
|
|||
|
|
Width = 100,
|
|||
|
|
ReadOnly = false,
|
|||
|
|
DefaultCellStyle = (DataGridViewCellStyle)editableStyle.Clone()
|
|||
|
|
};
|
|||
|
|
dataGridView1.Columns.Add(col1);
|
|||
|
|
|
|||
|
|
// 列2:试样次数2
|
|||
|
|
DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn
|
|||
|
|
{
|
|||
|
|
Name = $"试样{i}_2",
|
|||
|
|
HeaderText = $"试样{i}\n2",
|
|||
|
|
DataPropertyName = $"试样{i}_2",
|
|||
|
|
Width = 100,
|
|||
|
|
ReadOnly = false,
|
|||
|
|
DefaultCellStyle = (DataGridViewCellStyle)editableStyle.Clone()
|
|||
|
|
};
|
|||
|
|
dataGridView1.Columns.Add(col2);
|
|||
|
|
|
|||
|
|
// 列3:试样次数3
|
|||
|
|
DataGridViewTextBoxColumn col3 = new DataGridViewTextBoxColumn
|
|||
|
|
{
|
|||
|
|
Name = $"试样{i}_3",
|
|||
|
|
HeaderText = $"试样{i}\n3",
|
|||
|
|
DataPropertyName = $"试样{i}_3",
|
|||
|
|
Width = 100,
|
|||
|
|
ReadOnly = false,
|
|||
|
|
DefaultCellStyle = (DataGridViewCellStyle)editableStyle.Clone()
|
|||
|
|
};
|
|||
|
|
dataGridView1.Columns.Add(col3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 绑定数据源
|
|||
|
|
dataGridView1.DataSource = sampleDataTable;
|
|||
|
|
|
|||
|
|
// 调试:输出列信息
|
|||
|
|
System.Diagnostics.Debug.WriteLine("=== DataGridView 列信息 ===");
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"DataTable 列数: {sampleDataTable.Columns.Count}");
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"DataGridView 列数: {dataGridView1.Columns.Count}");
|
|||
|
|
|
|||
|
|
for (int i = 0; i < dataGridView1.Columns.Count; i++)
|
|||
|
|
{
|
|||
|
|
var col = dataGridView1.Columns[i];
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"列{i}: Name={col.Name}, DataPropertyName={col.DataPropertyName}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加单元格格式化事件
|
|||
|
|
dataGridView1.CellFormatting += DataGridView1_CellFormatting;
|
|||
|
|
|
|||
|
|
// 添加单元格编辑事件(用于手动输入后重新计算)
|
|||
|
|
dataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
|
|||
|
|
|
|||
|
|
// 添加单元格编辑前事件(动态控制可编辑性)
|
|||
|
|
dataGridView1.CellBeginEdit += DataGridView1_CellBeginEdit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 单元格编辑前事件 - 动态控制哪些单元格可以编辑
|
|||
|
|
/// </summary>
|
|||
|
|
private void DataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (e.RowIndex >= 0 && e.ColumnIndex > 0) // 跳过序号列
|
|||
|
|
{
|
|||
|
|
string rowName = dataGridView1.Rows[e.RowIndex].Cells["序号"].Value?.ToString() ?? "";
|
|||
|
|
|
|||
|
|
// 吸芯高度(mm)行 - 所有列都可以编辑
|
|||
|
|
if (rowName == "吸芯高度(mm)")
|
|||
|
|
{
|
|||
|
|
return; // 允许编辑
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 其他行 - 取消编辑
|
|||
|
|
e.Cancel = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 单元格格式化事件 - 处理数值显示格式
|
|||
|
|
/// </summary>
|
|||
|
|
private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (e.RowIndex >= 0 && e.Value != null && e.Value != DBNull.Value)
|
|||
|
|
{
|
|||
|
|
// 格式化数值列(保留2位小数)
|
|||
|
|
string columnName = dataGridView1.Columns[e.ColumnIndex].Name;
|
|||
|
|
if (columnName.Contains("试样") && columnName != "序号")
|
|||
|
|
{
|
|||
|
|
if (double.TryParse(e.Value.ToString(), out double value))
|
|||
|
|
{
|
|||
|
|
// 如果值为0,根据行和列决定是否显示
|
|||
|
|
if (value == 0)
|
|||
|
|
{
|
|||
|
|
string rowName = dataGridView1.Rows[e.RowIndex].Cells["序号"].Value?.ToString() ?? "";
|
|||
|
|
|
|||
|
|
// 根据示意图,某些单元格应该显示为空白
|
|||
|
|
if (ShouldBeEmpty(rowName, columnName))
|
|||
|
|
{
|
|||
|
|
e.Value = "";
|
|||
|
|
e.FormattingApplied = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
e.Value = value.ToString("F2");
|
|||
|
|
e.FormattingApplied = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 判断单元格是否应该为空白
|
|||
|
|
/// </summary>
|
|||
|
|
private bool ShouldBeEmpty(string rowName, string columnName)
|
|||
|
|
{
|
|||
|
|
// 吸水时间(s) - 只有试样次数1有数据
|
|||
|
|
if (rowName == "吸水时间(s)")
|
|||
|
|
{
|
|||
|
|
return columnName.EndsWith("_2") || columnName.EndsWith("_3");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 吸芯高度(mm) - 所有列都有数据(全部可编辑)
|
|||
|
|
if (rowName == "吸芯高度(mm)")
|
|||
|
|
{
|
|||
|
|
return false; // 不应该为空
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 芯吸速率(mm/min) - 只有试样次数3有数据
|
|||
|
|
if (rowName == "芯吸速率(mm/min)")
|
|||
|
|
{
|
|||
|
|
return columnName.EndsWith("_1") || columnName.EndsWith("_2");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 平均芯吸速率(mm/min) - 只有试样次数3有数据
|
|||
|
|
if (rowName == "平均芯吸速率(mm/min)")
|
|||
|
|
{
|
|||
|
|
return columnName.EndsWith("_1") || columnName.EndsWith("_2");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 标准偏差 - 只有试样次数3有数据
|
|||
|
|
if (rowName == "标准偏差")
|
|||
|
|
{
|
|||
|
|
return columnName.EndsWith("_1") || columnName.EndsWith("_2");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 单元格值改变事件 - 手动输入后重新计算
|
|||
|
|
/// </summary>
|
|||
|
|
private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
|
|||
|
|
{
|
|||
|
|
if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
|
|||
|
|
{
|
|||
|
|
string rowName = dataGridView1.Rows[e.RowIndex].Cells["序号"].Value?.ToString() ?? "";
|
|||
|
|
|
|||
|
|
// 如果是吸芯高度行,任何列的修改都触发重新计算
|
|||
|
|
if (rowName == "吸芯高度(mm)")
|
|||
|
|
{
|
|||
|
|
RecalculateRow(e.RowIndex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化事件处理器
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializeEventHandlers()
|
|||
|
|
{
|
|||
|
|
// 连接设备按钮
|
|||
|
|
button1.Click += Button1_Click;
|
|||
|
|
|
|||
|
|
// 打印按钮
|
|||
|
|
button2.Click += Button2_Click;
|
|||
|
|
|
|||
|
|
// 导出按钮
|
|||
|
|
button3.Click += Button3_Click;
|
|||
|
|
|
|||
|
|
// 返回按钮
|
|||
|
|
button4.Click += Button4_Click;
|
|||
|
|
|
|||
|
|
// 生成模拟数据按钮
|
|||
|
|
button5.Click += Button5_Click;
|
|||
|
|
|
|||
|
|
// 更新日期时间标签
|
|||
|
|
System.Windows.Forms.Timer clockTimer = new System.Windows.Forms.Timer();
|
|||
|
|
clockTimer.Interval = 1000;
|
|||
|
|
clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|||
|
|
clockTimer.Start();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 连接设备按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void Button1_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
if (dataTimer.Enabled)
|
|||
|
|
{
|
|||
|
|
StopDataCollection();
|
|||
|
|
button1.Text = "🔗 连接设备";
|
|||
|
|
button1.BackColor = Color.FromArgb(46, 204, 113);
|
|||
|
|
MessageBox.Show("已停止数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
StartDataCollection();
|
|||
|
|
button1.Text = "⏸️ 停止采集";
|
|||
|
|
button1.BackColor = Color.FromArgb(231, 76, 60);
|
|||
|
|
MessageBox.Show("开始数据采集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 打印按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void Button2_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show("打印功能开发中...", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 导出按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void Button3_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
SaveFileDialog saveFileDialog = new SaveFileDialog
|
|||
|
|
{
|
|||
|
|
Filter = "Excel 文件 (*.xlsx)|*.xlsx|Excel 97-2003 (*.xls)|*.xls",
|
|||
|
|
FileName = $"液体吸收测试报告_{DateTime.Now:yyyyMMdd_HHmmss}",
|
|||
|
|
Title = "导出数据"
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
|||
|
|
{
|
|||
|
|
GenerateReport(saveFileDialog.FileName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 返回按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void Button4_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
this.Close();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 生成模拟数据按钮点击事件
|
|||
|
|
/// </summary>
|
|||
|
|
private void Button5_Click(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
GenerateMockData();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化数据表结构 - 包含所有必要的列
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializeDataTable()
|
|||
|
|
{
|
|||
|
|
sampleDataTable = new DataTable();
|
|||
|
|
|
|||
|
|
// 序号列
|
|||
|
|
sampleDataTable.Columns.Add("序号", typeof(string));
|
|||
|
|
|
|||
|
|
// 为每个试样添加3列(试样次数:1、2、3)
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
sampleDataTable.Columns.Add($"试样{i}_1", typeof(double)); // 系统读数
|
|||
|
|
sampleDataTable.Columns.Add($"试样{i}_2", typeof(double)); // 手动输入
|
|||
|
|
sampleDataTable.Columns.Add($"试样{i}_3", typeof(double)); // 系统计算
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化6行数据
|
|||
|
|
AddDataRow("吸水时间(s)");
|
|||
|
|
AddDataRow("吸芯高度(mm)");
|
|||
|
|
AddDataRow("芯吸速率(mm/min)");
|
|||
|
|
AddDataRow("平均芯吸速率(mm/min)");
|
|||
|
|
AddDataRow("标准偏差");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 添加数据行
|
|||
|
|
/// </summary>
|
|||
|
|
private void AddDataRow(string rowName)
|
|||
|
|
{
|
|||
|
|
DataRow row = sampleDataTable.NewRow();
|
|||
|
|
row["序号"] = rowName;
|
|||
|
|
|
|||
|
|
// 初始化所有数值列为0
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
row[$"试样{i}_1"] = 0.0;
|
|||
|
|
row[$"试样{i}_2"] = 0.0;
|
|||
|
|
row[$"试样{i}_3"] = 0.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sampleDataTable.Rows.Add(row);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化定时器用于模拟寄存器数据读取
|
|||
|
|
/// </summary>
|
|||
|
|
private void InitializeTimer()
|
|||
|
|
{
|
|||
|
|
dataTimer = new System.Windows.Forms.Timer();
|
|||
|
|
dataTimer.Interval = 1000; // 每秒读取一次
|
|||
|
|
dataTimer.Tick += DataTimer_Tick;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 定时器事件 - 模拟从寄存器读取吸水时间数据
|
|||
|
|
/// </summary>
|
|||
|
|
private void DataTimer_Tick(object sender, EventArgs e)
|
|||
|
|
{
|
|||
|
|
// 读取吸水时间(s)- 从寄存器读取
|
|||
|
|
DataRow timeRow = sampleDataTable.Rows[0]; // 第一行:吸水时间
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
double registerValue = ReadRegisterData(i - 1);
|
|||
|
|
timeRow[$"试样{i}_1"] = registerValue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 触发界面更新
|
|||
|
|
UpdateDisplay();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 模拟从寄存器读取数据
|
|||
|
|
/// 实际应用中替换为真实的 Modbus 或其他协议读取
|
|||
|
|
/// </summary>
|
|||
|
|
private double ReadRegisterData(int registerAddress)
|
|||
|
|
{
|
|||
|
|
// 模拟数据:30-34 之间的随机值
|
|||
|
|
Random random = new Random(Guid.NewGuid().GetHashCode());
|
|||
|
|
return 30 + random.NextDouble() * 4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 生成模拟测试数据
|
|||
|
|
/// </summary>
|
|||
|
|
public void GenerateMockData()
|
|||
|
|
{
|
|||
|
|
Random random = new Random();
|
|||
|
|
|
|||
|
|
// 第1行:吸水时间(s)- 试样次数1(系统读数)
|
|||
|
|
DataRow timeRow = sampleDataTable.Rows[0];
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
double timeValue = Math.Round(30 + random.NextDouble() * 4, 2);
|
|||
|
|
timeRow[$"试样{i}_1"] = timeValue;
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"吸水时间 - 试样{i}_1 = {timeValue}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 第2行:吸芯高度(mm)- 所有列都可编辑(手动输入)
|
|||
|
|
DataRow heightRow = sampleDataTable.Rows[1];
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
double height1 = Math.Round(50 + random.NextDouble() * 20, 2);
|
|||
|
|
double height2 = Math.Round(50 + random.NextDouble() * 20, 2);
|
|||
|
|
double height3 = Math.Round(50 + random.NextDouble() * 20, 2);
|
|||
|
|
|
|||
|
|
heightRow[$"试样{i}_1"] = height1;
|
|||
|
|
heightRow[$"试样{i}_2"] = height2;
|
|||
|
|
heightRow[$"试样{i}_3"] = height3;
|
|||
|
|
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"吸芯高度 - 试样{i}_1 = {height1}");
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"吸芯高度 - 试样{i}_2 = {height2}");
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"吸芯高度 - 试样{i}_3 = {height3}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算所有行
|
|||
|
|
CalculateAllRows();
|
|||
|
|
|
|||
|
|
// 更新显示
|
|||
|
|
UpdateDisplay();
|
|||
|
|
|
|||
|
|
// 调试信息:验证数据
|
|||
|
|
System.Diagnostics.Debug.WriteLine("=== 模拟数据生成完成 ===");
|
|||
|
|
DataRow rateRow = sampleDataTable.Rows[2];
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"芯吸速率 - 试样{i}_3 = {rateRow[$"试样{i}_3"]}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MessageBox.Show("已生成模拟数据\n请查看输出窗口的调试信息", "模拟数据生成", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新界面显示
|
|||
|
|
/// </summary>
|
|||
|
|
private void UpdateDisplay()
|
|||
|
|
{
|
|||
|
|
// 刷新 DataGridView
|
|||
|
|
if (dataGridView1.DataSource != null)
|
|||
|
|
{
|
|||
|
|
((DataTable)dataGridView1.DataSource).AcceptChanges();
|
|||
|
|
dataGridView1.Refresh();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 计算所有行的数据
|
|||
|
|
/// </summary>
|
|||
|
|
private void CalculateAllRows()
|
|||
|
|
{
|
|||
|
|
// 第3行:芯吸速率(mm/min)= 吸芯高度 / (吸水时间 / 60)
|
|||
|
|
CalculateWickingRate();
|
|||
|
|
|
|||
|
|
// 第4行:平均芯吸速率(mm/min)
|
|||
|
|
CalculateAverageWickingRate();
|
|||
|
|
|
|||
|
|
// 第5行:标准偏差
|
|||
|
|
CalculateStandardDeviation();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 重新计算指定行
|
|||
|
|
/// </summary>
|
|||
|
|
private void RecalculateRow(int rowIndex)
|
|||
|
|
{
|
|||
|
|
CalculateAllRows();
|
|||
|
|
UpdateDisplay();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 计算芯吸速率(mm/min)
|
|||
|
|
/// 公式:芯吸速率 = 吸芯高度(mm) / (吸水时间(s) / 60)
|
|||
|
|
/// </summary>
|
|||
|
|
private void CalculateWickingRate()
|
|||
|
|
{
|
|||
|
|
DataRow timeRow = sampleDataTable.Rows[0]; // 吸水时间
|
|||
|
|
DataRow heightRow = sampleDataTable.Rows[1]; // 吸芯高度
|
|||
|
|
DataRow rateRow = sampleDataTable.Rows[2]; // 芯吸速率
|
|||
|
|
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
// 获取吸水时间(试样次数1:系统读数)
|
|||
|
|
double time = ConvertToDouble(timeRow[$"试样{i}_1"]);
|
|||
|
|
|
|||
|
|
// 获取吸芯高度(优先使用对应列的数据)
|
|||
|
|
// 试样次数1 → 使用吸芯高度_1
|
|||
|
|
// 试样次数2 → 使用吸芯高度_2
|
|||
|
|
// 试样次数3 → 使用吸芯高度_3
|
|||
|
|
double height1 = ConvertToDouble(heightRow[$"试样{i}_1"]);
|
|||
|
|
double height2 = ConvertToDouble(heightRow[$"试样{i}_2"]);
|
|||
|
|
double height3 = ConvertToDouble(heightRow[$"试样{i}_3"]);
|
|||
|
|
|
|||
|
|
// 计算芯吸速率(使用对应的吸芯高度)
|
|||
|
|
double rate = 0;
|
|||
|
|
if (time > 0)
|
|||
|
|
{
|
|||
|
|
// 使用平均吸芯高度或选择非零值
|
|||
|
|
double avgHeight = 0;
|
|||
|
|
int count = 0;
|
|||
|
|
if (height1 > 0) { avgHeight += height1; count++; }
|
|||
|
|
if (height2 > 0) { avgHeight += height2; count++; }
|
|||
|
|
if (height3 > 0) { avgHeight += height3; count++; }
|
|||
|
|
|
|||
|
|
if (count > 0)
|
|||
|
|
{
|
|||
|
|
avgHeight /= count;
|
|||
|
|
rate = avgHeight / (time / 60.0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 存储到试样次数3(系统计算)
|
|||
|
|
rateRow[$"试样{i}_3"] = Math.Round(rate, 2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 安全地将对象转换为double,处理DBNull和null情况
|
|||
|
|
/// </summary>
|
|||
|
|
private double ConvertToDouble(object value)
|
|||
|
|
{
|
|||
|
|
if (value == null || value == DBNull.Value)
|
|||
|
|
{
|
|||
|
|
return 0.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (double.TryParse(value.ToString(), out double result))
|
|||
|
|
{
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return 0.0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 计算平均芯吸速率(mm/min)
|
|||
|
|
/// </summary>
|
|||
|
|
private void CalculateAverageWickingRate()
|
|||
|
|
{
|
|||
|
|
DataRow rateRow = sampleDataTable.Rows[2]; // 芯吸速率
|
|||
|
|
DataRow avgRow = sampleDataTable.Rows[3]; // 平均芯吸速率
|
|||
|
|
|
|||
|
|
List<double> rates = new List<double>();
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
double rate = ConvertToDouble(rateRow[$"试样{i}_3"]);
|
|||
|
|
if (rate > 0)
|
|||
|
|
{
|
|||
|
|
rates.Add(rate);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double average = rates.Count > 0 ? rates.Average() : 0;
|
|||
|
|
|
|||
|
|
// 所有试样显示相同的平均值(存储在试样次数3)
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
avgRow[$"试样{i}_3"] = Math.Round(average, 2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 计算标准偏差
|
|||
|
|
/// </summary>
|
|||
|
|
private void CalculateStandardDeviation()
|
|||
|
|
{
|
|||
|
|
DataRow rateRow = sampleDataTable.Rows[2]; // 芯吸速率
|
|||
|
|
DataRow stdRow = sampleDataTable.Rows[4]; // 标准偏差
|
|||
|
|
|
|||
|
|
List<double> rates = new List<double>();
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
double rate = ConvertToDouble(rateRow[$"试样{i}_3"]);
|
|||
|
|
if (rate > 0)
|
|||
|
|
{
|
|||
|
|
rates.Add(rate);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double stdDev = 0;
|
|||
|
|
if (rates.Count > 1)
|
|||
|
|
{
|
|||
|
|
double average = rates.Average();
|
|||
|
|
double sumOfSquares = rates.Sum(r => Math.Pow(r - average, 2));
|
|||
|
|
stdDev = Math.Sqrt(sumOfSquares / (rates.Count - 1));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 所有试样显示相同的标准偏差(存储在试样次数3)
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
stdRow[$"试样{i}_3"] = Math.Round(stdDev, 2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 启动数据采集
|
|||
|
|
/// </summary>
|
|||
|
|
public void StartDataCollection()
|
|||
|
|
{
|
|||
|
|
dataTimer.Start();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 停止数据采集
|
|||
|
|
/// </summary>
|
|||
|
|
public void StopDataCollection()
|
|||
|
|
{
|
|||
|
|
dataTimer.Stop();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 导出数据到 Excel (.xlsx)
|
|||
|
|
/// </summary>
|
|||
|
|
public void ExportToExcel(string filePath)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
IWorkbook workbook;
|
|||
|
|
|
|||
|
|
// 根据文件扩展名选择格式
|
|||
|
|
if (filePath.EndsWith(".xlsx"))
|
|||
|
|
{
|
|||
|
|
workbook = new XSSFWorkbook(); // Excel 2007+
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
workbook = new HSSFWorkbook(); // Excel 97-2003
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ISheet sheet = workbook.CreateSheet("液体吸收测试报表");
|
|||
|
|
|
|||
|
|
// 创建样式
|
|||
|
|
ICellStyle headerStyle = workbook.CreateCellStyle();
|
|||
|
|
headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index;
|
|||
|
|
headerStyle.FillPattern = FillPattern.SolidForeground;
|
|||
|
|
headerStyle.BorderBottom = NPOIBorderStyle.Thin;
|
|||
|
|
headerStyle.BorderTop = NPOIBorderStyle.Thin;
|
|||
|
|
headerStyle.BorderLeft = NPOIBorderStyle.Thin;
|
|||
|
|
headerStyle.BorderRight = NPOIBorderStyle.Thin;
|
|||
|
|
headerStyle.Alignment = NPOIHorizontalAlignment.Center;
|
|||
|
|
headerStyle.VerticalAlignment = VerticalAlignment.Center;
|
|||
|
|
IFont headerFont = workbook.CreateFont();
|
|||
|
|
headerFont.IsBold = true;
|
|||
|
|
headerStyle.SetFont(headerFont);
|
|||
|
|
|
|||
|
|
// 白色背景样式(偶数行)
|
|||
|
|
ICellStyle whiteStyle = workbook.CreateCellStyle();
|
|||
|
|
whiteStyle.BorderBottom = NPOIBorderStyle.Thin;
|
|||
|
|
whiteStyle.BorderTop = NPOIBorderStyle.Thin;
|
|||
|
|
whiteStyle.BorderLeft = NPOIBorderStyle.Thin;
|
|||
|
|
whiteStyle.BorderRight = NPOIBorderStyle.Thin;
|
|||
|
|
whiteStyle.Alignment = NPOIHorizontalAlignment.Center;
|
|||
|
|
whiteStyle.VerticalAlignment = VerticalAlignment.Center;
|
|||
|
|
|
|||
|
|
// 黄色背景样式(奇数行)
|
|||
|
|
ICellStyle yellowStyle = workbook.CreateCellStyle();
|
|||
|
|
yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.LightYellow.Index;
|
|||
|
|
yellowStyle.FillPattern = FillPattern.SolidForeground;
|
|||
|
|
yellowStyle.BorderBottom = NPOIBorderStyle.Thin;
|
|||
|
|
yellowStyle.BorderTop = NPOIBorderStyle.Thin;
|
|||
|
|
yellowStyle.BorderLeft = NPOIBorderStyle.Thin;
|
|||
|
|
yellowStyle.BorderRight = NPOIBorderStyle.Thin;
|
|||
|
|
yellowStyle.Alignment = NPOIHorizontalAlignment.Center;
|
|||
|
|
yellowStyle.VerticalAlignment = VerticalAlignment.Center;
|
|||
|
|
|
|||
|
|
// 创建第一行表头(试样1-5)
|
|||
|
|
IRow headerRow1 = sheet.CreateRow(0);
|
|||
|
|
headerRow1.Height = 400; // 设置行高(单位:1/20点)
|
|||
|
|
|
|||
|
|
ICell cell0 = headerRow1.CreateCell(0);
|
|||
|
|
cell0.SetCellValue("序号");
|
|||
|
|
cell0.CellStyle = headerStyle;
|
|||
|
|
sheet.AddMergedRegion(new CellRangeAddress(0, 1, 0, 0)); // 合并序号列
|
|||
|
|
|
|||
|
|
int colIndex = 1;
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
ICell cell = headerRow1.CreateCell(colIndex);
|
|||
|
|
cell.SetCellValue($"试样{i}");
|
|||
|
|
cell.CellStyle = headerStyle;
|
|||
|
|
sheet.AddMergedRegion(new CellRangeAddress(0, 0, colIndex, colIndex + 2));
|
|||
|
|
colIndex += 3;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建第二行表头(试样次数:1、2、3)
|
|||
|
|
IRow headerRow2 = sheet.CreateRow(1);
|
|||
|
|
headerRow2.Height = 400; // 设置行高
|
|||
|
|
|
|||
|
|
colIndex = 1;
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
ICell cell1 = headerRow2.CreateCell(colIndex++);
|
|||
|
|
cell1.SetCellValue("1");
|
|||
|
|
cell1.CellStyle = headerStyle;
|
|||
|
|
|
|||
|
|
ICell cell2 = headerRow2.CreateCell(colIndex++);
|
|||
|
|
cell2.SetCellValue("2");
|
|||
|
|
cell2.CellStyle = headerStyle;
|
|||
|
|
|
|||
|
|
ICell cell3 = headerRow2.CreateCell(colIndex++);
|
|||
|
|
cell3.SetCellValue("3");
|
|||
|
|
cell3.CellStyle = headerStyle;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 填充数据
|
|||
|
|
int rowIndex = 2;
|
|||
|
|
int dataRowIndex = 0;
|
|||
|
|
foreach (DataRow dataRow in sampleDataTable.Rows)
|
|||
|
|
{
|
|||
|
|
IRow row = sheet.CreateRow(rowIndex);
|
|||
|
|
row.Height = 380; // 设置数据行高
|
|||
|
|
|
|||
|
|
// 根据行索引选择样式(隔行变色)
|
|||
|
|
ICellStyle rowStyle = (dataRowIndex % 2 == 0) ? whiteStyle : yellowStyle;
|
|||
|
|
|
|||
|
|
// 序号列
|
|||
|
|
ICell cellSeq = row.CreateCell(0);
|
|||
|
|
cellSeq.SetCellValue(dataRow["序号"].ToString());
|
|||
|
|
cellSeq.CellStyle = rowStyle;
|
|||
|
|
|
|||
|
|
colIndex = 1;
|
|||
|
|
for (int i = 1; i <= 5; i++)
|
|||
|
|
{
|
|||
|
|
// 试样次数1
|
|||
|
|
ICell cell1 = row.CreateCell(colIndex++);
|
|||
|
|
double val1 = ConvertToDouble(dataRow[$"试样{i}_1"]);
|
|||
|
|
if (val1 != 0)
|
|||
|
|
{
|
|||
|
|
cell1.SetCellValue(val1);
|
|||
|
|
}
|
|||
|
|
cell1.CellStyle = rowStyle;
|
|||
|
|
|
|||
|
|
// 试样次数2
|
|||
|
|
ICell cell2 = row.CreateCell(colIndex++);
|
|||
|
|
double val2 = ConvertToDouble(dataRow[$"试样{i}_2"]);
|
|||
|
|
if (val2 != 0)
|
|||
|
|
{
|
|||
|
|
cell2.SetCellValue(val2);
|
|||
|
|
}
|
|||
|
|
cell2.CellStyle = rowStyle;
|
|||
|
|
|
|||
|
|
// 试样次数3
|
|||
|
|
ICell cell3 = row.CreateCell(colIndex++);
|
|||
|
|
double val3 = ConvertToDouble(dataRow[$"试样{i}_3"]);
|
|||
|
|
if (val3 != 0)
|
|||
|
|
{
|
|||
|
|
cell3.SetCellValue(val3);
|
|||
|
|
}
|
|||
|
|
cell3.CellStyle = rowStyle;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
rowIndex++;
|
|||
|
|
dataRowIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 手动设置列宽(单位:1/256字符宽度)
|
|||
|
|
sheet.SetColumnWidth(0, 20 * 256); // 序号列:20个字符宽度
|
|||
|
|
for (int i = 1; i <= 15; i++)
|
|||
|
|
{
|
|||
|
|
sheet.SetColumnWidth(i, 12 * 256); // 数据列:12个字符宽度
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保存文件
|
|||
|
|
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
|
|||
|
|
{
|
|||
|
|
workbook.Write(fs);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MessageBox.Show($"数据已成功导出到:{filePath}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 生成报表(带格式的 Excel)
|
|||
|
|
/// </summary>
|
|||
|
|
public void GenerateReport(string filePath)
|
|||
|
|
{
|
|||
|
|
// 使用 ExportToExcel 方法
|
|||
|
|
ExportToExcel(filePath);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清空数据
|
|||
|
|
/// </summary>
|
|||
|
|
public void ClearData()
|
|||
|
|
{
|
|||
|
|
// 重新初始化数据表
|
|||
|
|
sampleDataTable.Clear();
|
|||
|
|
InitializeDataTable();
|
|||
|
|
UpdateDisplay();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|