1293 lines
48 KiB
C#
1293 lines
48 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
|
||
{
|
||
#region 常量定义
|
||
private const int TIMER_INTERVAL = 1000;
|
||
private const string DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||
private const string ROW_WICKING_TIME = "吸水时间(s)";
|
||
private const string ROW_WICKING_HEIGHT = "吸芯高度(mm)";
|
||
private const string ROW_WICKING_RATE = "芯吸速率(mm/min)";
|
||
private const string ROW_AVG_WICKING_RATE = "平均芯吸速率(mm/min)";
|
||
private const string ROW_STD_DEVIATION = "标准偏差";
|
||
#endregion
|
||
|
||
#region 字段
|
||
private System.Windows.Forms.Timer dataTimer;
|
||
private System.Windows.Forms.Timer clockTimer;
|
||
private DataTable sampleDataTable;
|
||
private int currentSampleCount = 5; // 当前试样数量,固定为5(最多5组,共15次测试)
|
||
private readonly Random random = new Random();
|
||
private bool isVerticalLayout = true; // true=纵向(当前),false=横向
|
||
private const int MAX_SAMPLE_COUNT = 5; // 最大试样数量限制为5
|
||
#endregion
|
||
|
||
public Form3()
|
||
{
|
||
InitializeComponent();
|
||
InitializeDataTable();
|
||
InitializeTimer();
|
||
InitializeDataGridView();
|
||
InitializeEventHandlers();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置试样数量(1-5)
|
||
/// Form3限定为最多5组试样,每组3次测试,共15次测试数据
|
||
/// </summary>
|
||
public void SetSampleCount(int count)
|
||
{
|
||
if (count < 1 || count > MAX_SAMPLE_COUNT)
|
||
{
|
||
ShowMessage($"试样数量必须在1-{MAX_SAMPLE_COUNT}之间\n" +
|
||
$"Form3限定为最多{MAX_SAMPLE_COUNT}组试样(共{MAX_SAMPLE_COUNT * 3}次测试)",
|
||
"参数错误", MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
currentSampleCount = count;
|
||
|
||
// 重新初始化数据表和界面
|
||
sampleDataTable.Clear();
|
||
InitializeDataTable();
|
||
InitializeDataGridView();
|
||
UpdateDisplay();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化 DataGridView 列 - 实现2级表头(纵向)或普通表头(横向)
|
||
/// </summary>
|
||
private void InitializeDataGridView()
|
||
{
|
||
dataGridView1.SuspendLayout();
|
||
try
|
||
{
|
||
dataGridView1.AutoGenerateColumns = false;
|
||
dataGridView1.DataSource = null;
|
||
dataGridView1.Columns.Clear();
|
||
|
||
// 移除可能存在的事件处理器
|
||
dataGridView1.CellFormatting -= DataGridView1_CellFormatting;
|
||
dataGridView1.CellValueChanged -= DataGridView1_CellValueChanged;
|
||
dataGridView1.CellBeginEdit -= DataGridView1_CellBeginEdit;
|
||
|
||
// 创建共享样式(不设置背景色,让 AlternatingRowsDefaultCellStyle 生效)
|
||
DataGridViewCellStyle centerStyle = new DataGridViewCellStyle
|
||
{
|
||
Alignment = DataGridViewContentAlignment.MiddleCenter
|
||
};
|
||
|
||
if (isVerticalLayout)
|
||
{
|
||
// 纵向布局:2级表头
|
||
InitializeVerticalLayout(centerStyle);
|
||
}
|
||
else
|
||
{
|
||
// 横向布局:普通表头
|
||
InitializeHorizontalLayout(centerStyle);
|
||
}
|
||
|
||
// 绑定数据源
|
||
dataGridView1.DataSource = sampleDataTable;
|
||
dataGridView1.CellFormatting += DataGridView1_CellFormatting;
|
||
dataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
|
||
dataGridView1.CellBeginEdit += DataGridView1_CellBeginEdit;
|
||
}
|
||
finally
|
||
{
|
||
dataGridView1.ResumeLayout();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化纵向布局(当前默认布局)
|
||
/// </summary>
|
||
private void InitializeVerticalLayout(DataGridViewCellStyle centerStyle)
|
||
{
|
||
// 序号列
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = "序号",
|
||
HeaderText = "序号",
|
||
DataPropertyName = "序号",
|
||
Width = 180,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = centerStyle
|
||
});
|
||
|
||
// 为每个试样添加3列(2级表头:试样N + 子列1/2/3)
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = $"试样{i}_1",
|
||
HeaderText = $"试样{i}\n1",
|
||
DataPropertyName = $"试样{i}_1",
|
||
Width = 100,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = $"试样{i}_2",
|
||
HeaderText = $"试样{i}\n2",
|
||
DataPropertyName = $"试样{i}_2",
|
||
Width = 100,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = $"试样{i}_3",
|
||
HeaderText = $"试样{i}\n3",
|
||
DataPropertyName = $"试样{i}_3",
|
||
Width = 100,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化横向布局
|
||
/// </summary>
|
||
private void InitializeHorizontalLayout(DataGridViewCellStyle centerStyle)
|
||
{
|
||
// 序号列
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = "序号",
|
||
HeaderText = "序号",
|
||
DataPropertyName = "序号",
|
||
Width = 120,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = centerStyle
|
||
});
|
||
|
||
// 数据项列
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = ROW_WICKING_TIME,
|
||
HeaderText = ROW_WICKING_TIME,
|
||
DataPropertyName = ROW_WICKING_TIME,
|
||
Width = 120,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = ROW_WICKING_HEIGHT,
|
||
HeaderText = ROW_WICKING_HEIGHT,
|
||
DataPropertyName = ROW_WICKING_HEIGHT,
|
||
Width = 120,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = ROW_WICKING_RATE,
|
||
HeaderText = ROW_WICKING_RATE,
|
||
DataPropertyName = ROW_WICKING_RATE,
|
||
Width = 140,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = ROW_AVG_WICKING_RATE,
|
||
HeaderText = ROW_AVG_WICKING_RATE,
|
||
DataPropertyName = ROW_AVG_WICKING_RATE,
|
||
Width = 160,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = ROW_STD_DEVIATION,
|
||
HeaderText = ROW_STD_DEVIATION,
|
||
DataPropertyName = ROW_STD_DEVIATION,
|
||
Width = 120,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = (DataGridViewCellStyle)centerStyle.Clone()
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单元格编辑前事件 - 动态控制哪些单元格可以编辑
|
||
/// </summary>
|
||
private void DataGridView1_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
|
||
{
|
||
// 所有单元格都不可编辑(吸芯高度从PLC自动读取)
|
||
e.Cancel = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单元格格式化事件 - 处理数值显示格式
|
||
/// </summary>
|
||
private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
|
||
{
|
||
if (e.RowIndex < 0 || e.ColumnIndex == 0) return; // 跳过序号列
|
||
|
||
// 获取当前行的序号,判断是否是标准偏差行
|
||
string rowName = "";
|
||
if (e.RowIndex < dataGridView1.Rows.Count)
|
||
{
|
||
var row = dataGridView1.Rows[e.RowIndex];
|
||
if (row.Cells["序号"].Value != null)
|
||
{
|
||
rowName = row.Cells["序号"].Value.ToString();
|
||
}
|
||
}
|
||
|
||
// 格式化数值显示为2位小数
|
||
if (e.Value != null && e.Value != DBNull.Value)
|
||
{
|
||
if (double.TryParse(e.Value.ToString(), out double numValue))
|
||
{
|
||
// 标准偏差行:即使为0也要显示
|
||
if (rowName == ROW_STD_DEVIATION)
|
||
{
|
||
e.Value = numValue.ToString("F2");
|
||
e.FormattingApplied = true;
|
||
return;
|
||
}
|
||
|
||
// 其他行:如果值为0或接近0,显示为空白
|
||
if (Math.Abs(numValue) < 0.001)
|
||
{
|
||
e.Value = "";
|
||
e.FormattingApplied = true;
|
||
return;
|
||
}
|
||
|
||
// 显示2位小数
|
||
e.Value = numValue.ToString("F2");
|
||
e.FormattingApplied = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// null 或 DBNull 显示为空白
|
||
e.Value = "";
|
||
e.FormattingApplied = true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单元格值改变事件 - 手动输入后重新计算
|
||
/// 注意:由于所有单元格都不可编辑,此事件不会被触发
|
||
/// </summary>
|
||
private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
|
||
{
|
||
// 所有单元格都不可编辑,此事件不会被触发
|
||
// 保留此方法以防将来需要启用编辑功能
|
||
}
|
||
|
||
/// <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;
|
||
|
||
clockTimer = new System.Windows.Forms.Timer
|
||
{
|
||
Interval = TIMER_INTERVAL
|
||
};
|
||
clockTimer.Tick += (s, e) => label2.Text = DateTime.Now.ToString(DATE_TIME_FORMAT);
|
||
clockTimer.Start();
|
||
|
||
// 不再在 Form3 内部创建切换按钮,改为在 MainForm 中创建
|
||
// CreateToggleButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建切换按钮
|
||
/// </summary>
|
||
private void CreateToggleButton()
|
||
{
|
||
Button toggleButton = new Button
|
||
{
|
||
Name = "buttonToggle",
|
||
Text = "🔄 横向",
|
||
Width = 100,
|
||
Height = 30,
|
||
BackColor = Color.FromArgb(52, 152, 219),
|
||
ForeColor = Color.White,
|
||
FlatStyle = FlatStyle.Flat,
|
||
Font = new Font("微软雅黑", 9F, FontStyle.Bold),
|
||
Cursor = Cursors.Hand
|
||
};
|
||
|
||
toggleButton.FlatAppearance.BorderSize = 0;
|
||
toggleButton.Click += (s, e) =>
|
||
{
|
||
ToggleTableLayout();
|
||
toggleButton.Text = isVerticalLayout ? "🔄 横向" : "🔄 纵向";
|
||
};
|
||
|
||
// 将按钮添加到 panel2
|
||
if (this.Controls.Find("tableLayoutPanel1", true).FirstOrDefault() is TableLayoutPanel tlp1)
|
||
{
|
||
if (tlp1.Controls.Find("tableLayoutPanel2", true).FirstOrDefault() is TableLayoutPanel tlp2)
|
||
{
|
||
if (tlp2.Controls.Find("panel2", true).FirstOrDefault() is Panel panel2)
|
||
{
|
||
toggleButton.Location = new Point(panel2.Width - 120, 5);
|
||
toggleButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
|
||
panel2.Controls.Add(toggleButton);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换表格方向(纵向/横向)
|
||
/// </summary>
|
||
public void ToggleTableLayout()
|
||
{
|
||
isVerticalLayout = !isVerticalLayout;
|
||
|
||
// 保存当前数据
|
||
var currentData = SaveCurrentData();
|
||
|
||
// 重新初始化表格
|
||
InitializeDataTable();
|
||
InitializeDataGridView();
|
||
|
||
// 恢复数据
|
||
RestoreData(currentData);
|
||
|
||
// 更新显示
|
||
RefreshDataGridView();
|
||
|
||
ShowMessage($"已切换到{(isVerticalLayout ? "纵向" : "横向")}布局", "布局切换", MessageBoxIcon.Information);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前布局方向
|
||
/// </summary>
|
||
public bool IsVerticalLayout()
|
||
{
|
||
return isVerticalLayout;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存当前数据
|
||
/// </summary>
|
||
private Dictionary<string, Dictionary<string, double>> SaveCurrentData()
|
||
{
|
||
var data = new Dictionary<string, Dictionary<string, double>>();
|
||
|
||
if (sampleDataTable == null || sampleDataTable.Rows.Count == 0)
|
||
return data;
|
||
|
||
foreach (DataRow row in sampleDataTable.Rows)
|
||
{
|
||
string rowName = row["序号"]?.ToString() ?? "";
|
||
if (string.IsNullOrEmpty(rowName)) continue;
|
||
|
||
var rowData = new Dictionary<string, double>();
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
for (int j = 1; j <= 3; j++)
|
||
{
|
||
string colName = $"试样{i}_{j}";
|
||
if (row.Table.Columns.Contains(colName))
|
||
{
|
||
rowData[colName] = ConvertToDouble(row[colName]);
|
||
}
|
||
}
|
||
}
|
||
data[rowName] = rowData;
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 恢复数据
|
||
/// </summary>
|
||
private void RestoreData(Dictionary<string, Dictionary<string, double>> data)
|
||
{
|
||
if (data == null || data.Count == 0 || sampleDataTable == null)
|
||
return;
|
||
|
||
foreach (DataRow row in sampleDataTable.Rows)
|
||
{
|
||
string rowName = row["序号"]?.ToString() ?? "";
|
||
if (string.IsNullOrEmpty(rowName) || !data.ContainsKey(rowName))
|
||
continue;
|
||
|
||
var rowData = data[rowName];
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
for (int j = 1; j <= 3; j++)
|
||
{
|
||
string colName = $"试样{i}_{j}";
|
||
if (row.Table.Columns.Contains(colName) && rowData.ContainsKey(colName))
|
||
{
|
||
row[colName] = rowData[colName];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连接设备按钮点击事件
|
||
/// </summary>
|
||
private void Button1_Click(object sender, EventArgs e)
|
||
{
|
||
if (dataTimer.Enabled)
|
||
{
|
||
StopDataCollection();
|
||
UpdateButtonState(button1, "🔗 连接设备", Color.FromArgb(46, 204, 113));
|
||
ShowMessage("已停止数据采集");
|
||
}
|
||
else
|
||
{
|
||
StartDataCollection();
|
||
UpdateButtonState(button1, "⏸️ 停止采集", Color.FromArgb(231, 76, 60));
|
||
ShowMessage("开始数据采集");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打印按钮点击事件
|
||
/// </summary>
|
||
private void Button2_Click(object sender, EventArgs e)
|
||
{
|
||
ShowMessage("打印功能开发中...");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新按钮状态
|
||
/// </summary>
|
||
private void UpdateButtonState(Button button, string text, Color backColor)
|
||
{
|
||
button.Text = text;
|
||
button.BackColor = backColor;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示提示消息
|
||
/// </summary>
|
||
private void ShowMessage(string message, string title = "提示", MessageBoxIcon icon = MessageBoxIcon.Information)
|
||
{
|
||
MessageBox.Show(message, title, MessageBoxButtons.OK, icon);
|
||
}
|
||
|
||
/// <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();
|
||
|
||
if (isVerticalLayout)
|
||
{
|
||
// 纵向布局:序号列 + 试样列
|
||
sampleDataTable.Columns.Add("序号", typeof(string));
|
||
|
||
// 为每个试样添加3列(试样次数:1、2、3)
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
sampleDataTable.Columns.Add($"试样{i}_1", typeof(double));
|
||
sampleDataTable.Columns.Add($"试样{i}_2", typeof(double));
|
||
sampleDataTable.Columns.Add($"试样{i}_3", typeof(double));
|
||
}
|
||
|
||
// 初始化5行数据
|
||
AddDataRow(ROW_WICKING_TIME);
|
||
AddDataRow(ROW_WICKING_HEIGHT);
|
||
AddDataRow(ROW_WICKING_RATE);
|
||
AddDataRow(ROW_AVG_WICKING_RATE);
|
||
AddDataRow(ROW_STD_DEVIATION);
|
||
}
|
||
else
|
||
{
|
||
// 横向布局:序号列 + 数据项列
|
||
sampleDataTable.Columns.Add("序号", typeof(string));
|
||
sampleDataTable.Columns.Add(ROW_WICKING_TIME, typeof(double));
|
||
sampleDataTable.Columns.Add(ROW_WICKING_HEIGHT, typeof(double));
|
||
sampleDataTable.Columns.Add(ROW_WICKING_RATE, typeof(double));
|
||
sampleDataTable.Columns.Add(ROW_AVG_WICKING_RATE, typeof(double));
|
||
sampleDataTable.Columns.Add(ROW_STD_DEVIATION, typeof(double));
|
||
|
||
// 为每个试样的每次测试添加一行
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
for (int j = 1; j <= 3; j++)
|
||
{
|
||
DataRow row = sampleDataTable.NewRow();
|
||
row["序号"] = $"试样{i}_{j}";
|
||
row[ROW_WICKING_TIME] = 0.0;
|
||
row[ROW_WICKING_HEIGHT] = 0.0;
|
||
row[ROW_WICKING_RATE] = 0.0;
|
||
row[ROW_AVG_WICKING_RATE] = 0.0;
|
||
row[ROW_STD_DEVIATION] = 0.0;
|
||
sampleDataTable.Rows.Add(row);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加数据行
|
||
/// </summary>
|
||
private void AddDataRow(string rowName)
|
||
{
|
||
DataRow row = sampleDataTable.NewRow();
|
||
row["序号"] = rowName;
|
||
|
||
// 初始化所有数值列为0
|
||
for (int i = 1; i <= currentSampleCount; 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)
|
||
{
|
||
if (isVerticalLayout)
|
||
{
|
||
// 纵向布局:第1行读取吸水时间
|
||
DataRow timeRow = sampleDataTable.Rows[0];
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
double time1 = ReadRegisterData((i - 1) * 3);
|
||
double time2 = ReadRegisterData((i - 1) * 3 + 1);
|
||
double time3 = ReadRegisterData((i - 1) * 3 + 2);
|
||
|
||
timeRow[$"试样{i}_1"] = time1;
|
||
timeRow[$"试样{i}_2"] = time2;
|
||
timeRow[$"试样{i}_3"] = time3;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 横向布局:每行读取吸水时间
|
||
int rowIndex = 0;
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
for (int j = 1; j <= 3; j++)
|
||
{
|
||
if (rowIndex < sampleDataTable.Rows.Count)
|
||
{
|
||
double time = ReadRegisterData((i - 1) * 3 + (j - 1));
|
||
sampleDataTable.Rows[rowIndex][ROW_WICKING_TIME] = time;
|
||
rowIndex++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计算所有相关数据
|
||
CalculateAllRows();
|
||
UpdateDisplay();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 模拟从寄存器读取数据
|
||
/// </summary>
|
||
private double ReadRegisterData(int registerAddress)
|
||
{
|
||
// 吸水时间寄存器:30-34秒
|
||
return 30 + random.NextDouble() * 4;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成模拟测试数据
|
||
/// </summary>
|
||
public void GenerateMockData()
|
||
{
|
||
// 清空所有行的数据(保持表结构)
|
||
foreach (DataRow row in sampleDataTable.Rows)
|
||
{
|
||
for (int colIndex = 1; colIndex < sampleDataTable.Columns.Count; colIndex++)
|
||
{
|
||
row[colIndex] = 0.0;
|
||
}
|
||
}
|
||
|
||
if (isVerticalLayout)
|
||
{
|
||
// 纵向布局:第1行生成吸水时间
|
||
DataRow timeRow = sampleDataTable.Rows[0];
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
double baseTime = 31 + random.NextDouble() * 2;
|
||
double time1 = Math.Round(baseTime + (random.NextDouble() - 0.5) * 2, 2);
|
||
double time2 = Math.Round(baseTime + (random.NextDouble() - 0.5) * 2, 2);
|
||
double time3 = Math.Round(baseTime + (random.NextDouble() - 0.5) * 2, 2);
|
||
|
||
timeRow[$"试样{i}_1"] = time1;
|
||
timeRow[$"试样{i}_2"] = time2;
|
||
timeRow[$"试样{i}_3"] = time3;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 横向布局:每行生成吸水时间
|
||
int rowIndex = 0;
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
double baseTime = 31 + random.NextDouble() * 2;
|
||
for (int j = 1; j <= 3; j++)
|
||
{
|
||
if (rowIndex < sampleDataTable.Rows.Count)
|
||
{
|
||
double time = Math.Round(baseTime + (random.NextDouble() - 0.5) * 2, 2);
|
||
sampleDataTable.Rows[rowIndex][ROW_WICKING_TIME] = time;
|
||
rowIndex++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计算所有相关数据
|
||
CalculateAllRows();
|
||
|
||
// 更新显示
|
||
RefreshDataGridView();
|
||
|
||
ShowMessage($"已生成 {currentSampleCount} 个试样的吸水时间数据\n" +
|
||
$"- 吸水时间:30-34秒(系统读数,每个试样3次)\n" +
|
||
$"- 吸芯高度:请手动输入(每个试样3次测试)\n" +
|
||
$"- 芯吸速率:输入吸芯高度后自动计算",
|
||
"模拟数据生成");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新界面显示
|
||
/// </summary>
|
||
private void UpdateDisplay()
|
||
{
|
||
RefreshDataGridView();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新DataGridView
|
||
/// </summary>
|
||
private void RefreshDataGridView()
|
||
{
|
||
if (dataGridView1.DataSource is DataTable dt)
|
||
{
|
||
dt.AcceptChanges();
|
||
dataGridView1.Refresh();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算所有行的数据
|
||
/// </summary>
|
||
private void CalculateAllRows()
|
||
{
|
||
// 第3行:芯吸速率(mm/min)= 吸芯高度 / (吸水时间 / 60)
|
||
CalculateWickingRate();
|
||
|
||
// 第4行:平均芯吸速率(mm/min)
|
||
CalculateAverageWickingRate();
|
||
|
||
// 第5行:标准偏差
|
||
CalculateStandardDeviation();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算芯吸速率(mm/min)
|
||
/// 公式:芯吸速率 = 吸芯高度(mm) / (吸水时间(s) / 60)
|
||
/// </summary>
|
||
private void CalculateWickingRate()
|
||
{
|
||
if (isVerticalLayout)
|
||
{
|
||
// 纵向布局
|
||
DataRow timeRow = sampleDataTable.Rows[0];
|
||
DataRow heightRow = sampleDataTable.Rows[1];
|
||
DataRow rateRow = sampleDataTable.Rows[2];
|
||
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
double time1 = ConvertToDouble(timeRow[$"试样{i}_1"]);
|
||
double time2 = ConvertToDouble(timeRow[$"试样{i}_2"]);
|
||
double time3 = ConvertToDouble(timeRow[$"试样{i}_3"]);
|
||
|
||
double height1 = ConvertToDouble(heightRow[$"试样{i}_1"]);
|
||
double height2 = ConvertToDouble(heightRow[$"试样{i}_2"]);
|
||
double height3 = ConvertToDouble(heightRow[$"试样{i}_3"]);
|
||
|
||
double rate1 = (time1 > 0 && height1 > 0) ? height1 / (time1 / 60.0) : 0;
|
||
double rate2 = (time2 > 0 && height2 > 0) ? height2 / (time2 / 60.0) : 0;
|
||
double rate3 = (time3 > 0 && height3 > 0) ? height3 / (time3 / 60.0) : 0;
|
||
|
||
rateRow[$"试样{i}_1"] = Math.Round(rate1, 2);
|
||
rateRow[$"试样{i}_2"] = Math.Round(rate2, 2);
|
||
rateRow[$"试样{i}_3"] = Math.Round(rate3, 2);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 横向布局
|
||
foreach (DataRow row in sampleDataTable.Rows)
|
||
{
|
||
double time = ConvertToDouble(row[ROW_WICKING_TIME]);
|
||
double height = ConvertToDouble(row[ROW_WICKING_HEIGHT]);
|
||
double rate = (time > 0 && height > 0) ? height / (time / 60.0) : 0;
|
||
row[ROW_WICKING_RATE] = 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)
|
||
/// 计算方式:每个试样的3次测试计算一个平均值,合并显示在该试样的一列中
|
||
/// 纵向布局:显示在该试样的第1列(试样N_1)
|
||
/// 横向布局:显示在该试样的第3次测试行
|
||
/// </summary>
|
||
private void CalculateAverageWickingRate()
|
||
{
|
||
if (isVerticalLayout)
|
||
{
|
||
// 纵向布局
|
||
DataRow rateRow = sampleDataTable.Rows[2];
|
||
DataRow avgRow = sampleDataTable.Rows[3];
|
||
|
||
// 清空平均值行的所有数据
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
avgRow[$"试样{i}_1"] = 0.0;
|
||
avgRow[$"试样{i}_2"] = 0.0;
|
||
avgRow[$"试样{i}_3"] = 0.0;
|
||
}
|
||
|
||
// 计算每个试样的平均值,并填充到该试样的第1列(合并显示效果)
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
double rate1 = ConvertToDouble(rateRow[$"试样{i}_1"]);
|
||
double rate2 = ConvertToDouble(rateRow[$"试样{i}_2"]);
|
||
double rate3 = ConvertToDouble(rateRow[$"试样{i}_3"]);
|
||
|
||
List<double> groupRates = new List<double>();
|
||
if (rate1 > 0) groupRates.Add(rate1);
|
||
if (rate2 > 0) groupRates.Add(rate2);
|
||
if (rate3 > 0) groupRates.Add(rate3);
|
||
|
||
// 计算该试样的平均值
|
||
double groupAvg = groupRates.Count > 0 ? groupRates.Average() : 0;
|
||
|
||
// 只填充到该试样的第1列,实现合并显示效果
|
||
avgRow[$"试样{i}_1"] = Math.Round(groupAvg, 2);
|
||
// 第2、3列保持为0(显示为空白)
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 横向布局
|
||
// 清空所有行的平均值列
|
||
foreach (DataRow row in sampleDataTable.Rows)
|
||
{
|
||
row[ROW_AVG_WICKING_RATE] = 0.0;
|
||
}
|
||
|
||
// 计算每个试样的平均值
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
List<double> groupRates = new List<double>();
|
||
|
||
for (int j = 1; j <= 3; j++)
|
||
{
|
||
int rowIndex = (i - 1) * 3 + (j - 1);
|
||
if (rowIndex < sampleDataTable.Rows.Count)
|
||
{
|
||
double rate = ConvertToDouble(sampleDataTable.Rows[rowIndex][ROW_WICKING_RATE]);
|
||
if (rate > 0) groupRates.Add(rate);
|
||
}
|
||
}
|
||
|
||
// 计算该试样的平均值
|
||
double groupAvg = groupRates.Count > 0 ? groupRates.Average() : 0;
|
||
|
||
// 填充到该试样的第3次测试行(合并显示效果)
|
||
int targetRowIndex = (i - 1) * 3 + 2; // 第3次测试的行索引
|
||
if (targetRowIndex < sampleDataTable.Rows.Count)
|
||
{
|
||
sampleDataTable.Rows[targetRowIndex][ROW_AVG_WICKING_RATE] = Math.Round(groupAvg, 2);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算标准偏差
|
||
/// 计算方式:基于每个试样的平均芯吸速率计算标准偏差(组间标准偏差)
|
||
/// 结果:只有一个标准偏差值,合并显示在第1列
|
||
///
|
||
/// 说明:
|
||
/// - 每5个试样为一组,计算该组的标准偏差
|
||
/// - 如果试样数量不足5个,则计算现有试样的标准偏差
|
||
/// - 标准偏差基于每个试样的平均芯吸速率计算
|
||
/// </summary>
|
||
private void CalculateStandardDeviation()
|
||
{
|
||
if (isVerticalLayout)
|
||
{
|
||
// 纵向布局
|
||
DataRow rateRow = sampleDataTable.Rows[2];
|
||
DataRow stdRow = sampleDataTable.Rows[4];
|
||
|
||
// 清空标准偏差行的所有数据
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
stdRow[$"试样{i}_1"] = 0.0;
|
||
stdRow[$"试样{i}_2"] = 0.0;
|
||
stdRow[$"试样{i}_3"] = 0.0;
|
||
}
|
||
|
||
// 计算每组(5个试样)的标准偏差
|
||
int groupCount = (int)Math.Ceiling((double)currentSampleCount / 5.0);
|
||
|
||
for (int groupIndex = 0; groupIndex < groupCount; groupIndex++)
|
||
{
|
||
int startSample = groupIndex * 5 + 1;
|
||
int endSample = Math.Min(startSample + 4, currentSampleCount);
|
||
|
||
List<double> groupAverages = new List<double>(); // 存储该组每个试样的平均值
|
||
|
||
// 计算该组每个试样的平均值
|
||
for (int i = startSample; i <= endSample; i++)
|
||
{
|
||
double rate1 = ConvertToDouble(rateRow[$"试样{i}_1"]);
|
||
double rate2 = ConvertToDouble(rateRow[$"试样{i}_2"]);
|
||
double rate3 = ConvertToDouble(rateRow[$"试样{i}_3"]);
|
||
|
||
List<double> sampleRates = new List<double>();
|
||
if (rate1 > 0) sampleRates.Add(rate1);
|
||
if (rate2 > 0) sampleRates.Add(rate2);
|
||
if (rate3 > 0) sampleRates.Add(rate3);
|
||
|
||
// 如果该试样有有效数据,计算该试样的平均值
|
||
if (sampleRates.Count > 0)
|
||
{
|
||
double sampleAvg = sampleRates.Average();
|
||
groupAverages.Add(sampleAvg);
|
||
}
|
||
}
|
||
|
||
// 基于该组试样的平均值计算标准偏差
|
||
double stdDev = 0;
|
||
if (groupAverages.Count > 1)
|
||
{
|
||
double overallAverage = groupAverages.Average();
|
||
double sumOfSquares = groupAverages.Sum(avg => Math.Pow(avg - overallAverage, 2));
|
||
stdDev = Math.Sqrt(sumOfSquares / (groupAverages.Count - 1));
|
||
}
|
||
|
||
// 只在该组第1个试样的第1列显示标准偏差(合并显示效果)
|
||
stdRow[$"试样{startSample}_1"] = Math.Round(stdDev, 2);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 横向布局
|
||
// 清空所有行的标准偏差列
|
||
foreach (DataRow row in sampleDataTable.Rows)
|
||
{
|
||
row[ROW_STD_DEVIATION] = 0.0;
|
||
}
|
||
|
||
// 计算每组(5个试样)的标准偏差
|
||
int groupCount = (int)Math.Ceiling((double)currentSampleCount / 5.0);
|
||
|
||
for (int groupIndex = 0; groupIndex < groupCount; groupIndex++)
|
||
{
|
||
int startSample = groupIndex * 5 + 1;
|
||
int endSample = Math.Min(startSample + 4, currentSampleCount);
|
||
|
||
List<double> groupAverages = new List<double>(); // 存储该组每个试样的平均值
|
||
|
||
// 计算该组每个试样的平均值
|
||
for (int i = startSample; i <= endSample; i++)
|
||
{
|
||
List<double> sampleRates = new List<double>();
|
||
|
||
for (int j = 1; j <= 3; j++)
|
||
{
|
||
int rowIndex = (i - 1) * 3 + (j - 1);
|
||
if (rowIndex < sampleDataTable.Rows.Count)
|
||
{
|
||
double rate = ConvertToDouble(sampleDataTable.Rows[rowIndex][ROW_WICKING_RATE]);
|
||
if (rate > 0) sampleRates.Add(rate);
|
||
}
|
||
}
|
||
|
||
// 如果该试样有有效数据,计算该试样的平均值
|
||
if (sampleRates.Count > 0)
|
||
{
|
||
double sampleAvg = sampleRates.Average();
|
||
groupAverages.Add(sampleAvg);
|
||
}
|
||
}
|
||
|
||
// 基于该组试样的平均值计算标准偏差
|
||
double stdDev = 0;
|
||
if (groupAverages.Count > 1)
|
||
{
|
||
double overallAverage = groupAverages.Average();
|
||
double sumOfSquares = groupAverages.Sum(avg => Math.Pow(avg - overallAverage, 2));
|
||
stdDev = Math.Sqrt(sumOfSquares / (groupAverages.Count - 1));
|
||
}
|
||
|
||
// 只在该组第1个试样的第1次测试行显示标准偏差(合并显示效果)
|
||
int targetRowIndex = (startSample - 1) * 3; // 第1次测试的行索引
|
||
if (targetRowIndex < sampleDataTable.Rows.Count)
|
||
{
|
||
sampleDataTable.Rows[targetRowIndex][ROW_STD_DEVIATION] = 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 = filePath.EndsWith(".xlsx")
|
||
? (IWorkbook)new XSSFWorkbook()
|
||
: new HSSFWorkbook();
|
||
|
||
ISheet sheet = workbook.CreateSheet("液体芯吸速率测试报表");
|
||
|
||
// 创建样式
|
||
var styles = CreateReportStyles(workbook);
|
||
|
||
// 创建表头(2级)
|
||
CreateReportHeader(sheet, styles.headerStyle);
|
||
|
||
// 填充数据
|
||
FillReportData(sheet, styles.dataStyle, styles.yellowStyle);
|
||
|
||
// 设置列宽
|
||
SetReportColumnWidths(sheet);
|
||
|
||
// 保存文件
|
||
SaveWorkbook(workbook, filePath);
|
||
|
||
ShowMessage($"数据已成功导出到:{filePath}", "导出成功");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowMessage($"导出失败:{ex.Message}", "错误", MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建报表样式
|
||
/// </summary>
|
||
private (ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle)
|
||
CreateReportStyles(IWorkbook workbook)
|
||
{
|
||
// 表头样式
|
||
ICellStyle headerStyle = workbook.CreateCellStyle();
|
||
headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index;
|
||
headerStyle.FillPattern = FillPattern.SolidForeground;
|
||
SetBorders(headerStyle);
|
||
headerStyle.Alignment = NPOIHorizontalAlignment.Center;
|
||
headerStyle.VerticalAlignment = VerticalAlignment.Center;
|
||
IFont headerFont = workbook.CreateFont();
|
||
headerFont.IsBold = true;
|
||
headerStyle.SetFont(headerFont);
|
||
|
||
// 白色背景样式
|
||
ICellStyle dataStyle = workbook.CreateCellStyle();
|
||
SetBorders(dataStyle);
|
||
dataStyle.Alignment = NPOIHorizontalAlignment.Center;
|
||
dataStyle.VerticalAlignment = VerticalAlignment.Center;
|
||
|
||
// 黄色背景样式
|
||
ICellStyle yellowStyle = workbook.CreateCellStyle();
|
||
yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.LightYellow.Index;
|
||
yellowStyle.FillPattern = FillPattern.SolidForeground;
|
||
SetBorders(yellowStyle);
|
||
yellowStyle.Alignment = NPOIHorizontalAlignment.Center;
|
||
yellowStyle.VerticalAlignment = VerticalAlignment.Center;
|
||
|
||
return (headerStyle, dataStyle, yellowStyle);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置单元格边框
|
||
/// </summary>
|
||
private void SetBorders(ICellStyle style)
|
||
{
|
||
style.BorderBottom = NPOIBorderStyle.Thin;
|
||
style.BorderTop = NPOIBorderStyle.Thin;
|
||
style.BorderLeft = NPOIBorderStyle.Thin;
|
||
style.BorderRight = NPOIBorderStyle.Thin;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建报表表头(2级)
|
||
/// </summary>
|
||
private void CreateReportHeader(ISheet sheet, ICellStyle headerStyle)
|
||
{
|
||
// 创建第一行表头(试样1-N)
|
||
IRow headerRow1 = sheet.CreateRow(0);
|
||
headerRow1.Height = 400;
|
||
|
||
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 <= currentSampleCount; 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 <= currentSampleCount; 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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 填充报表数据
|
||
/// </summary>
|
||
private void FillReportData(ISheet sheet, ICellStyle dataStyle, ICellStyle yellowStyle)
|
||
{
|
||
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) ? dataStyle : yellowStyle;
|
||
|
||
// 序号列
|
||
ICell cellSeq = row.CreateCell(0);
|
||
cellSeq.SetCellValue(dataRow["序号"].ToString());
|
||
cellSeq.CellStyle = rowStyle;
|
||
|
||
int colIndex = 1;
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
// 试样次数1
|
||
ICell cell1 = row.CreateCell(colIndex++);
|
||
double val1 = ConvertToDouble(dataRow[$"试样{i}_1"]);
|
||
if (Math.Abs(val1) >= 0.001) // 只有非0值才显示
|
||
{
|
||
cell1.SetCellValue(val1);
|
||
}
|
||
cell1.CellStyle = rowStyle;
|
||
|
||
// 试样次数2
|
||
ICell cell2 = row.CreateCell(colIndex++);
|
||
double val2 = ConvertToDouble(dataRow[$"试样{i}_2"]);
|
||
if (Math.Abs(val2) >= 0.001) // 只有非0值才显示
|
||
{
|
||
cell2.SetCellValue(val2);
|
||
}
|
||
cell2.CellStyle = rowStyle;
|
||
|
||
// 试样次数3
|
||
ICell cell3 = row.CreateCell(colIndex++);
|
||
double val3 = ConvertToDouble(dataRow[$"试样{i}_3"]);
|
||
if (Math.Abs(val3) >= 0.001) // 只有非0值才显示
|
||
{
|
||
cell3.SetCellValue(val3);
|
||
}
|
||
cell3.CellStyle = rowStyle;
|
||
}
|
||
|
||
rowIndex++;
|
||
dataRowIndex++;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置报表列宽
|
||
/// </summary>
|
||
private void SetReportColumnWidths(ISheet sheet)
|
||
{
|
||
sheet.SetColumnWidth(0, 20 * 256); // 序号列
|
||
int totalColumns = currentSampleCount * 3;
|
||
for (int i = 1; i <= totalColumns; i++)
|
||
{
|
||
sheet.SetColumnWidth(i, 12 * 256); // 数据列
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存工作簿
|
||
/// </summary>
|
||
private void SaveWorkbook(IWorkbook workbook, string filePath)
|
||
{
|
||
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
|
||
{
|
||
workbook.Write(fs);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成报表(带格式的 Excel)
|
||
/// </summary>
|
||
public void GenerateReport(string filePath)
|
||
{
|
||
// 使用 ExportToExcel 方法
|
||
ExportToExcel(filePath);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清空数据
|
||
/// </summary>
|
||
public void ClearData()
|
||
{
|
||
sampleDataTable.Clear();
|
||
InitializeDataTable();
|
||
RefreshDataGridView();
|
||
}
|
||
}
|
||
}
|