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_AVG_ABSORPTION = "液体吸收量平均值(%)"; private const string ROW_MAX_ABSORPTION = "液体吸收量最大值(%)"; #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(); } /// /// 初始化 DataGridView 列 /// 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(); } } /// /// 单元格格式化事件 /// 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 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 Button2_Click(object sender, EventArgs e) { 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 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(object)); } } /// /// 设置试样数量并重新初始化 /// public void SetSampleCount(int count) { if (count < 1 || count > 20) // 限制最大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) { // 清除旧的计算行 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); } /// /// 移除计算行 /// private void RemoveCalculatedRows() { var rowsToRemove = sampleDataTable.AsEnumerable() .Where(r => r.Field("序号") == ROW_ABSORPTION || r.Field("序号") == ROW_AVG_ABSORPTION || r.Field("序号") == ROW_MAX_ABSORPTION) .ToList(); foreach (var row in rowsToRemove) { sampleDataTable.Rows.Remove(row); } } /// /// 模拟从寄存器读取数据 /// private double ReadRegisterData(int registerAddress) { return MIN_WEIGHT + random.NextDouble() * (MAX_WEIGHT - MIN_WEIGHT); } /// /// 生成模拟测试数据 /// public void GenerateMockData() { sampleDataTable.Clear(); sampleCount = 0; // 生成模拟数据 double[] initialWeights = new double[currentSampleCount]; double[] afterWeights = new double[currentSampleCount]; for (int i = 0; i < currentSampleCount; i++) { initialWeights[i] = Math.Round(MIN_WEIGHT + random.NextDouble() * (MAX_WEIGHT - MIN_WEIGHT), 2); afterWeights[i] = Math.Round(initialWeights[i] * (1.2 + random.NextDouble() * 0.3), 2); // 浸润后增重20%-50% } sampleCount = 1; UpdateDisplay(initialWeights, afterWeights); ShowMessage($"已生成 {currentSampleCount} 个试样的模拟数据", "模拟数据生成"); } /// /// 更新界面显示 /// 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 avgRow = sampleDataTable.NewRow(); avgRow["序号"] = ROW_AVG_ABSORPTION; double avgAbsorption = absorptions.Average(); double stdDev = CalculateStandardDeviation(absorptions); // 第1列显示平均值 avgRow["试样1"] = avgAbsorption; // 第2列显示标准偏差 if (count >= 2) { avgRow["试样2"] = stdDev; } // 其他列为空 for (int i = 3; i <= count; i++) { avgRow[$"试样{i}"] = DBNull.Value; } sampleDataTable.Rows.Add(avgRow); // 5. 液体吸收量最大值行 DataRow maxRow = sampleDataTable.NewRow(); maxRow["序号"] = ROW_MAX_ABSORPTION; double maxAbsorption = absorptions.Max(); // 第1列显示最大值 maxRow["试样1"] = maxAbsorption; // 其他列为空 for (int i = 2; i <= count; i++) { maxRow[$"试样{i}"] = DBNull.Value; } sampleDataTable.Rows.Add(maxRow); // 刷新界面 RefreshDataGridView(); } /// /// 计算标准偏差 /// private double CalculateStandardDeviation(double[] values) { if (values.Length == 0) return 0; double avg = values.Average(); double sumOfSquares = values.Sum(val => Math.Pow(val - avg, 2)); return Math.Sqrt(sumOfSquares / values.Length); } /// /// 刷新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) /// 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); } } /// /// 创建报表样式 /// 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 FillReportData(ISheet sheet, ICellStyle dataStyle, ICellStyle yellowStyle) { int rowIndex = 3; foreach (DataRow dataRow in sampleDataTable.Rows) { IRow row = sheet.CreateRow(rowIndex++); string rowName = dataRow["序号"].ToString(); // 序号列 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; } } } /// /// 设置报表列宽 /// private void SetReportColumnWidths(ISheet sheet) { sheet.SetColumnWidth(0, 25 * 256); for (int i = 1; i <= currentSampleCount; i++) { sheet.SetColumnWidth(i, 18 * 256); } } /// /// 保存工作簿 /// private void SaveWorkbook(IWorkbook workbook, string filePath) { using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { workbook.Write(fs); } } /// /// 清空数据 /// public void ClearData() { sampleDataTable.Clear(); sampleCount = 0; RefreshDataGridView(); } } }