907 lines
32 KiB
C#
907 lines
32 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 Form2 : Form
|
||
{
|
||
#region 常量定义
|
||
private const int TIMER_INTERVAL = 1000;
|
||
private const double MIN_WEIGHT = 10.0;
|
||
private const double MAX_WEIGHT = 20.0;
|
||
private const string DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||
private const string ROW_INITIAL_WEIGHT = "初始重量(g)";
|
||
private const string ROW_AFTER_WEIGHT = "浸润后重量(g)";
|
||
private const string ROW_ABSORPTION = "液体吸收量(%)";
|
||
private const string ROW_SOAK_TIME = "浸润时间";
|
||
private const string ROW_HANG_TIME = "悬挂时间";
|
||
private const string ROW_RUN_SPEED = "运行速度";
|
||
private const string ROW_AVG_ABSORPTION = "液体吸收量平均值(%)";
|
||
private const string ROW_MAX_ABSORPTION = "液体吸收量最大值(%)";
|
||
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 sampleCount = 0;
|
||
private int currentSampleCount = 5; // 当前试样数量,可动态调整
|
||
private readonly Random random = new Random();
|
||
#endregion
|
||
|
||
public Form2()
|
||
{
|
||
InitializeComponent();
|
||
InitializeDataTable();
|
||
InitializeTimer();
|
||
InitializeDataGridView();
|
||
InitializeEventHandlers();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化 DataGridView 列
|
||
/// </summary>
|
||
private void InitializeDataGridView()
|
||
{
|
||
dataGridView1.SuspendLayout();
|
||
try
|
||
{
|
||
dataGridView1.Columns.Clear();
|
||
|
||
// 创建共享样式
|
||
var centerStyle = new DataGridViewCellStyle
|
||
{
|
||
Alignment = DataGridViewContentAlignment.MiddleCenter
|
||
};
|
||
|
||
var yellowCenterStyle = new DataGridViewCellStyle
|
||
{
|
||
Alignment = DataGridViewContentAlignment.MiddleCenter,
|
||
BackColor = Color.FromArgb(255, 255, 200)
|
||
};
|
||
|
||
// 添加序号列
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = "序号",
|
||
HeaderText = "序号",
|
||
DataPropertyName = "序号",
|
||
Width = 180,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = centerStyle
|
||
});
|
||
|
||
// 根据当前试样数量动态添加列
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
|
||
{
|
||
Name = $"试样{i}",
|
||
HeaderText = $"试样{i}",
|
||
DataPropertyName = $"试样{i}",
|
||
Width = 150,
|
||
ReadOnly = true,
|
||
DefaultCellStyle = yellowCenterStyle
|
||
});
|
||
}
|
||
|
||
// 绑定数据源
|
||
dataGridView1.DataSource = sampleDataTable;
|
||
dataGridView1.CellFormatting += DataGridView1_CellFormatting;
|
||
}
|
||
finally
|
||
{
|
||
dataGridView1.ResumeLayout();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单元格格式化事件
|
||
/// </summary>
|
||
private void DataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
|
||
{
|
||
if (e.RowIndex < 0) return;
|
||
|
||
// 格式化数值显示为2位小数
|
||
if (e.Value != null && e.Value != DBNull.Value)
|
||
{
|
||
if (double.TryParse(e.Value.ToString(), out double numValue))
|
||
{
|
||
e.Value = numValue.ToString("F2");
|
||
e.FormattingApplied = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <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();
|
||
}
|
||
|
||
/// <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();
|
||
sampleDataTable.Columns.Add("序号", typeof(string));
|
||
|
||
// 根据当前试样数量动态添加列
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
sampleDataTable.Columns.Add($"试样{i}", typeof(object));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置试样数量并重新初始化
|
||
/// </summary>
|
||
public void SetSampleCount(int count)
|
||
{
|
||
if (count < 1 || count > 20) // 限制最大20个试样
|
||
{
|
||
ShowMessage("试样数量必须在1-20之间", "参数错误", MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
currentSampleCount = count;
|
||
InitializeDataTable();
|
||
InitializeDataGridView();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化定时器用于模拟寄存器数据读取
|
||
/// </summary>
|
||
private void InitializeTimer()
|
||
{
|
||
dataTimer = new System.Windows.Forms.Timer
|
||
{
|
||
Interval = TIMER_INTERVAL
|
||
};
|
||
dataTimer.Tick += DataTimer_Tick;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定时器事件 - 模拟从寄存器读取数据
|
||
/// </summary>
|
||
private void DataTimer_Tick(object sender, EventArgs e)
|
||
{
|
||
// 清除旧的计算行
|
||
RemoveCalculatedRows();
|
||
|
||
// 读取初始重量和浸润后重量
|
||
double[] initialWeights = new double[currentSampleCount];
|
||
double[] afterWeights = new double[currentSampleCount];
|
||
|
||
for (int i = 0; i < currentSampleCount; i++)
|
||
{
|
||
initialWeights[i] = ReadRegisterData(i * 2); // 初始重量
|
||
afterWeights[i] = ReadRegisterData(i * 2 + 1); // 浸润后重量
|
||
}
|
||
|
||
sampleCount++;
|
||
|
||
// 更新显示
|
||
UpdateDisplay(initialWeights, afterWeights);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移除计算行
|
||
/// </summary>
|
||
private void RemoveCalculatedRows()
|
||
{
|
||
var rowsToRemove = sampleDataTable.AsEnumerable()
|
||
.Where(r => r.Field<string>("序号") == ROW_ABSORPTION ||
|
||
r.Field<string>("序号") == ROW_SOAK_TIME ||
|
||
r.Field<string>("序号") == ROW_HANG_TIME ||
|
||
r.Field<string>("序号") == ROW_RUN_SPEED ||
|
||
r.Field<string>("序号") == ROW_AVG_ABSORPTION ||
|
||
r.Field<string>("序号") == ROW_MAX_ABSORPTION ||
|
||
r.Field<string>("序号") == ROW_STD_DEVIATION ||
|
||
r.Field<string>("序号") == "液体吸收量平均值(%)试样1-5" ||
|
||
r.Field<string>("序号") == "液体吸收量平均值(%)试样6-10" ||
|
||
r.Field<string>("序号") == "液体吸收量最大值(%)试样1-5" ||
|
||
r.Field<string>("序号") == "液体吸收量最大值(%)试样6-10" ||
|
||
r.Field<string>("序号") == "标准偏差试样1-5" ||
|
||
r.Field<string>("序号") == "标准偏差试样6-10")
|
||
.ToList();
|
||
|
||
foreach (var row in rowsToRemove)
|
||
{
|
||
sampleDataTable.Rows.Remove(row);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 模拟从寄存器读取数据
|
||
/// </summary>
|
||
private double ReadRegisterData(int registerAddress)
|
||
{
|
||
return MIN_WEIGHT + random.NextDouble() * (MAX_WEIGHT - MIN_WEIGHT);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成模拟测试数据(精确模拟真实测试场景)
|
||
/// </summary>
|
||
public void GenerateMockData()
|
||
{
|
||
sampleDataTable.Clear();
|
||
sampleCount = 0;
|
||
|
||
// 生成模拟数据
|
||
double[] initialWeights = new double[currentSampleCount];
|
||
double[] afterWeights = new double[currentSampleCount];
|
||
|
||
// 设置基准值,使数据更真实
|
||
double baseInitialWeight = 15.0; // 基准初始重量
|
||
double baseAbsorptionRate = 0.35; // 基准吸收率 35%
|
||
|
||
for (int i = 0; i < currentSampleCount; i++)
|
||
{
|
||
// 初始重量:在基准值附近波动 ±3g
|
||
initialWeights[i] = Math.Round(baseInitialWeight + (random.NextDouble() - 0.5) * 6.0, 2);
|
||
|
||
// 吸收率:在基准吸收率附近波动 ±10%
|
||
double absorptionRate = baseAbsorptionRate + (random.NextDouble() - 0.5) * 0.2;
|
||
|
||
// 浸润后重量 = 初始重量 × (1 + 吸收率)
|
||
afterWeights[i] = Math.Round(initialWeights[i] * (1 + absorptionRate), 2);
|
||
}
|
||
|
||
sampleCount = 1;
|
||
UpdateDisplay(initialWeights, afterWeights);
|
||
|
||
// 计算并显示统计信息
|
||
double[] absorptions = new double[currentSampleCount];
|
||
for (int i = 0; i < currentSampleCount; i++)
|
||
{
|
||
absorptions[i] = ((afterWeights[i] - initialWeights[i]) / initialWeights[i]) * 100;
|
||
}
|
||
|
||
double avgAbsorption = absorptions.Average();
|
||
double stdDev = CalculateStandardDeviation(absorptions);
|
||
double maxAbsorption = absorptions.Max();
|
||
double minAbsorption = absorptions.Min();
|
||
|
||
string message = $"已生成 {currentSampleCount} 个试样的模拟数据\n\n" +
|
||
$"液体吸收量统计:\n" +
|
||
$"平均值:{avgAbsorption:F2}%\n" +
|
||
$"标准偏差:{stdDev:F2}%\n" +
|
||
$"最大值:{maxAbsorption:F2}%\n" +
|
||
$"最小值:{minAbsorption:F2}%";
|
||
|
||
ShowMessage(message, "模拟数据生成");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新界面显示
|
||
/// </summary>
|
||
private void UpdateDisplay(double[] initialWeights, double[] afterWeights)
|
||
{
|
||
sampleDataTable.Clear();
|
||
|
||
int count = Math.Min(initialWeights.Length, currentSampleCount);
|
||
|
||
// 1. 初始重量行
|
||
DataRow initialRow = sampleDataTable.NewRow();
|
||
initialRow["序号"] = ROW_INITIAL_WEIGHT;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
initialRow[$"试样{i + 1}"] = initialWeights[i];
|
||
}
|
||
sampleDataTable.Rows.Add(initialRow);
|
||
|
||
// 2. 浸润后重量行
|
||
DataRow afterRow = sampleDataTable.NewRow();
|
||
afterRow["序号"] = ROW_AFTER_WEIGHT;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
afterRow[$"试样{i + 1}"] = afterWeights[i];
|
||
}
|
||
sampleDataTable.Rows.Add(afterRow);
|
||
|
||
// 3. 液体吸收量行 (%)
|
||
DataRow absorptionRow = sampleDataTable.NewRow();
|
||
absorptionRow["序号"] = ROW_ABSORPTION;
|
||
double[] absorptions = new double[count];
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
absorptions[i] = ((afterWeights[i] - initialWeights[i]) / initialWeights[i]) * 100;
|
||
absorptionRow[$"试样{i + 1}"] = absorptions[i];
|
||
}
|
||
sampleDataTable.Rows.Add(absorptionRow);
|
||
|
||
// 4. 浸润时间行
|
||
DataRow soakTimeRow = sampleDataTable.NewRow();
|
||
soakTimeRow["序号"] = ROW_SOAK_TIME;
|
||
// 为每个试样生成浸润时间(基准60s,波动±10s)
|
||
for (int i = 1; i <= count; i++)
|
||
{
|
||
int soakTime = 60 + random.Next(-10, 11); // 50-70秒
|
||
soakTimeRow[$"试样{i}"] = $"{soakTime}s";
|
||
}
|
||
sampleDataTable.Rows.Add(soakTimeRow);
|
||
|
||
// 5. 悬挂时间行
|
||
DataRow hangTimeRow = sampleDataTable.NewRow();
|
||
hangTimeRow["序号"] = ROW_HANG_TIME;
|
||
// 为每个试样生成悬挂时间(基准30s,波动±5s)
|
||
for (int i = 1; i <= count; i++)
|
||
{
|
||
int hangTime = 30 + random.Next(-5, 6); // 25-35秒
|
||
hangTimeRow[$"试样{i}"] = $"{hangTime}s";
|
||
}
|
||
sampleDataTable.Rows.Add(hangTimeRow);
|
||
|
||
// 6. 运行速度行
|
||
DataRow runSpeedRow = sampleDataTable.NewRow();
|
||
runSpeedRow["序号"] = ROW_RUN_SPEED;
|
||
// 为每个试样生成运行速度(基准100mm/min,波动±10mm/min)
|
||
for (int i = 1; i <= count; i++)
|
||
{
|
||
int runSpeed = 100 + random.Next(-10, 11); // 90-110 mm/min
|
||
runSpeedRow[$"试样{i}"] = $"{runSpeed}mm/min";
|
||
}
|
||
sampleDataTable.Rows.Add(runSpeedRow);
|
||
|
||
// 7. 液体吸收量平均值行(试样1-5)
|
||
DataRow avgRow1 = sampleDataTable.NewRow();
|
||
avgRow1["序号"] = "液体吸收量平均值(%)试样1-5";
|
||
|
||
// 计算试样1-5的平均值
|
||
int group1Count = Math.Min(5, count);
|
||
if (group1Count > 0)
|
||
{
|
||
double avgAbsorption1 = absorptions.Take(group1Count).Average();
|
||
avgRow1["试样1"] = avgAbsorption1;
|
||
}
|
||
|
||
// 试样2-5列为空(只设置到第5列或count,取较小值)
|
||
int maxCol1 = Math.Min(5, count);
|
||
for (int i = 2; i <= maxCol1; i++)
|
||
{
|
||
avgRow1[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
|
||
// 如果有试样6-10,这些列也设置为空
|
||
for (int i = 6; i <= count; i++)
|
||
{
|
||
avgRow1[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
sampleDataTable.Rows.Add(avgRow1);
|
||
|
||
// 8. 液体吸收量平均值行(试样6-10)- 仅当试样数量>5时添加
|
||
if (count > 5)
|
||
{
|
||
DataRow avgRow2 = sampleDataTable.NewRow();
|
||
avgRow2["序号"] = "液体吸收量平均值(%)试样6-10";
|
||
|
||
// 计算试样6-10的平均值
|
||
int group2Count = Math.Min(5, count - 5);
|
||
if (group2Count > 0)
|
||
{
|
||
double avgAbsorption2 = absorptions.Skip(5).Take(group2Count).Average();
|
||
avgRow2["试样6"] = avgAbsorption2;
|
||
}
|
||
|
||
// 其他列为空
|
||
for (int i = 1; i <= 5; i++)
|
||
{
|
||
avgRow2[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
for (int i = 7; i <= count; i++)
|
||
{
|
||
avgRow2[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
sampleDataTable.Rows.Add(avgRow2);
|
||
}
|
||
|
||
// 9. 液体吸收量最大值行(试样1-5)
|
||
DataRow maxRow1 = sampleDataTable.NewRow();
|
||
maxRow1["序号"] = "液体吸收量最大值(%)试样1-5";
|
||
|
||
// 计算试样1-5的最大值
|
||
if (group1Count > 0)
|
||
{
|
||
double maxAbsorption1 = absorptions.Take(group1Count).Max();
|
||
maxRow1["试样1"] = maxAbsorption1;
|
||
}
|
||
|
||
// 试样2-5列为空
|
||
for (int i = 2; i <= maxCol1; i++)
|
||
{
|
||
maxRow1[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
|
||
// 如果有试样6-10,这些列也设置为空
|
||
for (int i = 6; i <= count; i++)
|
||
{
|
||
maxRow1[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
sampleDataTable.Rows.Add(maxRow1);
|
||
|
||
// 10. 液体吸收量最大值行(试样6-10)- 仅当试样数量>5时添加
|
||
if (count > 5)
|
||
{
|
||
DataRow maxRow2 = sampleDataTable.NewRow();
|
||
maxRow2["序号"] = "液体吸收量最大值(%)试样6-10";
|
||
|
||
// 计算试样6-10的最大值
|
||
int group2Count = Math.Min(5, count - 5);
|
||
if (group2Count > 0)
|
||
{
|
||
double maxAbsorption2 = absorptions.Skip(5).Take(group2Count).Max();
|
||
maxRow2["试样6"] = maxAbsorption2;
|
||
}
|
||
|
||
// 其他列为空
|
||
for (int i = 1; i <= 5; i++)
|
||
{
|
||
maxRow2[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
for (int i = 7; i <= count; i++)
|
||
{
|
||
maxRow2[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
sampleDataTable.Rows.Add(maxRow2);
|
||
}
|
||
|
||
// 11. 标准偏差行(试样1-5)
|
||
DataRow stdDevRow1 = sampleDataTable.NewRow();
|
||
stdDevRow1["序号"] = "标准偏差试样1-5";
|
||
|
||
// 计算试样1-5的标准偏差
|
||
if (group1Count > 1)
|
||
{
|
||
double stdDev1 = CalculateStandardDeviation(absorptions.Take(group1Count).ToArray());
|
||
stdDevRow1["试样1"] = stdDev1;
|
||
}
|
||
|
||
// 试样2-5列为空
|
||
for (int i = 2; i <= maxCol1; i++)
|
||
{
|
||
stdDevRow1[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
|
||
// 如果有试样6-10,这些列也设置为空
|
||
for (int i = 6; i <= count; i++)
|
||
{
|
||
stdDevRow1[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
sampleDataTable.Rows.Add(stdDevRow1);
|
||
|
||
// 12. 标准偏差行(试样6-10)- 仅当试样数量>5时添加
|
||
if (count > 5)
|
||
{
|
||
DataRow stdDevRow2 = sampleDataTable.NewRow();
|
||
stdDevRow2["序号"] = "标准偏差试样6-10";
|
||
|
||
// 计算试样6-10的标准偏差
|
||
int group2Count = Math.Min(5, count - 5);
|
||
if (group2Count > 1)
|
||
{
|
||
double stdDev2 = CalculateStandardDeviation(absorptions.Skip(5).Take(group2Count).ToArray());
|
||
stdDevRow2["试样6"] = stdDev2;
|
||
}
|
||
|
||
// 其他列为空
|
||
for (int i = 1; i <= 5; i++)
|
||
{
|
||
stdDevRow2[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
for (int i = 7; i <= count; i++)
|
||
{
|
||
stdDevRow2[$"试样{i}"] = DBNull.Value;
|
||
}
|
||
sampleDataTable.Rows.Add(stdDevRow2);
|
||
}
|
||
|
||
// 刷新界面
|
||
RefreshDataGridView();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算标准偏差(样本标准偏差,n-1)
|
||
/// </summary>
|
||
private double CalculateStandardDeviation(double[] values)
|
||
{
|
||
if (values == null || values.Length <= 1) return 0;
|
||
|
||
double avg = values.Average();
|
||
double sumOfSquares = values.Sum(val => Math.Pow(val - avg, 2));
|
||
// 使用样本标准偏差公式:除以 (n-1)
|
||
return Math.Sqrt(sumOfSquares / (values.Length - 1));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新DataGridView
|
||
/// </summary>
|
||
private void RefreshDataGridView()
|
||
{
|
||
if (dataGridView1.DataSource is DataTable dt)
|
||
{
|
||
dt.AcceptChanges();
|
||
dataGridView1.Refresh();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动数据采集
|
||
/// </summary>
|
||
public void StartDataCollection()
|
||
{
|
||
dataTimer.Start();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止数据采集
|
||
/// </summary>
|
||
public void StopDataCollection()
|
||
{
|
||
dataTimer.Stop();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成报表(带格式的 Excel)
|
||
/// </summary>
|
||
public void GenerateReport(string filePath)
|
||
{
|
||
try
|
||
{
|
||
IWorkbook workbook = new XSSFWorkbook();
|
||
ISheet sheet = workbook.CreateSheet("液体吸收测试报表");
|
||
|
||
// 创建样式
|
||
var styles = CreateReportStyles(workbook);
|
||
|
||
// 创建标题
|
||
CreateReportTitle(sheet, styles.titleStyle);
|
||
|
||
// 创建表头
|
||
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 titleStyle, ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle)
|
||
CreateReportStyles(IWorkbook workbook)
|
||
{
|
||
// 标题样式
|
||
ICellStyle titleStyle = workbook.CreateCellStyle();
|
||
IFont titleFont = workbook.CreateFont();
|
||
titleFont.FontHeightInPoints = 16;
|
||
titleFont.IsBold = true;
|
||
titleStyle.SetFont(titleFont);
|
||
titleStyle.Alignment = NPOIHorizontalAlignment.Center;
|
||
|
||
// 表头样式
|
||
ICellStyle headerStyle = workbook.CreateCellStyle();
|
||
headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index;
|
||
headerStyle.FillPattern = FillPattern.SolidForeground;
|
||
SetBorders(headerStyle);
|
||
IFont headerFont = workbook.CreateFont();
|
||
headerFont.IsBold = true;
|
||
headerStyle.SetFont(headerFont);
|
||
headerStyle.Alignment = NPOIHorizontalAlignment.Center;
|
||
|
||
// 数据样式
|
||
ICellStyle dataStyle = workbook.CreateCellStyle();
|
||
SetBorders(dataStyle);
|
||
dataStyle.Alignment = NPOIHorizontalAlignment.Center;
|
||
|
||
// 黄色背景样式
|
||
ICellStyle yellowStyle = workbook.CreateCellStyle();
|
||
yellowStyle.CloneStyleFrom(dataStyle);
|
||
yellowStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index;
|
||
yellowStyle.FillPattern = FillPattern.SolidForeground;
|
||
|
||
return (titleStyle, 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>
|
||
/// 创建报表标题
|
||
/// </summary>
|
||
private void CreateReportTitle(ISheet sheet, ICellStyle titleStyle)
|
||
{
|
||
IRow titleRow = sheet.CreateRow(0);
|
||
ICell titleCell = titleRow.CreateCell(0);
|
||
titleCell.SetCellValue("液体吸收测试报表");
|
||
titleCell.CellStyle = titleStyle;
|
||
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, currentSampleCount));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建报表表头
|
||
/// </summary>
|
||
private void CreateReportHeader(ISheet sheet, ICellStyle headerStyle)
|
||
{
|
||
IRow headerRow = sheet.CreateRow(2);
|
||
|
||
// 序号列
|
||
ICell cell0 = headerRow.CreateCell(0);
|
||
cell0.SetCellValue("序号");
|
||
cell0.CellStyle = headerStyle;
|
||
|
||
// 动态创建试样列
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
ICell cell = headerRow.CreateCell(i);
|
||
cell.SetCellValue($"试样{i}");
|
||
cell.CellStyle = headerStyle;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 填充报表数据
|
||
/// </summary>
|
||
private void FillReportData(ISheet sheet, ICellStyle dataStyle, ICellStyle yellowStyle)
|
||
{
|
||
int rowIndex = 3;
|
||
|
||
foreach (DataRow dataRow in sampleDataTable.Rows)
|
||
{
|
||
string rowName = dataRow["序号"].ToString();
|
||
|
||
// 处理试样1-5分组行的合并单元格
|
||
if (rowName == "液体吸收量平均值(%)试样1-5" ||
|
||
rowName == "液体吸收量最大值(%)试样1-5" ||
|
||
rowName == "标准偏差试样1-5")
|
||
{
|
||
IRow row = sheet.CreateRow(rowIndex++);
|
||
|
||
// 序号列
|
||
ICell nameCell = row.CreateCell(0);
|
||
nameCell.SetCellValue(rowName);
|
||
nameCell.CellStyle = dataStyle;
|
||
|
||
// 获取试样1的值
|
||
var value = dataRow["试样1"];
|
||
if (value != null && value != DBNull.Value)
|
||
{
|
||
ICell cell = row.CreateCell(1);
|
||
cell.SetCellValue(Convert.ToDouble(value));
|
||
cell.CellStyle = yellowStyle;
|
||
|
||
// 合并试样1-5列
|
||
int group1Count = Math.Min(5, currentSampleCount);
|
||
if (group1Count > 1)
|
||
{
|
||
sheet.AddMergedRegion(new CellRangeAddress(rowIndex - 1, rowIndex - 1, 1, group1Count));
|
||
}
|
||
}
|
||
}
|
||
// 处理试样6-10分组行的合并单元格
|
||
else if (rowName == "液体吸收量平均值(%)试样6-10" ||
|
||
rowName == "液体吸收量最大值(%)试样6-10" ||
|
||
rowName == "标准偏差试样6-10")
|
||
{
|
||
IRow row = sheet.CreateRow(rowIndex++);
|
||
|
||
// 序号列
|
||
ICell nameCell = row.CreateCell(0);
|
||
nameCell.SetCellValue(rowName);
|
||
nameCell.CellStyle = dataStyle;
|
||
|
||
// 获取试样6的值
|
||
var value = dataRow["试样6"];
|
||
if (value != null && value != DBNull.Value)
|
||
{
|
||
ICell cell = row.CreateCell(6);
|
||
cell.SetCellValue(Convert.ToDouble(value));
|
||
cell.CellStyle = yellowStyle;
|
||
|
||
// 合并试样6-10列
|
||
int group2Count = Math.Min(5, currentSampleCount - 5);
|
||
if (group2Count > 1)
|
||
{
|
||
sheet.AddMergedRegion(new CellRangeAddress(rowIndex - 1, rowIndex - 1, 6, 5 + group2Count));
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 普通行处理
|
||
IRow row = sheet.CreateRow(rowIndex++);
|
||
|
||
// 序号列
|
||
ICell nameCell = row.CreateCell(0);
|
||
nameCell.SetCellValue(rowName);
|
||
nameCell.CellStyle = dataStyle;
|
||
|
||
// 数据列
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
ICell cell = row.CreateCell(i);
|
||
var value = dataRow[$"试样{i}"];
|
||
|
||
if (value != null && value != DBNull.Value)
|
||
{
|
||
if (double.TryParse(value.ToString(), out double numValue))
|
||
{
|
||
cell.SetCellValue(numValue);
|
||
}
|
||
else
|
||
{
|
||
cell.SetCellValue(value.ToString());
|
||
}
|
||
}
|
||
|
||
cell.CellStyle = yellowStyle;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置报表列宽
|
||
/// </summary>
|
||
private void SetReportColumnWidths(ISheet sheet)
|
||
{
|
||
sheet.SetColumnWidth(0, 25 * 256);
|
||
for (int i = 1; i <= currentSampleCount; i++)
|
||
{
|
||
sheet.SetColumnWidth(i, 18 * 256);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存工作簿
|
||
/// </summary>
|
||
private void SaveWorkbook(IWorkbook workbook, string filePath)
|
||
{
|
||
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
|
||
{
|
||
workbook.Write(fs);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清空数据
|
||
/// </summary>
|
||
public void ClearData()
|
||
{
|
||
sampleDataTable.Clear();
|
||
sampleCount = 0;
|
||
RefreshDataGridView();
|
||
}
|
||
}
|
||
}
|
||
|