2025-12-31 09:43:35 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Data;
|
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
using System.Windows.Forms;
|
2025-12-31 17:26:27 +08:00
|
|
|
|
using System.IO;
|
2026-01-03 18:55:39 +08:00
|
|
|
|
using System.IO.Ports;
|
|
|
|
|
|
using System.Linq;
|
2025-12-31 17:26:27 +08:00
|
|
|
|
using NPOI.SS.UserModel;
|
|
|
|
|
|
using NPOI.XSSF.UserModel;
|
|
|
|
|
|
using NPOI.SS.Util;
|
|
|
|
|
|
using NPOIBorderStyle = NPOI.SS.UserModel.BorderStyle;
|
|
|
|
|
|
using NPOIHorizontalAlignment = NPOI.SS.UserModel.HorizontalAlignment;
|
2026-01-03 18:55:39 +08:00
|
|
|
|
using Modbus.Device;
|
2025-12-31 09:43:35 +08:00
|
|
|
|
|
|
|
|
|
|
namespace WindowsFormsApp6
|
|
|
|
|
|
{
|
|
|
|
|
|
public partial class MainForm : Form
|
2025-12-31 17:26:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
private Form1 form1Instance;
|
|
|
|
|
|
private Form2 form2Instance;
|
|
|
|
|
|
private Form3 form3Instance;
|
2026-01-03 15:59:38 +08:00
|
|
|
|
private System.Windows.Forms.Timer timeUpdateTimer;
|
2026-01-03 18:55:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 串口和Modbus相关
|
|
|
|
|
|
private SerialPort _serialPort;
|
|
|
|
|
|
private IModbusMaster _modbusMaster;
|
|
|
|
|
|
private System.Windows.Forms.Timer _readTimer;
|
2025-12-31 09:43:35 +08:00
|
|
|
|
|
|
|
|
|
|
public MainForm()
|
|
|
|
|
|
{
|
|
|
|
|
|
InitializeComponent();
|
2025-12-31 17:26:27 +08:00
|
|
|
|
InitializeTabControl();
|
|
|
|
|
|
InitializeEmbeddedForms();
|
2026-01-03 15:59:38 +08:00
|
|
|
|
InitializeTimeUpdate();
|
2026-01-03 18:55:39 +08:00
|
|
|
|
InitializeSerialPortAndModbus();
|
2026-01-03 15:59:38 +08:00
|
|
|
|
CenterInfoControls();
|
2025-12-31 17:26:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-03 15:59:38 +08:00
|
|
|
|
private void InitializeTimeUpdate()
|
2025-12-31 17:26:27 +08:00
|
|
|
|
{
|
2026-01-03 15:59:38 +08:00
|
|
|
|
// 初始化时间显示
|
|
|
|
|
|
textBox7.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
|
textBox7.ReadOnly = true; // 设置为只读
|
|
|
|
|
|
|
|
|
|
|
|
// 创建计时器,每秒更新一次
|
|
|
|
|
|
timeUpdateTimer = new System.Windows.Forms.Timer();
|
|
|
|
|
|
timeUpdateTimer.Interval = 1000;
|
|
|
|
|
|
timeUpdateTimer.Tick += (s, e) => textBox7.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
|
timeUpdateTimer.Start();
|
2025-12-31 17:26:27 +08:00
|
|
|
|
}
|
2025-12-31 09:43:35 +08:00
|
|
|
|
|
2026-01-03 18:55:39 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化串口和Modbus通信
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void InitializeSerialPortAndModbus()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 初始化读取定时器(信号量触发模式)
|
|
|
|
|
|
_readTimer = new System.Windows.Forms.Timer();
|
|
|
|
|
|
_readTimer.Interval = 100; // 100ms检查一次信号量
|
|
|
|
|
|
_readTimer.Tick += ReadTimer_Tick;
|
|
|
|
|
|
|
2026-01-04 14:53:08 +08:00
|
|
|
|
// 尝试打开默认串口
|
|
|
|
|
|
if (!TryOpenSerialPort("COM2"))
|
2026-01-03 18:55:39 +08:00
|
|
|
|
{
|
2026-01-04 14:53:08 +08:00
|
|
|
|
// 如果默认串口失败,弹出端口选择对话框
|
|
|
|
|
|
ShowPortSelectionDialog();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-03 18:55:39 +08:00
|
|
|
|
|
2026-01-04 14:53:08 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 尝试打开指定的串口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool TryOpenSerialPort(string portName)
|
|
|
|
|
|
{
|
2026-01-03 18:55:39 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-01-04 14:53:08 +08:00
|
|
|
|
// 如果已有串口打开,先关闭
|
|
|
|
|
|
if (_serialPort != null && _serialPort.IsOpen)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
{
|
2026-01-04 14:53:08 +08:00
|
|
|
|
_serialPort.Close();
|
|
|
|
|
|
_serialPort.Dispose();
|
2026-01-03 18:55:39 +08:00
|
|
|
|
}
|
2026-01-04 14:53:08 +08:00
|
|
|
|
|
|
|
|
|
|
// 配置串口参数
|
|
|
|
|
|
_serialPort = new SerialPort
|
|
|
|
|
|
{
|
|
|
|
|
|
PortName = portName,
|
|
|
|
|
|
BaudRate = 19200,
|
|
|
|
|
|
DataBits = 8,
|
|
|
|
|
|
Parity = Parity.None,
|
|
|
|
|
|
StopBits = StopBits.One,
|
|
|
|
|
|
ReadTimeout = 500,
|
|
|
|
|
|
WriteTimeout = 500,
|
|
|
|
|
|
RtsEnable = false,
|
|
|
|
|
|
DtrEnable = false
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 打开串口
|
|
|
|
|
|
_serialPort.Open();
|
|
|
|
|
|
|
|
|
|
|
|
// 创建Modbus RTU主站
|
|
|
|
|
|
_modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort);
|
|
|
|
|
|
_modbusMaster.Transport.WaitToRetryMilliseconds = 200;
|
|
|
|
|
|
_modbusMaster.Transport.Retries = 1;
|
|
|
|
|
|
_modbusMaster.Transport.ReadTimeout = 500;
|
|
|
|
|
|
|
|
|
|
|
|
// 启动定时读取
|
|
|
|
|
|
_readTimer.Start();
|
|
|
|
|
|
|
|
|
|
|
|
ShowInfoMsg($"串口 {portName} 初始化成功");
|
|
|
|
|
|
return true;
|
2026-01-03 18:55:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-01-04 14:53:08 +08:00
|
|
|
|
System.Diagnostics.Debug.WriteLine($"打开串口 {portName} 失败:{ex.Message}");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 显示端口选择对话框
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ShowPortSelectionDialog()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取当前计算机所有可用串口
|
|
|
|
|
|
string[] ports = SerialPort.GetPortNames();
|
|
|
|
|
|
|
|
|
|
|
|
if (ports == null || ports.Length == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowErrorMsg("未检测到可用的串口,请检查串口连接。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建端口选择对话框
|
|
|
|
|
|
Form portDialog = new Form
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "选择串口",
|
|
|
|
|
|
Width = 350,
|
|
|
|
|
|
Height = 200,
|
|
|
|
|
|
StartPosition = FormStartPosition.CenterParent,
|
|
|
|
|
|
FormBorderStyle = FormBorderStyle.FixedDialog,
|
|
|
|
|
|
MaximizeBox = false,
|
|
|
|
|
|
MinimizeBox = false
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Label label = new Label
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "串口初始化失败,请选择可用的串口:",
|
|
|
|
|
|
Location = new Point(20, 20),
|
|
|
|
|
|
AutoSize = true
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
ComboBox comboBox = new ComboBox
|
|
|
|
|
|
{
|
|
|
|
|
|
Location = new Point(20, 50),
|
|
|
|
|
|
Width = 290,
|
|
|
|
|
|
DropDownStyle = ComboBoxStyle.DropDownList
|
|
|
|
|
|
};
|
|
|
|
|
|
comboBox.Items.AddRange(ports);
|
|
|
|
|
|
if (comboBox.Items.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
comboBox.SelectedIndex = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Button btnOK = new Button
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "确定",
|
|
|
|
|
|
DialogResult = DialogResult.OK,
|
|
|
|
|
|
Location = new Point(140, 100),
|
|
|
|
|
|
Width = 80
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Button btnCancel = new Button
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "取消",
|
|
|
|
|
|
DialogResult = DialogResult.Cancel,
|
|
|
|
|
|
Location = new Point(230, 100),
|
|
|
|
|
|
Width = 80
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
portDialog.Controls.AddRange(new Control[] { label, comboBox, btnOK, btnCancel });
|
|
|
|
|
|
portDialog.AcceptButton = btnOK;
|
|
|
|
|
|
portDialog.CancelButton = btnCancel;
|
|
|
|
|
|
|
|
|
|
|
|
// 显示对话框
|
|
|
|
|
|
if (portDialog.ShowDialog(this) == DialogResult.OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
string selectedPort = comboBox.SelectedItem?.ToString();
|
|
|
|
|
|
if (!string.IsNullOrEmpty(selectedPort))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TryOpenSerialPort(selectedPort))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 成功打开
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 选择的端口也失败,询问是否重新选择
|
|
|
|
|
|
var result = MessageBox.Show(
|
|
|
|
|
|
$"打开串口 {selectedPort} 失败,是否重新选择?",
|
|
|
|
|
|
"错误",
|
|
|
|
|
|
MessageBoxButtons.YesNo,
|
|
|
|
|
|
MessageBoxIcon.Error);
|
|
|
|
|
|
|
|
|
|
|
|
if (result == DialogResult.Yes)
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowPortSelectionDialog(); // 递归调用,重新选择
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowErrorMsg("未选择串口,数据采集功能将不可用。");
|
2026-01-03 18:55:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 定时读取Modbus数据(信号量触发模式)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ReadTimer_Tick(object sender, EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_modbusMaster == null || _serialPort == null || !_serialPort.IsOpen)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
byte slaveId = 1; // PLC从站地址
|
|
|
|
|
|
|
|
|
|
|
|
// 读取三个信号量(M103, M252, M310)
|
|
|
|
|
|
// Modbus地址:M103 = 103, M252 = 252, M310 = 310
|
|
|
|
|
|
// 使用ReadCoils读取线圈状态(M区)
|
|
|
|
|
|
|
|
|
|
|
|
bool signalM103 = ReadCoil(slaveId, 103); // Form1信号量
|
|
|
|
|
|
bool signalM252 = ReadCoil(slaveId, 252); // Form2信号量
|
|
|
|
|
|
bool signalM310 = ReadCoil(slaveId, 310); // Form3信号量
|
|
|
|
|
|
|
|
|
|
|
|
// 根据信号量触发相应的数据读取
|
|
|
|
|
|
if (signalM103)
|
|
|
|
|
|
{
|
|
|
|
|
|
ReadForm1Data(slaveId);
|
|
|
|
|
|
// 读取完成后清除信号量
|
|
|
|
|
|
WriteCoil(slaveId, 103, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (signalM252)
|
|
|
|
|
|
{
|
|
|
|
|
|
ReadForm2Data(slaveId);
|
|
|
|
|
|
// 读取完成后清除信号量
|
|
|
|
|
|
WriteCoil(slaveId, 252, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (signalM310)
|
|
|
|
|
|
{
|
|
|
|
|
|
ReadForm3Data(slaveId);
|
|
|
|
|
|
// 读取完成后清除信号量
|
|
|
|
|
|
WriteCoil(slaveId, 310, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 读取失败时不弹窗,避免频繁打扰用户
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"Modbus读取失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 读取单个线圈状态
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool ReadCoil(byte slaveId, ushort address)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
bool[] coils = _modbusMaster.ReadCoils(slaveId, address, 1);
|
|
|
|
|
|
return coils[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 写入单个线圈状态
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void WriteCoil(byte slaveId, ushort address, bool value)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_modbusMaster.WriteSingleCoil(slaveId, address, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"写入线圈失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 读取Form1数据(液体吸收时间)
|
|
|
|
|
|
/// PLC地址:D200 - 时间(s)
|
|
|
|
|
|
/// 信号量:M103
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ReadForm1Data(byte slaveId)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 读取D200寄存器(时间数据)
|
|
|
|
|
|
// 假设有5个试样,每个试样1个时间值
|
|
|
|
|
|
ushort[] registers = _modbusMaster.ReadHoldingRegisters(slaveId, 200, 5);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用反射获取Form1的私有字段
|
|
|
|
|
|
var sampleDataTableField = form1Instance.GetType()
|
|
|
|
|
|
.GetField("sampleDataTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
|
|
|
|
|
|
if (sampleDataTableField != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
DataTable dataTable = sampleDataTableField.GetValue(form1Instance) as DataTable;
|
|
|
|
|
|
|
|
|
|
|
|
if (dataTable != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 清除旧的平均值行
|
|
|
|
|
|
var avgRows = dataTable.Select("序号 = '平均时间(s)'");
|
|
|
|
|
|
foreach (var row in avgRows)
|
|
|
|
|
|
{
|
|
|
|
|
|
dataTable.Rows.Remove(row);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新数据行
|
|
|
|
|
|
DataRow dataRow = dataTable.NewRow();
|
|
|
|
|
|
dataRow["序号"] = "时间(s)";
|
|
|
|
|
|
|
|
|
|
|
|
// 填充时间数据(将寄存器值转换为实际时间,假设寄存器值为整数秒*10)
|
|
|
|
|
|
for (int i = 0; i < Math.Min(registers.Length, 5); i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
double timeValue = registers[i] / 10.0; // 转换为秒
|
|
|
|
|
|
dataRow[$"试样{i + 1}"] = timeValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dataTable.Rows.Add(dataRow);
|
|
|
|
|
|
|
|
|
|
|
|
// 计算并添加平均值行
|
|
|
|
|
|
double totalAvg = 0;
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
for (int i = 0; i < Math.Min(registers.Length, 5); i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
totalAvg += registers[i] / 10.0;
|
|
|
|
|
|
count++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
DataRow avgRow = dataTable.NewRow();
|
|
|
|
|
|
avgRow["序号"] = "平均时间(s)";
|
|
|
|
|
|
avgRow["试样1"] = totalAvg / count;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 2; i <= 5; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
avgRow[$"试样{i}"] = DBNull.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dataTable.Rows.Add(avgRow);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新显示
|
|
|
|
|
|
dataTable.AcceptChanges();
|
|
|
|
|
|
this.Invoke(new Action(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 触发Form1的界面刷新
|
|
|
|
|
|
var refreshMethod = form1Instance.GetType()
|
|
|
|
|
|
.GetMethod("RefreshDataGridView", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
refreshMethod?.Invoke(form1Instance, null);
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"Form1数据读取成功:{registers.Length}个寄存器");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"读取Form1数据失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 读取Form2数据(液体吸收量)
|
2026-01-04 10:47:39 +08:00
|
|
|
|
/// PLC地址:D420 - 初始重量, D422 - 浸润后重量, D402 - 浸润时间, D406 - 悬挂时间, D310 - 运行速度
|
2026-01-03 18:55:39 +08:00
|
|
|
|
/// 信号量:M252
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ReadForm2Data(byte slaveId)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 读取初始重量(D420开始,5个试样)
|
|
|
|
|
|
ushort[] initialWeights = _modbusMaster.ReadHoldingRegisters(slaveId, 420, 5);
|
|
|
|
|
|
|
|
|
|
|
|
// 读取浸润后重量(D422开始,5个试样)
|
|
|
|
|
|
ushort[] afterWeights = _modbusMaster.ReadHoldingRegisters(slaveId, 422, 5);
|
|
|
|
|
|
|
2026-01-04 10:47:39 +08:00
|
|
|
|
// 读取浸润时间(D402开始,5个试样)
|
|
|
|
|
|
ushort[] soakTimes = _modbusMaster.ReadHoldingRegisters(slaveId, 402, 5);
|
2026-01-03 18:55:39 +08:00
|
|
|
|
|
2026-01-04 10:47:39 +08:00
|
|
|
|
// 读取悬挂时间(D406开始,5个试样)
|
|
|
|
|
|
ushort[] hangTimes = _modbusMaster.ReadHoldingRegisters(slaveId, 406, 5);
|
|
|
|
|
|
|
|
|
|
|
|
// 读取运行速度(D310开始,5个试样)
|
|
|
|
|
|
ushort[] runSpeeds = _modbusMaster.ReadHoldingRegisters(slaveId, 310, 5);
|
2026-01-03 18:55:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 使用反射获取Form2的私有字段
|
|
|
|
|
|
var sampleDataTableField = form2Instance.GetType()
|
|
|
|
|
|
.GetField("sampleDataTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
|
|
|
|
|
|
if (sampleDataTableField != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
DataTable dataTable = sampleDataTableField.GetValue(form2Instance) as DataTable;
|
|
|
|
|
|
|
|
|
|
|
|
if (dataTable != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
dataTable.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
int count = Math.Min(initialWeights.Length, 5);
|
|
|
|
|
|
|
|
|
|
|
|
// 转换数据(假设寄存器值为实际值*100)
|
|
|
|
|
|
double[] initialWeightValues = new double[count];
|
|
|
|
|
|
double[] afterWeightValues = new double[count];
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
initialWeightValues[i] = initialWeights[i] / 100.0;
|
|
|
|
|
|
afterWeightValues[i] = afterWeights[i] / 100.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 初始重量行
|
|
|
|
|
|
DataRow initialRow = dataTable.NewRow();
|
|
|
|
|
|
initialRow["序号"] = "初始重量(g)";
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
initialRow[$"试样{i + 1}"] = initialWeightValues[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(initialRow);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 浸润后重量行
|
|
|
|
|
|
DataRow afterRow = dataTable.NewRow();
|
|
|
|
|
|
afterRow["序号"] = "浸润后重量(g)";
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
afterRow[$"试样{i + 1}"] = afterWeightValues[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(afterRow);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 液体吸收量行 (%)
|
|
|
|
|
|
DataRow absorptionRow = dataTable.NewRow();
|
|
|
|
|
|
absorptionRow["序号"] = "液体吸收量(%)";
|
|
|
|
|
|
double[] absorptions = new double[count];
|
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
absorptions[i] = ((afterWeightValues[i] - initialWeightValues[i]) / initialWeightValues[i]) * 100;
|
|
|
|
|
|
absorptionRow[$"试样{i + 1}"] = absorptions[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(absorptionRow);
|
|
|
|
|
|
|
2026-01-04 10:47:39 +08:00
|
|
|
|
// 4. 浸润时间行(每个试样独立的浸润时间)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
DataRow soakTimeRow = dataTable.NewRow();
|
|
|
|
|
|
soakTimeRow["序号"] = "浸润时间";
|
2026-01-04 10:47:39 +08:00
|
|
|
|
for (int i = 0; i < count; i++)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
{
|
2026-01-04 10:47:39 +08:00
|
|
|
|
soakTimeRow[$"试样{i + 1}"] = $"{soakTimes[i]}s";
|
2026-01-03 18:55:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(soakTimeRow);
|
|
|
|
|
|
|
2026-01-04 10:47:39 +08:00
|
|
|
|
// 5. 悬挂时间行(每个试样独立的悬挂时间)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
DataRow hangTimeRow = dataTable.NewRow();
|
|
|
|
|
|
hangTimeRow["序号"] = "悬挂时间";
|
2026-01-04 10:47:39 +08:00
|
|
|
|
for (int i = 0; i < count; i++)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
{
|
2026-01-04 10:47:39 +08:00
|
|
|
|
hangTimeRow[$"试样{i + 1}"] = $"{hangTimes[i]}s";
|
2026-01-03 18:55:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(hangTimeRow);
|
|
|
|
|
|
|
2026-01-04 10:47:39 +08:00
|
|
|
|
// 6. 运行速度行(每个试样独立的运行速度)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
DataRow runSpeedRow = dataTable.NewRow();
|
|
|
|
|
|
runSpeedRow["序号"] = "运行速度";
|
2026-01-04 10:47:39 +08:00
|
|
|
|
for (int i = 0; i < count; i++)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
{
|
2026-01-04 10:47:39 +08:00
|
|
|
|
runSpeedRow[$"试样{i + 1}"] = $"{runSpeeds[i]}mm/min";
|
2026-01-03 18:55:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(runSpeedRow);
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 液体吸收量平均值行
|
|
|
|
|
|
DataRow avgRow = dataTable.NewRow();
|
|
|
|
|
|
avgRow["序号"] = "液体吸收量平均值(%)";
|
|
|
|
|
|
double avgAbsorption = absorptions.Average();
|
|
|
|
|
|
avgRow["试样1"] = avgAbsorption;
|
|
|
|
|
|
for (int i = 2; i <= count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
avgRow[$"试样{i}"] = DBNull.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(avgRow);
|
|
|
|
|
|
|
|
|
|
|
|
// 8. 液体吸收量最大值行
|
|
|
|
|
|
DataRow maxRow = dataTable.NewRow();
|
|
|
|
|
|
maxRow["序号"] = "液体吸收量最大值(%)";
|
|
|
|
|
|
double maxAbsorption = absorptions.Max();
|
|
|
|
|
|
maxRow["试样1"] = maxAbsorption;
|
|
|
|
|
|
for (int i = 2; i <= count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
maxRow[$"试样{i}"] = DBNull.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(maxRow);
|
|
|
|
|
|
|
|
|
|
|
|
// 9. 标准偏差行
|
|
|
|
|
|
DataRow stdDevRow = dataTable.NewRow();
|
|
|
|
|
|
stdDevRow["序号"] = "标准偏差";
|
|
|
|
|
|
double stdDev = CalculateStandardDeviation(absorptions);
|
|
|
|
|
|
stdDevRow["试样1"] = stdDev;
|
|
|
|
|
|
for (int i = 2; i <= count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
stdDevRow[$"试样{i}"] = DBNull.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
dataTable.Rows.Add(stdDevRow);
|
|
|
|
|
|
|
|
|
|
|
|
// 刷新显示
|
|
|
|
|
|
dataTable.AcceptChanges();
|
|
|
|
|
|
this.Invoke(new Action(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var refreshMethod = form2Instance.GetType()
|
|
|
|
|
|
.GetMethod("RefreshDataGridView", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
refreshMethod?.Invoke(form2Instance, null);
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine("Form2数据读取成功");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"读取Form2数据失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 读取Form3数据(液体芯吸速率)
|
2026-01-04 10:13:59 +08:00
|
|
|
|
/// PLC地址:D212 - 吸水时间(s)
|
2026-01-03 18:55:39 +08:00
|
|
|
|
/// 信号量:M310
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ReadForm3Data(byte slaveId)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-01-04 10:13:59 +08:00
|
|
|
|
// 读取吸水时间(D212开始,5个试样 * 3次测试 = 15个寄存器)
|
|
|
|
|
|
ushort[] wickingTimes = _modbusMaster.ReadHoldingRegisters(slaveId, 212, 15);
|
2026-01-03 18:55:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 使用反射获取Form3的私有字段
|
|
|
|
|
|
var sampleDataTableField = form3Instance.GetType()
|
|
|
|
|
|
.GetField("sampleDataTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
|
|
|
|
|
|
if (sampleDataTableField != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
DataTable dataTable = sampleDataTableField.GetValue(form3Instance) as DataTable;
|
|
|
|
|
|
|
|
|
|
|
|
if (dataTable != null && dataTable.Rows.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取第一行(吸水时间行)
|
|
|
|
|
|
DataRow timeRow = dataTable.Rows[0];
|
|
|
|
|
|
|
|
|
|
|
|
// 填充吸水时间数据(假设寄存器值为实际时间*10)
|
|
|
|
|
|
int registerIndex = 0;
|
|
|
|
|
|
for (int i = 1; i <= 5; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (registerIndex < wickingTimes.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
timeRow[$"试样{i}_1"] = wickingTimes[registerIndex++] / 10.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (registerIndex < wickingTimes.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
timeRow[$"试样{i}_2"] = wickingTimes[registerIndex++] / 10.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (registerIndex < wickingTimes.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
timeRow[$"试样{i}_3"] = wickingTimes[registerIndex++] / 10.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调用Form3的计算方法
|
|
|
|
|
|
this.Invoke(new Action(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var calculateMethod = form3Instance.GetType()
|
|
|
|
|
|
.GetMethod("CalculateAllRows", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
calculateMethod?.Invoke(form3Instance, null);
|
|
|
|
|
|
|
|
|
|
|
|
var refreshMethod = form3Instance.GetType()
|
|
|
|
|
|
.GetMethod("RefreshDataGridView", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
refreshMethod?.Invoke(form3Instance, null);
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine("Form3数据读取成功");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"读取Form3数据失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 计算标准偏差
|
|
|
|
|
|
/// </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));
|
|
|
|
|
|
return Math.Sqrt(sumOfSquares / (values.Length - 1));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 显示错误消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ShowErrorMsg(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show(message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 显示信息消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ShowInfoMsg(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show(message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:26:27 +08:00
|
|
|
|
private void InitializeTabControl()
|
|
|
|
|
|
{
|
|
|
|
|
|
tabControl1.SelectedIndexChanged += TabControl1_SelectedIndexChanged;
|
|
|
|
|
|
}
|
2025-12-31 09:43:35 +08:00
|
|
|
|
|
2025-12-31 17:26:27 +08:00
|
|
|
|
private void TabControl1_SelectedIndexChanged(object sender, EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateTitleForCurrentTab();
|
2025-12-31 17:41:01 +08:00
|
|
|
|
|
|
|
|
|
|
// 根据选中的 Tab 显示/隐藏切换布局按钮
|
|
|
|
|
|
// 只有在 Form3 (Tab 2, index=2) 时显示切换按钮
|
|
|
|
|
|
buttonToggleLayout.Visible = (tabControl1.SelectedIndex == 2);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新切换按钮的文本
|
|
|
|
|
|
if (tabControl1.SelectedIndex == 2 && form3Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateToggleButtonText();
|
|
|
|
|
|
}
|
2025-12-31 09:43:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:26:27 +08:00
|
|
|
|
private void UpdateTitleForCurrentTab()
|
2025-12-31 09:43:35 +08:00
|
|
|
|
{
|
2025-12-31 17:26:27 +08:00
|
|
|
|
switch (tabControl1.SelectedIndex)
|
2025-12-31 09:43:35 +08:00
|
|
|
|
{
|
2025-12-31 17:26:27 +08:00
|
|
|
|
case 0:
|
|
|
|
|
|
label1.Text = " 液体吸收时间测试报告";
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
label1.Text = " 液体吸收量测试报告";
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
label1.Text = " 液体芯吸速率测试报告";
|
|
|
|
|
|
break;
|
2025-12-31 09:43:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:26:27 +08:00
|
|
|
|
private void InitializeEmbeddedForms()
|
2025-12-31 09:43:35 +08:00
|
|
|
|
{
|
2025-12-31 17:26:27 +08:00
|
|
|
|
form1Instance = new Form1();
|
|
|
|
|
|
form1Instance.TopLevel = false;
|
|
|
|
|
|
form1Instance.FormBorderStyle = FormBorderStyle.None;
|
|
|
|
|
|
form1Instance.Dock = DockStyle.Fill;
|
|
|
|
|
|
|
|
|
|
|
|
Panel panel1 = new Panel();
|
|
|
|
|
|
panel1.Dock = DockStyle.Fill;
|
|
|
|
|
|
panel1.Controls.Add(form1Instance.Controls["tableLayoutPanel1"].Controls["panel3"]);
|
|
|
|
|
|
tabPage1.Controls.Add(panel1);
|
|
|
|
|
|
form1Instance.Show();
|
|
|
|
|
|
|
|
|
|
|
|
form2Instance = new Form2();
|
|
|
|
|
|
form2Instance.TopLevel = false;
|
|
|
|
|
|
form2Instance.FormBorderStyle = FormBorderStyle.None;
|
|
|
|
|
|
form2Instance.Dock = DockStyle.Fill;
|
|
|
|
|
|
|
|
|
|
|
|
Panel panel2 = new Panel();
|
|
|
|
|
|
panel2.Dock = DockStyle.Fill;
|
|
|
|
|
|
panel2.Controls.Add(form2Instance.Controls["tableLayoutPanel1"].Controls["panel3"]);
|
|
|
|
|
|
tabPage2.Controls.Add(panel2);
|
|
|
|
|
|
form2Instance.Show();
|
|
|
|
|
|
|
|
|
|
|
|
form3Instance = new Form3();
|
|
|
|
|
|
form3Instance.TopLevel = false;
|
|
|
|
|
|
form3Instance.FormBorderStyle = FormBorderStyle.None;
|
|
|
|
|
|
form3Instance.Dock = DockStyle.Fill;
|
|
|
|
|
|
|
|
|
|
|
|
Panel panel3 = new Panel();
|
|
|
|
|
|
panel3.Dock = DockStyle.Fill;
|
|
|
|
|
|
panel3.Controls.Add(form3Instance.Controls["tableLayoutPanel1"].Controls["panel3"]);
|
|
|
|
|
|
tabPage3.Controls.Add(panel3);
|
|
|
|
|
|
form3Instance.Show();
|
2025-12-31 09:43:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-03 15:59:38 +08:00
|
|
|
|
private void panelInfo_Resize(object sender, EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
CenterInfoControls();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CenterInfoControls()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 每列的宽度定义
|
|
|
|
|
|
int col1LabelWidth = 75; // 样品名称/仪器
|
|
|
|
|
|
int col1InputWidth = 140;
|
|
|
|
|
|
int col2LabelWidth = 75; // 物料编码/设备编号
|
|
|
|
|
|
int col2InputWidth = 140;
|
|
|
|
|
|
int col3LabelWidth = 75; // 批号/操作人员(操作人员需要更宽)
|
|
|
|
|
|
int col3InputWidth = 140;
|
|
|
|
|
|
int col4LabelWidth = 75; // 测试时间/数据文件
|
|
|
|
|
|
int col4InputWidth = 140;
|
|
|
|
|
|
|
|
|
|
|
|
int gap = 15; // 列之间的间距
|
|
|
|
|
|
|
|
|
|
|
|
// 计算总宽度
|
|
|
|
|
|
int totalWidth = col1LabelWidth + col1InputWidth + gap +
|
|
|
|
|
|
col2LabelWidth + col2InputWidth + gap +
|
|
|
|
|
|
col3LabelWidth + col3InputWidth + gap +
|
|
|
|
|
|
col4LabelWidth + col4InputWidth;
|
|
|
|
|
|
|
|
|
|
|
|
int startX = (panelInfo.Width - totalWidth) / 2;
|
|
|
|
|
|
|
|
|
|
|
|
// 第一行 Y 坐标
|
|
|
|
|
|
int y1 = 10;
|
|
|
|
|
|
int labelOffsetY = 3; // 标签相对输入框的垂直偏移
|
|
|
|
|
|
|
|
|
|
|
|
// 第一列:样品名称
|
|
|
|
|
|
int col1X = startX;
|
|
|
|
|
|
label3.Location = new Point(col1X, y1 + labelOffsetY);
|
|
|
|
|
|
textBox1.Location = new Point(col1X + col1LabelWidth, y1);
|
|
|
|
|
|
|
|
|
|
|
|
// 第二列:物料编码
|
|
|
|
|
|
int col2X = col1X + col1LabelWidth + col1InputWidth + gap;
|
|
|
|
|
|
label4.Location = new Point(col2X, y1 + labelOffsetY);
|
|
|
|
|
|
textBox2.Location = new Point(col2X + col2LabelWidth, y1);
|
|
|
|
|
|
|
|
|
|
|
|
// 第三列:批号
|
|
|
|
|
|
int col3X = col2X + col2LabelWidth + col2InputWidth + gap;
|
|
|
|
|
|
label5.Location = new Point(col3X, y1 + labelOffsetY);
|
|
|
|
|
|
textBox3.Location = new Point(col3X + col3LabelWidth, y1);
|
|
|
|
|
|
|
|
|
|
|
|
// 第四列:测试时间
|
|
|
|
|
|
int col4X = col3X + col3LabelWidth + col3InputWidth + gap;
|
|
|
|
|
|
label9.Location = new Point(col4X, y1 + labelOffsetY);
|
|
|
|
|
|
textBox7.Location = new Point(col4X + col4LabelWidth, y1);
|
|
|
|
|
|
|
|
|
|
|
|
// 第二行 Y 坐标
|
|
|
|
|
|
int y2 = 43;
|
|
|
|
|
|
|
|
|
|
|
|
// 第一列:仪器
|
|
|
|
|
|
label6.Location = new Point(col1X, y2 + labelOffsetY);
|
|
|
|
|
|
textBox4.Location = new Point(col1X + col1LabelWidth, y2);
|
|
|
|
|
|
|
|
|
|
|
|
// 第二列:设备编号
|
|
|
|
|
|
label7.Location = new Point(col2X, y2 + labelOffsetY);
|
|
|
|
|
|
textBox5.Location = new Point(col2X + col2LabelWidth, y2);
|
|
|
|
|
|
|
|
|
|
|
|
// 第三列:操作人员
|
|
|
|
|
|
label8.Location = new Point(col3X, y2 + labelOffsetY);
|
|
|
|
|
|
textBox6.Location = new Point(col3X + col3LabelWidth, y2);
|
|
|
|
|
|
|
|
|
|
|
|
// 第四列:数据文件
|
|
|
|
|
|
label10.Location = new Point(col4X, y2 + labelOffsetY);
|
|
|
|
|
|
textBox8.Location = new Point(col4X + col4LabelWidth, y2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:26:27 +08:00
|
|
|
|
private void buttonPrint_Click(object sender, EventArgs e)
|
2025-12-31 09:43:35 +08:00
|
|
|
|
{
|
2025-12-31 17:26:27 +08:00
|
|
|
|
MessageBox.Show("打印功能开发中", "提示");
|
2025-12-31 09:43:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:26:27 +08:00
|
|
|
|
private void buttonExport_Click(object sender, EventArgs e)
|
2025-12-31 09:43:35 +08:00
|
|
|
|
{
|
2025-12-31 17:26:27 +08:00
|
|
|
|
SaveFileDialog saveFileDialog = new SaveFileDialog
|
|
|
|
|
|
{
|
|
|
|
|
|
Filter = "Excel 文件 (*.xlsx)|*.xlsx",
|
|
|
|
|
|
FileName = $"测试报告_{DateTime.Now:yyyyMMdd_HHmmss}",
|
|
|
|
|
|
Title = "导出整合报告"
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
ExportIntegratedReport(saveFileDialog.FileName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void button5_Click(object sender, EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (tabControl1.SelectedIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
form1Instance.GenerateMockData();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
form2Instance.GenerateMockData();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
form3Instance.GenerateMockData();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:41:01 +08:00
|
|
|
|
private void buttonToggleLayout_Click(object sender, EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (form3Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 调用 Form3 的公共切换方法
|
|
|
|
|
|
form3Instance.ToggleTableLayout();
|
|
|
|
|
|
|
|
|
|
|
|
// 更新按钮文本
|
|
|
|
|
|
UpdateToggleButtonText();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新切换按钮的文本
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void UpdateToggleButtonText()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (form3Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 使用公共方法获取布局状态
|
|
|
|
|
|
bool isVertical = form3Instance.IsVerticalLayout();
|
|
|
|
|
|
buttonToggleLayout.Text = isVertical ? "🔄 横向布局" : "🔄 纵向布局";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:26:27 +08:00
|
|
|
|
private void ExportIntegratedReport(string filePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
IWorkbook workbook = new XSSFWorkbook();
|
2025-12-31 18:36:52 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建单个整合的工作表
|
|
|
|
|
|
CreateIntegratedSheet(workbook);
|
2025-12-31 17:26:27 +08:00
|
|
|
|
|
|
|
|
|
|
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
|
|
|
|
|
|
{
|
|
|
|
|
|
workbook.Write(fs);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MessageBox.Show($"导出成功:{filePath}", "成功");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show($"导出失败:{ex.Message}", "错误");
|
|
|
|
|
|
}
|
2025-12-31 09:43:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
private void CreateIntegratedSheet(IWorkbook workbook)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
ISheet sheet = workbook.CreateSheet("吸收性测试报告");
|
2025-12-31 17:54:34 +08:00
|
|
|
|
var styles = CreateReportStyles(workbook);
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 获取三个表单的数据
|
|
|
|
|
|
var sampleCountField1 = form1Instance.GetType()
|
2025-12-31 17:54:34 +08:00
|
|
|
|
.GetField("currentSampleCount", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
int sampleCount = sampleCountField1 != null ? (int)sampleCountField1.GetValue(form1Instance) : 5;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
var dataTableField1 = form1Instance.GetType()
|
2025-12-31 17:54:34 +08:00
|
|
|
|
.GetField("sampleDataTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
DataTable dataTable1 = dataTableField1?.GetValue(form1Instance) as DataTable;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
var dataTableField2 = form2Instance.GetType()
|
|
|
|
|
|
.GetField("sampleDataTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
DataTable dataTable2 = dataTableField2?.GetValue(form2Instance) as DataTable;
|
|
|
|
|
|
|
|
|
|
|
|
var dataTableField3 = form3Instance.GetType()
|
|
|
|
|
|
.GetField("sampleDataTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
|
|
DataTable dataTable3 = dataTableField3?.GetValue(form3Instance) as DataTable;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
|
|
|
|
|
int currentRow = 0;
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 1. 创建总标题
|
2025-12-31 17:54:34 +08:00
|
|
|
|
IRow titleRow = sheet.CreateRow(currentRow++);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
titleRow.Height = 800;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
ICell titleCell = titleRow.CreateCell(0);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
titleCell.SetCellValue("吸收性测试报告");
|
2025-12-31 17:54:34 +08:00
|
|
|
|
titleCell.CellStyle = styles.titleStyle;
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 为合并区域的所有单元格设置样式(总标题不需要边框,但为了一致性也设置)
|
|
|
|
|
|
for (int i = 1; i <= sampleCount + 5; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
titleRow.CreateCell(i).CellStyle = styles.titleStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, sampleCount + 5));
|
|
|
|
|
|
|
|
|
|
|
|
currentRow++; // 空行
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 创建信息输入区域
|
|
|
|
|
|
CreateInfoSection(sheet, ref currentRow, sampleCount, styles);
|
|
|
|
|
|
|
|
|
|
|
|
currentRow++; // 空行
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 创建液体吸收时间部分
|
|
|
|
|
|
CreateForm1Section(sheet, ref currentRow, sampleCount, dataTable1, styles);
|
|
|
|
|
|
|
|
|
|
|
|
currentRow++; // 空行
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 创建液体吸收量部分
|
|
|
|
|
|
CreateForm2Section(sheet, ref currentRow, sampleCount, dataTable2, styles);
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
|
|
|
|
|
currentRow++; // 空行
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 5. 创建液体芯吸速率部分
|
|
|
|
|
|
CreateForm3Section(sheet, ref currentRow, sampleCount, dataTable3, styles);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置列宽
|
|
|
|
|
|
sheet.SetColumnWidth(0, 20 * 256);
|
|
|
|
|
|
for (int i = 1; i <= sampleCount * 3; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
sheet.SetColumnWidth(i, 12 * 256);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CreateInfoSection(ISheet sheet, ref int currentRow, int sampleCount,
|
|
|
|
|
|
(ICellStyle titleStyle, ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle) styles)
|
|
|
|
|
|
{
|
2025-12-31 18:45:00 +08:00
|
|
|
|
// 从界面获取实际数据
|
|
|
|
|
|
string sampleName = textBox1.Text.Trim();
|
|
|
|
|
|
string materialCode = textBox2.Text.Trim();
|
|
|
|
|
|
string batchNumber = textBox3.Text.Trim();
|
|
|
|
|
|
string operatorName = textBox4.Text.Trim();
|
|
|
|
|
|
string instrument = textBox5.Text.Trim();
|
|
|
|
|
|
string deviceNumber = textBox6.Text.Trim();
|
|
|
|
|
|
string testTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 第一行信息
|
|
|
|
|
|
IRow infoRow1 = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
infoRow1.Height = 400;
|
|
|
|
|
|
|
|
|
|
|
|
// 样品名称
|
|
|
|
|
|
ICell cell1 = infoRow1.CreateCell(0);
|
|
|
|
|
|
cell1.SetCellValue("样品名称:");
|
|
|
|
|
|
cell1.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell2 = infoRow1.CreateCell(1);
|
2025-12-31 18:45:00 +08:00
|
|
|
|
cell2.SetCellValue(string.IsNullOrEmpty(sampleName) ? "" : sampleName);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
cell2.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
infoRow1.CreateCell(2).CellStyle = styles.yellowStyle;
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 1, 2));
|
|
|
|
|
|
|
|
|
|
|
|
// 物料编码
|
|
|
|
|
|
ICell cell3 = infoRow1.CreateCell(3);
|
|
|
|
|
|
cell3.SetCellValue("物料编码:");
|
|
|
|
|
|
cell3.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell4 = infoRow1.CreateCell(4);
|
2025-12-31 18:45:00 +08:00
|
|
|
|
cell4.SetCellValue(string.IsNullOrEmpty(materialCode) ? "" : materialCode);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
cell4.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
infoRow1.CreateCell(5).CellStyle = styles.yellowStyle;
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 4, 5));
|
|
|
|
|
|
|
|
|
|
|
|
// 批号
|
|
|
|
|
|
ICell cell5 = infoRow1.CreateCell(6);
|
|
|
|
|
|
cell5.SetCellValue("批号:");
|
|
|
|
|
|
cell5.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell6 = infoRow1.CreateCell(7);
|
2025-12-31 18:45:00 +08:00
|
|
|
|
cell6.SetCellValue(string.IsNullOrEmpty(batchNumber) ? "" : batchNumber);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
cell6.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
|
|
|
|
|
|
// 操作人员
|
|
|
|
|
|
ICell cell7 = infoRow1.CreateCell(8);
|
|
|
|
|
|
cell7.SetCellValue("操作人员:");
|
|
|
|
|
|
cell7.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell8 = infoRow1.CreateCell(9);
|
2025-12-31 18:45:00 +08:00
|
|
|
|
cell8.SetCellValue(string.IsNullOrEmpty(operatorName) ? "" : operatorName);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
cell8.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
|
|
|
|
|
|
// 第二行信息
|
|
|
|
|
|
IRow infoRow2 = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
infoRow2.Height = 400;
|
|
|
|
|
|
|
|
|
|
|
|
// 测试时间
|
|
|
|
|
|
ICell cell21 = infoRow2.CreateCell(0);
|
|
|
|
|
|
cell21.SetCellValue("测试时间:");
|
|
|
|
|
|
cell21.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell22 = infoRow2.CreateCell(1);
|
2025-12-31 18:45:00 +08:00
|
|
|
|
cell22.SetCellValue(testTime);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
cell22.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
infoRow2.CreateCell(2).CellStyle = styles.yellowStyle;
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 1, 2));
|
|
|
|
|
|
|
|
|
|
|
|
// 仪器
|
|
|
|
|
|
ICell cell23 = infoRow2.CreateCell(3);
|
|
|
|
|
|
cell23.SetCellValue("仪器:");
|
|
|
|
|
|
cell23.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell24 = infoRow2.CreateCell(4);
|
2025-12-31 18:45:00 +08:00
|
|
|
|
cell24.SetCellValue(string.IsNullOrEmpty(instrument) ? "" : instrument);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
cell24.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
infoRow2.CreateCell(5).CellStyle = styles.yellowStyle;
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 4, 5));
|
|
|
|
|
|
|
|
|
|
|
|
// 设备编号
|
|
|
|
|
|
ICell cell25 = infoRow2.CreateCell(6);
|
|
|
|
|
|
cell25.SetCellValue("设备编号:");
|
|
|
|
|
|
cell25.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell26 = infoRow2.CreateCell(7);
|
2025-12-31 18:45:00 +08:00
|
|
|
|
cell26.SetCellValue(string.IsNullOrEmpty(deviceNumber) ? "" : deviceNumber);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
cell26.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
|
|
|
|
|
|
// 数据文件
|
|
|
|
|
|
ICell cell27 = infoRow2.CreateCell(8);
|
|
|
|
|
|
cell27.SetCellValue("数据文件:");
|
|
|
|
|
|
cell27.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell cell28 = infoRow2.CreateCell(9);
|
|
|
|
|
|
cell28.SetCellValue("(报告保存路径)");
|
|
|
|
|
|
cell28.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CreateForm1Section(ISheet sheet, ref int currentRow, int sampleCount, DataTable dataTable,
|
|
|
|
|
|
(ICellStyle titleStyle, ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle) styles)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 子标题
|
|
|
|
|
|
IRow subtitleRow = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
subtitleRow.Height = 500;
|
|
|
|
|
|
ICell subtitleCell = subtitleRow.CreateCell(0);
|
|
|
|
|
|
subtitleCell.SetCellValue("液体吸收时间");
|
|
|
|
|
|
subtitleCell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
subtitleRow.CreateCell(i).CellStyle = styles.headerStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 0, sampleCount));
|
|
|
|
|
|
|
|
|
|
|
|
// 表头
|
2025-12-31 17:54:34 +08:00
|
|
|
|
IRow headerRow = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
headerRow.Height = 400;
|
|
|
|
|
|
ICell seqCell = headerRow.CreateCell(0);
|
|
|
|
|
|
seqCell.SetCellValue("序号");
|
|
|
|
|
|
seqCell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
ICell cell = headerRow.CreateCell(i);
|
|
|
|
|
|
cell.SetCellValue($"试样{i}");
|
|
|
|
|
|
cell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 数据行
|
|
|
|
|
|
if (dataTable != null && dataTable.Rows.Count > 0)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
foreach (DataRow dataRow in dataTable.Rows)
|
|
|
|
|
|
{
|
|
|
|
|
|
string rowName = dataRow["序号"]?.ToString() ?? "";
|
|
|
|
|
|
if (string.IsNullOrEmpty(rowName)) continue;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
IRow row = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
row.Height = 380;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
ICell nameCell = row.CreateCell(0);
|
|
|
|
|
|
nameCell.SetCellValue(rowName);
|
|
|
|
|
|
nameCell.CellStyle = styles.dataStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 如果是平均时间行,只显示一个合并的单元格
|
|
|
|
|
|
if (rowName.Contains("平均"))
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
ICell avgCell = row.CreateCell(1);
|
|
|
|
|
|
if (dataTable.Columns.Contains("试样1") && dataRow["试样1"] != DBNull.Value)
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
if (double.TryParse(dataRow["试样1"].ToString(), out double value))
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
avgCell.SetCellValue(value);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
avgCell.SetCellValue("");
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
avgCell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
avgCell.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
|
|
|
|
|
|
// 为合并区域的其他单元格设置样式
|
|
|
|
|
|
for (int i = 2; i <= sampleCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
row.CreateCell(i).CellStyle = styles.yellowStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 合并平均时间单元格
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 1, sampleCount));
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
2026-01-03 14:04:08 +08:00
|
|
|
|
else
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 普通数据行,显示每个试样的数据
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
ICell cell = row.CreateCell(i);
|
|
|
|
|
|
if (dataTable.Columns.Contains($"试样{i}") && dataRow[$"试样{i}"] != DBNull.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (double.TryParse(dataRow[$"试样{i}"].ToString(), out double value))
|
|
|
|
|
|
{
|
|
|
|
|
|
cell.SetCellValue(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
cell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
cell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
cell.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
}
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
private void CreateForm2Section(ISheet sheet, ref int currentRow, int sampleCount, DataTable dataTable,
|
|
|
|
|
|
(ICellStyle titleStyle, ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle) styles)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 子标题
|
|
|
|
|
|
IRow subtitleRow = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
subtitleRow.Height = 500;
|
|
|
|
|
|
ICell subtitleCell = subtitleRow.CreateCell(0);
|
|
|
|
|
|
subtitleCell.SetCellValue("液体吸收量");
|
|
|
|
|
|
subtitleCell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
2026-01-03 14:04:08 +08:00
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
subtitleRow.CreateCell(i).CellStyle = styles.headerStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
2026-01-03 14:04:08 +08:00
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 0, sampleCount));
|
2025-12-31 18:36:52 +08:00
|
|
|
|
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 表头
|
|
|
|
|
|
IRow headerRow = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
headerRow.Height = 400;
|
|
|
|
|
|
ICell seqCell = headerRow.CreateCell(0);
|
|
|
|
|
|
seqCell.SetCellValue("序号");
|
|
|
|
|
|
seqCell.CellStyle = styles.headerStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
|
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
ICell cell = headerRow.CreateCell(i);
|
2025-12-31 17:54:34 +08:00
|
|
|
|
cell.SetCellValue($"试样{i}");
|
|
|
|
|
|
cell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 数据行
|
|
|
|
|
|
if (dataTable != null && dataTable.Rows.Count > 0)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
foreach (DataRow dataRow in dataTable.Rows)
|
|
|
|
|
|
{
|
|
|
|
|
|
string rowName = dataRow["序号"]?.ToString() ?? "";
|
|
|
|
|
|
if (string.IsNullOrEmpty(rowName)) continue;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
IRow row = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
row.Height = 380;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
ICell nameCell = row.CreateCell(0);
|
|
|
|
|
|
nameCell.SetCellValue(rowName);
|
|
|
|
|
|
nameCell.CellStyle = styles.dataStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 判断是否是需要合并的行(平均值、最大值、标准偏差)
|
|
|
|
|
|
bool isMergedRow = rowName.Contains("平均值") || rowName.Contains("最大值") || rowName.Contains("标准偏差");
|
|
|
|
|
|
|
|
|
|
|
|
if (isMergedRow)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 合并为一列,只显示第一个试样的值
|
|
|
|
|
|
ICell valueCell = row.CreateCell(1);
|
|
|
|
|
|
if (dataTable.Columns.Contains("试样1") && dataRow["试样1"] != DBNull.Value)
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
object value = dataRow["试样1"];
|
2025-12-31 18:36:52 +08:00
|
|
|
|
if (value != null && double.TryParse(value.ToString(), out double numValue))
|
|
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
valueCell.SetCellValue(numValue);
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
valueCell.SetCellValue("");
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
valueCell.SetCellValue("");
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
2026-01-03 14:04:08 +08:00
|
|
|
|
valueCell.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
|
|
|
|
|
|
// 为合并区域的其他单元格设置样式
|
|
|
|
|
|
for (int i = 2; i <= sampleCount; i++)
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
row.CreateCell(i).CellStyle = styles.yellowStyle;
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 合并单元格
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 1, sampleCount));
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
2026-01-03 14:04:08 +08:00
|
|
|
|
else
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 普通数据行,显示每个试样的数据
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
ICell cell = row.CreateCell(i);
|
|
|
|
|
|
if (dataTable.Columns.Contains($"试样{i}") && dataRow[$"试样{i}"] != DBNull.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
object value = dataRow[$"试样{i}"];
|
|
|
|
|
|
if (value != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 尝试转换为 double
|
|
|
|
|
|
if (double.TryParse(value.ToString(), out double numValue))
|
|
|
|
|
|
{
|
|
|
|
|
|
cell.SetCellValue(numValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果不是数字,直接显示字符串值
|
|
|
|
|
|
cell.SetCellValue(value.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
cell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
cell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
cell.CellStyle = styles.yellowStyle;
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
private void CreateForm3Section(ISheet sheet, ref int currentRow, int sampleCount, DataTable dataTable,
|
|
|
|
|
|
(ICellStyle titleStyle, ICellStyle headerStyle, ICellStyle dataStyle, ICellStyle yellowStyle) styles)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 子标题
|
|
|
|
|
|
IRow subtitleRow = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
subtitleRow.Height = 500;
|
|
|
|
|
|
ICell subtitleCell = subtitleRow.CreateCell(0);
|
|
|
|
|
|
subtitleCell.SetCellValue("液体芯吸速率");
|
|
|
|
|
|
subtitleCell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
for (int i = 1; i <= sampleCount * 3; i++)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
subtitleRow.CreateCell(i).CellStyle = styles.headerStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
2025-12-31 18:36:52 +08:00
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 0, sampleCount * 3));
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 第一行表头(纵向/横向)
|
|
|
|
|
|
IRow headerRow1 = sheet.CreateRow(currentRow);
|
|
|
|
|
|
headerRow1.Height = 400;
|
|
|
|
|
|
ICell seqCell1 = headerRow1.CreateCell(0);
|
|
|
|
|
|
seqCell1.SetCellValue("序号");
|
|
|
|
|
|
seqCell1.CellStyle = styles.headerStyle;
|
|
|
|
|
|
|
|
|
|
|
|
ICell dirCell = headerRow1.CreateCell(1);
|
|
|
|
|
|
dirCell.SetCellValue("纵向/横向(选择按钮)");
|
|
|
|
|
|
dirCell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
for (int i = 2; i <= sampleCount * 3; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
headerRow1.CreateCell(i).CellStyle = styles.headerStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow, currentRow, 1, sampleCount * 3));
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
currentRow++;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 第二行表头(试样1-N,每个3列)
|
|
|
|
|
|
IRow headerRow2 = sheet.CreateRow(currentRow);
|
|
|
|
|
|
headerRow2.Height = 400;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 序号列在第二行也需要创建并设置样式(用于合并)
|
|
|
|
|
|
headerRow2.CreateCell(0).CellStyle = styles.headerStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
|
|
|
|
|
int colIndex = 1;
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
|
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
ICell cell = headerRow2.CreateCell(colIndex);
|
2025-12-31 17:54:34 +08:00
|
|
|
|
cell.SetCellValue($"试样{i}");
|
|
|
|
|
|
cell.CellStyle = styles.headerStyle;
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 为合并区域的所有单元格设置样式
|
|
|
|
|
|
headerRow2.CreateCell(colIndex + 1).CellStyle = styles.headerStyle;
|
|
|
|
|
|
headerRow2.CreateCell(colIndex + 2).CellStyle = styles.headerStyle;
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow, currentRow, colIndex, colIndex + 2));
|
2025-12-31 17:54:34 +08:00
|
|
|
|
colIndex += 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 合并序号列(跨两行)
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow, 0, 0));
|
|
|
|
|
|
|
|
|
|
|
|
currentRow++;
|
|
|
|
|
|
|
|
|
|
|
|
// 第三行表头(1、2、3)
|
|
|
|
|
|
IRow headerRow3 = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
headerRow3.Height = 400;
|
|
|
|
|
|
|
2025-12-31 17:54:34 +08:00
|
|
|
|
colIndex = 1;
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int j = 1; j <= 3; j++)
|
|
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
ICell cell = headerRow3.CreateCell(colIndex++);
|
2025-12-31 17:54:34 +08:00
|
|
|
|
cell.SetCellValue(j.ToString());
|
|
|
|
|
|
cell.CellStyle = styles.headerStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
// 数据行
|
|
|
|
|
|
if (dataTable != null && dataTable.Rows.Count > 0)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2025-12-31 18:36:52 +08:00
|
|
|
|
foreach (DataRow dataRow in dataTable.Rows)
|
|
|
|
|
|
{
|
|
|
|
|
|
string rowName = dataRow["序号"]?.ToString() ?? "";
|
|
|
|
|
|
if (string.IsNullOrEmpty(rowName)) continue;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
IRow row = sheet.CreateRow(currentRow++);
|
|
|
|
|
|
row.Height = 380;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
ICell nameCell = row.CreateCell(0);
|
|
|
|
|
|
nameCell.SetCellValue(rowName);
|
|
|
|
|
|
nameCell.CellStyle = styles.dataStyle;
|
|
|
|
|
|
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 判断是否是标准偏差行(需要合并为一列)
|
|
|
|
|
|
bool isStdDeviationRow = rowName.Contains("标准偏差");
|
|
|
|
|
|
|
|
|
|
|
|
if (isStdDeviationRow)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 标准偏差行:合并为一列,只显示第一个值
|
|
|
|
|
|
ICell valueCell = row.CreateCell(1);
|
|
|
|
|
|
string columnName = "试样1_1";
|
|
|
|
|
|
if (dataTable.Columns.Contains(columnName) && dataRow[columnName] != DBNull.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (double.TryParse(dataRow[columnName].ToString(), out double value))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Math.Abs(value) >= 0.001)
|
|
|
|
|
|
{
|
|
|
|
|
|
valueCell.SetCellValue(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
valueCell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
valueCell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
valueCell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
valueCell.CellStyle = styles.yellowStyle;
|
|
|
|
|
|
|
|
|
|
|
|
// 为合并区域的其他单元格设置样式
|
|
|
|
|
|
for (int i = 2; i <= sampleCount * 3; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
row.CreateCell(i).CellStyle = styles.yellowStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 合并单元格
|
|
|
|
|
|
sheet.AddMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 1, sampleCount * 3));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
// 普通数据行:显示每个试样的每次测试数据
|
|
|
|
|
|
colIndex = 1;
|
|
|
|
|
|
for (int i = 1; i <= sampleCount; i++)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
for (int j = 1; j <= 3; j++)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
ICell cell = row.CreateCell(colIndex++);
|
|
|
|
|
|
string columnName = $"试样{i}_{j}";
|
|
|
|
|
|
|
|
|
|
|
|
if (dataTable.Columns.Contains(columnName) && dataRow[columnName] != DBNull.Value)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
if (double.TryParse(dataRow[columnName].ToString(), out double value))
|
2025-12-31 18:36:52 +08:00
|
|
|
|
{
|
2026-01-03 14:04:08 +08:00
|
|
|
|
if (Math.Abs(value) >= 0.001)
|
|
|
|
|
|
{
|
|
|
|
|
|
cell.SetCellValue(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 所有空数据都显示为空白
|
|
|
|
|
|
cell.SetCellValue("");
|
|
|
|
|
|
}
|
2025-12-31 18:36:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 所有空数据都显示为空白
|
|
|
|
|
|
cell.SetCellValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 所有空数据都显示为空白
|
|
|
|
|
|
cell.SetCellValue("");
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
2026-01-03 14:04:08 +08:00
|
|
|
|
cell.CellStyle = styles.yellowStyle;
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
2025-12-31 17:54:34 +08:00
|
|
|
|
{
|
2026-01-03 18:55:39 +08:00
|
|
|
|
// 停止并释放定时器
|
2026-01-03 15:59:38 +08:00
|
|
|
|
timeUpdateTimer?.Stop();
|
|
|
|
|
|
timeUpdateTimer?.Dispose();
|
2026-01-03 18:55:39 +08:00
|
|
|
|
|
|
|
|
|
|
_readTimer?.Stop();
|
|
|
|
|
|
_readTimer?.Dispose();
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭Modbus和串口
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_modbusMaster?.Dispose();
|
|
|
|
|
|
|
|
|
|
|
|
if (_serialPort != null && _serialPort.IsOpen)
|
|
|
|
|
|
{
|
|
|
|
|
|
_serialPort.Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
_serialPort?.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"关闭串口失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 18:36:52 +08:00
|
|
|
|
Application.Exit();
|
2025-12-31 17:54:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
titleStyle.VerticalAlignment = VerticalAlignment.Center;
|
|
|
|
|
|
|
|
|
|
|
|
// 表头样式(灰色背景)
|
|
|
|
|
|
ICellStyle headerStyle = workbook.CreateCellStyle();
|
|
|
|
|
|
headerStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index;
|
|
|
|
|
|
headerStyle.FillPattern = FillPattern.SolidForeground;
|
|
|
|
|
|
SetBorders(headerStyle);
|
|
|
|
|
|
IFont headerFont = workbook.CreateFont();
|
|
|
|
|
|
headerFont.IsBold = true;
|
|
|
|
|
|
headerStyle.SetFont(headerFont);
|
|
|
|
|
|
headerStyle.Alignment = NPOIHorizontalAlignment.Center;
|
|
|
|
|
|
headerStyle.VerticalAlignment = VerticalAlignment.Center;
|
|
|
|
|
|
|
|
|
|
|
|
// 数据样式(白色背景)
|
|
|
|
|
|
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 (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;
|
|
|
|
|
|
}
|
2025-12-31 09:43:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|