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(); } } }