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 Form1 : Form
{
#region 常量定义
private const int TIMER_INTERVAL = 1000;
private const double MIN_SAMPLE_VALUE = 30.0;
private const double MAX_SAMPLE_VALUE = 34.0;
private const int COLUMN_WIDTH_STANDARD = 150;
private const int COLUMN_WIDTH_TIME = 200;
private const string AVG_ROW_LABEL = "平均时间(s)";
private const string TIME_ROW_LABEL = "时间(s)";
private const string DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
#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 Form1()
{
InitializeComponent();
InitializeDataTable();
InitializeTimer();
InitializeDataGridView();
InitializeEventHandlers();
}
///
/// 初始化 DataGridView 列
///
private void InitializeDataGridView()
{
dataGridView1.SuspendLayout();
try
{
dataGridView1.Columns.Clear();
// 创建共享样式
var centerAlignStyle = new DataGridViewCellStyle
{
Alignment = DataGridViewContentAlignment.MiddleCenter
};
var yellowCenterStyle = new DataGridViewCellStyle
{
Alignment = DataGridViewContentAlignment.MiddleCenter,
BackColor = Color.FromArgb(255, 255, 200)
};
// 添加序号列
AddColumn("序号", "序号", COLUMN_WIDTH_STANDARD, centerAlignStyle);
// 添加试样列
for (int i = 1; i <= currentSampleCount; i++)
{
AddColumn($"试样{i}", $"试样{i}", COLUMN_WIDTH_STANDARD, yellowCenterStyle);
}
// 绑定数据源
dataGridView1.DataSource = sampleDataTable;
dataGridView1.CellFormatting += DataGridView1_CellFormatting;
}
finally
{
dataGridView1.ResumeLayout();
}
}
///
/// 添加列的辅助方法
///
private void AddColumn(string name, string headerText, int width, DataGridViewCellStyle style)
{
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
{
Name = name,
HeaderText = headerText,
DataPropertyName = name,
Width = width,
ReadOnly = true,
DefaultCellStyle = style
});
}
///
/// 单元格格式化事件 - 处理平均值行的显示
///
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;
}
}
}
///
/// 尝试计算总平均值
///
private bool TryCalculateOverallAverage(DataGridViewRow row, out double overallAvg)
{
overallAvg = 0;
try
{
double sum = 0;
for (int i = 1; i <= currentSampleCount; i++)
{
var cellValue = row.Cells[$"试样{i}"].Value;
if (cellValue == null) return false;
sum += Convert.ToDouble(cellValue);
}
overallAvg = sum / currentSampleCount;
return true;
}
catch
{
return false;
}
}
// 移除TryCalculateOverallAverage方法,因为不再需要
///
/// 初始化事件处理器
///
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();
}
///
/// 连接设备按钮点击事件
///
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("开始数据采集");
}
}
///
/// 更新按钮状态
///
private void UpdateButtonState(Button button, string text, Color backColor)
{
button.Text = text;
button.BackColor = backColor;
}
///
/// 显示提示消息
///
private void ShowMessage(string message, string title = "提示", MessageBoxIcon icon = MessageBoxIcon.Information)
{
MessageBox.Show(message, title, MessageBoxButtons.OK, icon);
}
///
/// 打印按钮点击事件
///
private void Button2_Click(object sender, EventArgs e)
{
ShowMessage("打印功能开发中...");
}
///
/// 导出按钮点击事件
///
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);
}
}
///
/// 返回按钮点击事件
///
private void Button4_Click(object sender, EventArgs e)
{
this.Close();
}
///
/// 生成模拟数据按钮点击事件
///
private void Button5_Click(object sender, EventArgs e)
{
GenerateMockData();
}
///
/// 初始化数据表结构
///
private void InitializeDataTable()
{
sampleDataTable = new DataTable();
sampleDataTable.Columns.Add("序号", typeof(string));
for (int i = 1; i <= currentSampleCount; i++)
{
sampleDataTable.Columns.Add($"试样{i}", typeof(double));
}
}
///
/// 设置试样数量并重新初始化
///
public void SetSampleCount(int count)
{
if (count < 1 || count > 20)
{
ShowMessage("试样数量必须在1-20之间", "参数错误", MessageBoxIcon.Warning);
return;
}
currentSampleCount = count;
InitializeDataTable();
InitializeDataGridView();
}
///
/// 初始化定时器用于模拟寄存器数据读取
///
private void InitializeTimer()
{
dataTimer = new System.Windows.Forms.Timer
{
Interval = TIMER_INTERVAL
};
dataTimer.Tick += DataTimer_Tick;
}
///
/// 定时器事件 - 模拟从寄存器读取数据
///
private void DataTimer_Tick(object sender, EventArgs e)
{
// 移除旧的平均值行
RemoveAverageRows();
// 创建新数据行
DataRow dataRow = sampleDataTable.NewRow();
dataRow["序号"] = TIME_ROW_LABEL;
// 读取寄存器数据
for (int i = 0; i < currentSampleCount; i++)
{
dataRow[$"试样{i + 1}"] = ReadRegisterData(i);
}
// 不再设置时间列
sampleDataTable.Rows.Add(dataRow);
sampleCount++;
UpdateDisplay();
}
///
/// 移除平均值行
///
private void RemoveAverageRows()
{
var avgRows = sampleDataTable.Select($"序号 = '{AVG_ROW_LABEL}'");
foreach (var row in avgRows)
{
sampleDataTable.Rows.Remove(row);
}
}
///
/// 模拟从寄存器读取数据
/// 实际应用中替换为真实的 Modbus 或其他协议读取
///
private double ReadRegisterData(int registerAddress)
{
// 模拟数据:30-34 之间的随机值
return MIN_SAMPLE_VALUE + random.NextDouble() * (MAX_SAMPLE_VALUE - MIN_SAMPLE_VALUE);
}
///
/// 生成模拟测试数据
///
public void GenerateMockData()
{
sampleDataTable.Clear();
sampleCount = 0;
const int mockDataCount = 1;
for (int i = 0; i < mockDataCount; i++)
{
DataRow dataRow = sampleDataTable.NewRow();
dataRow["序号"] = TIME_ROW_LABEL;
// 为每个试样生成模拟数据
for (int j = 1; j <= currentSampleCount; j++)
{
double value = MIN_SAMPLE_VALUE + random.NextDouble() * (MAX_SAMPLE_VALUE - MIN_SAMPLE_VALUE);
dataRow[$"试样{j}"] = Math.Round(value, 2);
}
// 不再设置时间列
sampleDataTable.Rows.Add(dataRow);
sampleCount++;
}
UpdateDisplay();
ShowMessage($"已生成 {sampleCount} 条模拟数据", "模拟数据生成");
}
///
/// 更新界面显示
///
private void UpdateDisplay()
{
if (sampleDataTable.Rows.Count == 0)
return;
// 计算并添加/更新平均值行
UpdateAverageRow(null);
// 刷新界面
RefreshDataGridView();
}
///
/// 计算所有试样的总平均值
///
private double CalculateOverallAverage()
{
var timeRows = sampleDataTable.AsEnumerable()
.Where(r => r.Field("序号") == TIME_ROW_LABEL);
if (!timeRows.Any())
return 0;
double sum = 0;
int count = 0;
foreach (var row in timeRows)
{
for (int i = 1; i <= currentSampleCount; i++)
{
sum += row.Field($"试样{i}");
count++;
}
}
return count > 0 ? sum / count : 0;
}
///
/// 更新或添加平均值行
///
private void UpdateAverageRow(double[] averages)
{
var avgRows = sampleDataTable.Select($"序号 = '{AVG_ROW_LABEL}'");
// 计算总平均值
double overallAvg = CalculateOverallAverage();
if (avgRows.Length == 0)
{
// 添加新的平均值行
DataRow avgRow = sampleDataTable.NewRow();
avgRow["序号"] = AVG_ROW_LABEL;
// 只在第一列显示总平均值
avgRow["试样1"] = overallAvg;
// 其他列设置为空
for (int i = 2; i <= currentSampleCount; i++)
{
avgRow[$"试样{i}"] = DBNull.Value;
}
sampleDataTable.Rows.Add(avgRow);
}
else
{
// 更新现有平均值行
avgRows[0]["试样1"] = overallAvg;
// 其他列设置为空
for (int i = 2; i <= currentSampleCount; i++)
{
avgRows[0][$"试样{i}"] = DBNull.Value;
}
}
}
///
/// 刷新DataGridView
///
private void RefreshDataGridView()
{
if (dataGridView1.DataSource is DataTable dt)
{
dt.AcceptChanges();
dataGridView1.Refresh();
}
}
///
/// 启动数据采集
///
public void StartDataCollection()
{
dataTimer.Start();
}
///
/// 停止数据采集
///
public void StopDataCollection()
{
dataTimer.Stop();
}
///
/// 导出数据到 Excel (.xlsx)
///
public void ExportToExcel(string filePath)
{
try
{
IWorkbook workbook = CreateWorkbook(filePath);
ISheet sheet = workbook.CreateSheet("液体吸收测试报表");
// 创建样式
var styles = CreateExcelStyles(workbook);
// 创建标题行
CreateHeaderRow(sheet, styles.headerStyle);
// 填充数据
FillDataRows(sheet, styles.dataStyle);
// 添加平均值行
AddAverageRow(sheet, styles.avgStyle);
// 自动调整列宽
AutoSizeColumns(sheet, 7);
// 保存文件
SaveWorkbook(workbook, filePath);
ShowMessage($"数据已成功导出到:{filePath}", "导出成功");
}
catch (Exception ex)
{
ShowMessage($"导出失败:{ex.Message}", "错误", MessageBoxIcon.Error);
}
}
///
/// 创建工作簿
///
private IWorkbook CreateWorkbook(string filePath)
{
return filePath.EndsWith(".xlsx")
? (IWorkbook)new XSSFWorkbook()
: new HSSFWorkbook();
}
///
/// 创建Excel样式
///
private (ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle avgStyle) CreateExcelStyles(IWorkbook workbook)
{
// 标题样式
ICellStyle headerStyle = workbook.CreateCellStyle();
headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index;
headerStyle.FillPattern = FillPattern.SolidForeground;
IFont headerFont = workbook.CreateFont();
headerFont.IsBold = true;
headerStyle.SetFont(headerFont);
// 数据样式
ICellStyle dataStyle = workbook.CreateCellStyle();
// 平均值样式
ICellStyle avgStyle = workbook.CreateCellStyle();
avgStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Yellow.Index;
avgStyle.FillPattern = FillPattern.SolidForeground;
return (headerStyle, dataStyle, avgStyle);
}
///
/// 创建标题行
///
private void CreateHeaderRow(ISheet sheet, ICellStyle headerStyle)
{
IRow headerRow = sheet.CreateRow(0);
// 序号列
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;
}
}
///
/// 填充数据行
///
private void FillDataRows(ISheet sheet, ICellStyle dataStyle)
{
int rowIndex = 1;
foreach (DataRow dataRow in sampleDataTable.Rows)
{
if (dataRow["序号"].ToString() == AVG_ROW_LABEL)
continue;
IRow row = sheet.CreateRow(rowIndex++);
row.CreateCell(0).SetCellValue(dataRow["序号"].ToString());
for (int i = 1; i <= currentSampleCount; i++)
{
row.CreateCell(i).SetCellValue(Convert.ToDouble(dataRow[$"试样{i}"]));
}
}
}
///
/// 添加平均值行
///
private void AddAverageRow(ISheet sheet, ICellStyle avgStyle)
{
var dataRows = sampleDataTable.AsEnumerable()
.Where(r => r.Field("序号") != AVG_ROW_LABEL);
if (!dataRows.Any())
return;
int rowIndex = sheet.LastRowNum + 1;
IRow avgRow = sheet.CreateRow(rowIndex);
// 序号列
ICell labelCell = avgRow.CreateCell(0);
labelCell.SetCellValue(AVG_ROW_LABEL);
labelCell.CellStyle = avgStyle;
// 计算总平均值
double overallAvg = CalculateOverallAverage();
// 在第一个试样列显示总平均值
ICell avgCell = avgRow.CreateCell(1);
avgCell.SetCellValue(overallAvg);
avgCell.CellStyle = avgStyle;
// 合并所有试样列
if (currentSampleCount > 1)
{
sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, currentSampleCount));
}
}
///
/// 自动调整列宽
///
private void AutoSizeColumns(ISheet sheet, int columnCount)
{
for (int i = 0; i <= currentSampleCount; i++)
{
sheet.AutoSizeColumn(i);
}
}
///
/// 保存工作簿
///
private void SaveWorkbook(IWorkbook workbook, string filePath)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
workbook.Write(fs);
}
}
///
/// 生成报表(带格式的 Excel)
///
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);
// 创建数据行
CreateReportDataRows(sheet, styles.dataStyle, styles.yellowStyle);
// 设置列宽
SetReportColumnWidths(sheet);
// 保存文件
SaveWorkbook(workbook, filePath);
ShowMessage($"报表已成功生成:{filePath}", "生成成功");
}
catch (Exception ex)
{
ShowMessage($"生成报表失败:{ex.Message}", "错误", MessageBoxIcon.Error);
}
}
///
/// 创建报表样式
///
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);
}
///
/// 设置单元格边框
///
private void SetBorders(ICellStyle style)
{
style.BorderBottom = NPOIBorderStyle.Thin;
style.BorderTop = NPOIBorderStyle.Thin;
style.BorderLeft = NPOIBorderStyle.Thin;
style.BorderRight = NPOIBorderStyle.Thin;
}
///
/// 创建报表标题
///
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));
}
///
/// 创建报表表头
///
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;
}
// 不再添加"根据标准"列
}
///
/// 创建报表数据行
///
private void CreateReportDataRows(ISheet sheet, ICellStyle dataStyle, ICellStyle yellowStyle)
{
int rowIndex = 3;
// 从数据表中获取时间行数据
var timeRows = sampleDataTable.AsEnumerable()
.Where(r => r.Field("序号") == TIME_ROW_LABEL);
if (timeRows.Any())
{
foreach (var dataRow in timeRows)
{
IRow timeRow = sheet.CreateRow(rowIndex++);
ICell labelCell = timeRow.CreateCell(0);
labelCell.SetCellValue(TIME_ROW_LABEL);
labelCell.CellStyle = dataStyle;
for (int i = 1; i <= currentSampleCount; i++)
{
ICell cell = timeRow.CreateCell(i);
cell.SetCellValue(dataRow.Field($"试样{i}"));
cell.CellStyle = yellowStyle;
}
}
}
// 平均时间行
IRow avgRow = sheet.CreateRow(rowIndex);
ICell avgLabelCell = avgRow.CreateCell(0);
avgLabelCell.SetCellValue(AVG_ROW_LABEL);
avgLabelCell.CellStyle = dataStyle;
// 计算并显示总平均值
double overallAvg = CalculateOverallAverage();
ICell avgCell = avgRow.CreateCell(1);
avgCell.SetCellValue(overallAvg);
avgCell.CellStyle = yellowStyle;
// 合并所有试样列
if (currentSampleCount > 1)
{
sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, currentSampleCount));
}
}
///
/// 设置报表列宽
///
private void SetReportColumnWidths(ISheet sheet)
{
sheet.SetColumnWidth(0, 20 * 256);
for (int i = 1; i <= currentSampleCount; i++)
{
sheet.SetColumnWidth(i, 15 * 256);
}
}
///
/// 清空数据
///
public void ClearData()
{
sampleDataTable.Clear();
sampleCount = 0;
UpdateDisplay();
}
}
}