一、效果展示
启动界面

上位机采集

下位机模拟

导出excel


二、VS2022
.net4.8框架

Nuget安装NModbus

Nuget安装SQLite

Nuget安装NPOI

界面设计

三、代码展示
Form1.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
namespace TempHumidityMonitor {
public partial class Form1 : Form {
private ModbusHelper _modbusHelper;
private System.Windows.Forms.Timer _readTimer;
private AppConfig _appConfig; // 存储当前配置
private const byte SLAVE_ID = 1; // Modbus从站地址
// --- 新增:用于报警管理 ---
private string _lastAlarmMessage = ""; // 记录上一次的报警内容,用于去重
private DateTime _lastAlarmTime = DateTime.MinValue;
public Form1() {
InitializeComponent();
InitializeUI();
SetupTimer();
}
private void InitializeUI() {
// 设置图表
chartData.Series.Clear();
var seriesTemp = chartData.Series.Add("温度");
seriesTemp.ChartType = SeriesChartType.Line;
seriesTemp.Color = Color.Red;
var seriesHumi = chartData.Series.Add("湿度");
seriesHumi.ChartType = SeriesChartType.Line;
seriesHumi.Color = Color.Blue;
var chartArea = chartData.ChartAreas[0];
chartArea.AxisX.LabelStyle.Format = "HH:mm:ss";
chartArea.AxisX.MajorGrid.Interval = 3;
chartArea.AxisX.MajorGrid.IntervalType = DateTimeIntervalType.Seconds;
chartArea.AxisX.LabelStyle.Interval = 3;
chartArea.AxisX.LabelStyle.IntervalType = DateTimeIntervalType.Seconds;
chartArea.AxisY.Minimum = 0;
chartArea.AxisY.Maximum = 100;
chartArea.AxisY2.Enabled = AxisEnabled.True;
chartArea.AxisY2.Minimum = 0;
chartArea.AxisY2.Maximum = 100;
//chartArea.AxisX.ScaleView.Zoomable = true;
// 初始化状态标签
labelStatus.Text = "未连接";
labelStatus.ForeColor = Color.Red;
// 初始缩放大小设置为60秒(例如),可以根据需要调整
//chartArea.AxisX.ScaleView.Size = 10;
//chartArea.AxisX.ScaleView.SizeType = DateTimeIntervalType.Seconds;
}
private void SetupTimer() {
_readTimer = new System.Windows.Forms.Timer();
_readTimer.Interval = 2000; // 每2秒读取一次
_readTimer.Tick += ReadTimer_Tick;
}
private async void ReadTimer_Tick(object sender, EventArgs e) {
if (_modbusHelper == null || !_modbusHelper.IsConnected)
return;
try {
// 使用 Task.Run 并传入撤销令牌
var result = await Task.Run(() => _modbusHelper.ReadData(SLAVE_ID));
if (result.HasValue) {
float temp = result.Value.temperature;
float humi = result.Value.humidity;
// 更新UI
UpdateDisplay(temp, humi);
AddToChart(DateTime.Now, temp, humi);
DatabaseHelper.SaveData(temp, humi);
CheckAlarm(temp, humi);
}
else {
SetStatus("读取数据失败", Color.Orange);
}
}
catch (Exception ex) {
this.Invoke((MethodInvoker)delegate {
SetStatus($"读取异常: {ex.Message}", Color.Red);
});
}
}
private void UpdateDisplay(float temp, float humi) {
labelTemp.Text = $"{temp:F1} °C";
labelHumi.Text = $"{humi:F1} %";
SetStatus("连接正常", Color.Green);
}
private void AddToChart(DateTime time, float temp, float humi) {
// 添加数据点
chartData.Series["温度"].Points.AddXY(time, temp);
chartData.Series["湿度"].Points.AddXY(time, humi);
// 限制显示的数据点数量,防止图表过长
const int maxPoints = 10;
while (chartData.Series["温度"].Points.Count > maxPoints) {
chartData.Series["温度"].Points.RemoveAt(0);
chartData.Series["湿度"].Points.RemoveAt(0);
}
// 自动调整 X 轴范围
var chartArea = chartData.ChartAreas[0];
if (chartData.Series["温度"].Points.Count > 0) {
DateTime minTime = DateTime.FromOADate(chartData.Series["温度"].Points[0].XValue);
DateTime maxTime = time;
chartArea.AxisX.Minimum = minTime.ToOADate();
chartArea.AxisX.Maximum = maxTime.ToOADate();
}
chartData.Invalidate();
}
/// <summary>
/// 检查温湿度是否超出阈值,并更新UI和报警列表
/// </summary>
private void CheckAlarm(float temp, float humi) {
// 用于收集所有激活的报警
List<string> activeAlarms = new List<string>();
Color statusColor = Color.Green; // 默认正常颜色
// 获取当前阈值
float highTemp = (float)numericUpDownHighTemp.Value;
float lowTemp = (float)numericUpDownLowTemp.Value;
float highHumi = (float)numericUpDownHighHumi.Value;
float lowHumi = (float)numericUpDownLowHumi.Value;
// 检查温度
if (temp > highTemp) {
activeAlarms.Add($" 高温 {temp:F1}°C");
if (statusColor == Color.Green)
statusColor = Color.Red; // 第一个非正常报警决定主色调
}
else if (temp < lowTemp) {
activeAlarms.Add($"❄️ 低温 {temp:F1}°C");
if (statusColor == Color.Green)
statusColor = Color.Blue;
}
// 检查湿度
if (humi > highHumi) {
activeAlarms.Add($" 高湿 {humi:F1}%");
if (statusColor == Color.Green)
statusColor = Color.Orange;
}
else if (humi < lowHumi) {
activeAlarms.Add($"️ 低湿 {humi:F1}%");
if (statusColor == Color.Green)
statusColor = Color.Purple;
}
// --- 根据结果更新UI ---
if (activeAlarms.Count > 0) {
// 将所有报警信息用分号连接
string combinedMessage = string.Join("; ", activeAlarms);
// 更新状态栏
UpdateStatusStrip(combinedMessage, statusColor);
// 记录到报警列表(避免重复)
string logKey = string.Join("|", activeAlarms); // 用管道符连接作为唯一键
if (_lastAlarmMessage != logKey || (DateTime.Now - _lastAlarmTime).TotalSeconds > 60) {
LogAlarm(combinedMessage);
_lastAlarmMessage = logKey;
_lastAlarmTime = DateTime.Now;
// 可选:播放提示音
if ((DateTime.Now - _lastAlarmTime).TotalSeconds > 300) {
System.Media.SystemSounds.Exclamation.Play();
}
}
}
else {
// 所有指标正常
UpdateStatusStrip("正常", Color.Green);
_lastAlarmMessage = "";
}
}
/// <summary>
/// 更新底部状态栏
/// </summary>
/// <param>要显示的消息</param>
/// <param>文本颜色</param>
private void UpdateStatusStrip(string message, Color color)
{
// ✅ 使用 Form (this) 来检查和调用
if (this.InvokeRequired)
{
// 如果在工作线程,需要通过 Invoke 切换到UI线程
this.Invoke((MethodInvoker)delegate {
toolStripStatusLabel1.Text = message;
toolStripStatusLabel1.ForeColor = color;
});
}
else
{
// 如果已经在UI线程,直接更新
toolStripStatusLabel1.Text = message;
toolStripStatusLabel1.ForeColor = color;
}
}
/// <summary>
/// 记录报警到ListBox
/// </summary>
/// <param>报警消息</param>
private void LogAlarm(string message) {
string logEntry = $"[{DateTime.Now:HH:mm:ss}] {message}";
// ✅ 最佳实践:用 'this' 检查,用 listBoxAlarms.Invoke 执行
if (this.InvokeRequired) {
// 使用 listBoxAlarms 的 Invoke 方法将委托封送到UI线程
listBoxAlarms.Invoke((MethodInvoker)delegate {
listBoxAlarms.Items.Add(logEntry);
listBoxAlarms.TopIndex = listBoxAlarms.Items.Count - 1;
});
}
else {
// 直接在UI线程上操作
listBoxAlarms.Items.Add(logEntry);
listBoxAlarms.TopIndex = listBoxAlarms.Items.Count - 1;
}
}
private void SetStatus(string text, Color color) {
labelStatus.Text = text;
labelStatus.ForeColor = color;
}
// --- UI事件处理 ---
private void btnConnect_Click(object sender, EventArgs e) {
if (_modbusHelper?.IsConnected == true) {
_readTimer.Stop();
_modbusHelper?.Dispose();
_modbusHelper = null;
btnConnect.Text = "连接";
SetStatus("已断开", Color.Red);
return;
}
string portName = comboBoxPort.Text;
if (string.IsNullOrEmpty(portName)) {
MessageBox.Show("请选择串口号!");
return;
}
int baudRate = int.Parse(comboBoxBaudRate.Text);
_modbusHelper = new ModbusHelper();
if (_modbusHelper.Connect(portName, baudRate)) {
_readTimer.Start();
btnConnect.Text = "断开";
SetStatus("连接成功", Color.Green);
}
else {
MessageBox.Show("无法打开串口,请检查设备或端口设置。");
SetStatus("连接失败", Color.Red);
}
}
private void btnLoadHistory_Click(object sender, EventArgs e) {
DataTable data = DatabaseHelper.GetAllData();
dataGridViewHistory.DataSource = data;
}
private void Form1_Load(object sender, EventArgs e) {
// --- 加载配置 ---
_appConfig = JsonHelper.LoadConfig();
// 自动填充串口号
string[] ports = System.IO.Ports.SerialPort.GetPortNames();
comboBoxPort.Items.AddRange(ports);
if (!string.IsNullOrEmpty(_appConfig.PortName) && ports.Contains(_appConfig.PortName))
{
comboBoxPort.SelectedItem = _appConfig.PortName;
}
else if (ports.Length > 0)
{
comboBoxPort.SelectedIndex = 0; // 默认第一个
}
// 设置默认波特率
comboBoxBaudRate.Items.AddRange(new object[] { "9600", "19200", "38400", "57600", "115200" });
if (_appConfig.BaudRate.ToString() == comboBoxBaudRate.Items.Cast<string>().FirstOrDefault(x => x == _appConfig.BaudRate.ToString()))
{
comboBoxBaudRate.SelectedItem = _appConfig.BaudRate.ToString();
}
else
{
comboBoxBaudRate.SelectedItem = "115200"; // 默认
}
// 设置报警阈值
numericUpDownHighTemp.Value = (decimal)_appConfig.HighTemp;
numericUpDownLowTemp.Value = (decimal)_appConfig.LowTemp;
numericUpDownHighHumi.Value = (decimal)_appConfig.HighHumi;
numericUpDownLowHumi.Value = (decimal)_appConfig.LowHumi;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_readTimer?.Stop();
// 直接调用 Disconnect,它会安全地清理所有资源
_modbusHelper?.Dispose();
_modbusHelper = null;
// --- 保存当前配置 ---
_appConfig.PortName = comboBoxPort.Text;
_appConfig.BaudRate = int.Parse(comboBoxBaudRate.Text);
_appConfig.HighTemp = (float)numericUpDownHighTemp.Value;
_appConfig.LowTemp = (float)numericUpDownLowTemp.Value;
_appConfig.HighHumi = (float)numericUpDownHighHumi.Value;
_appConfig.LowHumi = (float)numericUpDownLowHumi.Value;
JsonHelper.SaveConfig(_appConfig);
}
private void btnExport_Click(object sender, EventArgs e) {
// ✅ 直接从数据库获取所有数据,不依赖 DataGridView 的 DataSource
DataTable allData = DatabaseHelper.GetAllData();
if (allData == null || allData.Rows.Count == 0) {
MessageBox.Show("数据库中没有历史数据可导出。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
using (SaveFileDialog sfd = new SaveFileDialog()) {
sfd.Filter = "Excel文件|*.xlsx";
sfd.Title = "导出历史数据";
if (sfd.ShowDialog() == DialogResult.OK) {
ExportToExcel(allData, sfd.FileName);
}
}
}
// 此方法目前只接收一个 DataTable,不再需要从 DataGridView 取数据
private void ExportToExcel(DataTable dt, string filePath) {
try {
NPOI.XSSF.UserModel.XSSFWorkbook workbook = new NPOI.XSSF.UserModel.XSSFWorkbook();
NPOI.SS.UserModel.ISheet sheet = workbook.CreateSheet("历史数据");
// 创建表头
NPOI.SS.UserModel.IRow headerRow = sheet.CreateRow(0);
for (int i = 0; i < dt.Columns.Count; i++) {
headerRow.CreateCell(i).SetCellValue(dt.Columns[i].ColumnName);
}
// 填充数据
for (int i = 0; i < dt.Rows.Count; i++) {
NPOI.SS.UserModel.IRow row = sheet.CreateRow(i + 1);
for (int j = 0; j < dt.Columns.Count; j++) {
row.CreateCell(j).SetCellValue(dt.Rows[i][j]?.ToString());
}
}
// 自动调整列宽
for (int i = 0; i < dt.Columns.Count; i++) {
sheet.AutoSizeColumn(i);
}
using (System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create, System.IO.FileAccess.Write)) {
workbook.Write(fs);
}
MessageBox.Show("导出成功!");
}
catch (Exception ex) {
MessageBox.Show($"导出失败: {ex.Message}");
}
}
}
}
Form1.Designer.cs
namespace TempHumidityMonitor {
partial class Form1 {
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param>如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent() {
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea2 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
System.Windows.Forms.DataVisualization.Charting.Legend legend2 = new System.Windows.Forms.DataVisualization.Charting.Legend();
System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series();
System.Windows.Forms.DataVisualization.Charting.Series series4 = new System.Windows.Forms.DataVisualization.Charting.Series();
this.comboBoxPort = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.comboBoxBaudRate = new System.Windows.Forms.ComboBox();
this.btnConnect = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.labelTemp = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.labelHumi = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.labelStatus = new System.Windows.Forms.Label();
this.chartData = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.numericUpDownLowHumi = new System.Windows.Forms.NumericUpDown();
this.numericUpDownHighHumi = new System.Windows.Forms.NumericUpDown();
this.numericUpDownLowTemp = new System.Windows.Forms.NumericUpDown();
this.numericUpDownHighTemp = new System.Windows.Forms.NumericUpDown();
this.label12 = new System.Windows.Forms.Label();
this.label11 = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.label9 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.dataGridViewHistory = new System.Windows.Forms.DataGridView();
this.btnLoadHistory = new System.Windows.Forms.Button();
this.btnExport = new System.Windows.Forms.Button();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
this.listBoxAlarms = new System.Windows.Forms.ListBox();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.chartData)).BeginInit();
this.groupBox2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownLowHumi)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownHighHumi)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownLowTemp)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownHighTemp)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewHistory)).BeginInit();
this.statusStrip1.SuspendLayout();
this.SuspendLayout();
//
// comboBoxPort
//
this.comboBoxPort.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxPort.FormattingEnabled = true;
this.comboBoxPort.Location = new System.Drawing.Point(65, 13);
this.comboBoxPort.Name = "comboBoxPort";
this.comboBoxPort.Size = new System.Drawing.Size(100, 24);
this.comboBoxPort.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 16);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(43, 16);
this.label1.TabIndex = 1;
this.label1.Text = "串口号:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(180, 16);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(43, 16);
this.label2.TabIndex = 2;
this.label2.Text = "波特率:";
//
// comboBoxBaudRate
//
this.comboBoxBaudRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxBaudRate.FormattingEnabled = true;
this.comboBoxBaudRate.Items.AddRange(new object[] {
"9600",
"19200",
"38400",
"57600",
"115200"});
this.comboBoxBaudRate.Location = new System.Drawing.Point(245, 13);
this.comboBoxBaudRate.Name = "comboBoxBaudRate";
this.comboBoxBaudRate.Size = new System.Drawing.Size(100, 24);
this.comboBoxBaudRate.TabIndex = 3;
//
// btnConnect
//
this.btnConnect.Location = new System.Drawing.Point(365, 11);
this.btnConnect.Name = "btnConnect";
this.btnConnect.Size = new System.Drawing.Size(80, 29);
this.btnConnect.TabIndex = 4;
this.btnConnect.Text = "连接";
this.btnConnect.UseVisualStyleBackColor = true;
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
//
// groupBox1
//
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.Controls.Add(this.labelTemp);
this.groupBox1.Controls.Add(this.label4);
this.groupBox1.Controls.Add(this.label5);
this.groupBox1.Controls.Add(this.labelHumi);
this.groupBox1.Controls.Add(this.label7);
this.groupBox1.Controls.Add(this.labelStatus);
this.groupBox1.Location = new System.Drawing.Point(12, 48);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(777, 64);
this.groupBox1.TabIndex = 5;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "实时状态";
//
// labelTemp
//
this.labelTemp.AutoSize = true;
this.labelTemp.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.labelTemp.ForeColor = System.Drawing.Color.Red;
this.labelTemp.Location = new System.Drawing.Point(100, 27);
this.labelTemp.Name = "labelTemp";
this.labelTemp.Size = new System.Drawing.Size(66, 22);
this.labelTemp.TabIndex = 7;
this.labelTemp.Text = "--.-- °C";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(183, 32);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(32, 16);
this.label4.TabIndex = 6;
this.label4.Text = "湿度:";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(6, 32);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(32, 16);
this.label5.TabIndex = 5;
this.label5.Text = "温度:";
//
// labelHumi
//
this.labelHumi.AutoSize = true;
this.labelHumi.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.labelHumi.ForeColor = System.Drawing.Color.Blue;
this.labelHumi.Location = new System.Drawing.Point(228, 27);
this.labelHumi.Name = "labelHumi";
this.labelHumi.Size = new System.Drawing.Size(63, 22);
this.labelHumi.TabIndex = 8;
this.labelHumi.Text = "--.-- %";
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(330, 32);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(32, 16);
this.label7.TabIndex = 9;
this.label7.Text = "状态:";
//
// labelStatus
//
this.labelStatus.AutoSize = true;
this.labelStatus.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.labelStatus.Location = new System.Drawing.Point(375, 32);
this.labelStatus.Name = "labelStatus";
this.labelStatus.Size = new System.Drawing.Size(37, 20);
this.labelStatus.TabIndex = 10;
this.labelStatus.Text = "就绪";
//
// chartData
//
this.chartData.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
chartArea2.AxisX.LabelStyle.Format = "HH:mm:ss";
chartArea2.AxisX.MajorGrid.Interval = 5D;
chartArea2.AxisY.Title = "温度 (°C)";
chartArea2.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.True;
chartArea2.AxisY2.Title = "湿度 (%)";
chartArea2.Name = "ChartArea1";
this.chartData.ChartAreas.Add(chartArea2);
legend2.Name = "Legend1";
this.chartData.Legends.Add(legend2);
this.chartData.Location = new System.Drawing.Point(12, 118);
this.chartData.Name = "chartData";
series3.ChartArea = "ChartArea1";
series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series3.Legend = "Legend1";
series3.Name = "温度";
series4.ChartArea = "ChartArea1";
series4.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series4.Legend = "Legend1";
series4.Name = "湿度";
this.chartData.Series.Add(series3);
this.chartData.Series.Add(series4);
this.chartData.Size = new System.Drawing.Size(777, 310);
this.chartData.TabIndex = 6;
this.chartData.Text = "chart1";
//
// groupBox2
//
this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.groupBox2.Controls.Add(this.numericUpDownLowHumi);
this.groupBox2.Controls.Add(this.numericUpDownHighHumi);
this.groupBox2.Controls.Add(this.numericUpDownLowTemp);
this.groupBox2.Controls.Add(this.numericUpDownHighTemp);
this.groupBox2.Controls.Add(this.label12);
this.groupBox2.Controls.Add(this.label11);
this.groupBox2.Controls.Add(this.label10);
this.groupBox2.Controls.Add(this.label9);
this.groupBox2.Controls.Add(this.label8);
this.groupBox2.Location = new System.Drawing.Point(12, 438);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(300, 160);
this.groupBox2.TabIndex = 7;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "报警设置";
//
// numericUpDownLowHumi
//
this.numericUpDownLowHumi.DecimalPlaces = 1;
this.numericUpDownLowHumi.Increment = new decimal(new int[] {
5,
0,
0,
65536});
this.numericUpDownLowHumi.Location = new System.Drawing.Point(150, 117);
this.numericUpDownLowHumi.Name = "numericUpDownLowHumi";
this.numericUpDownLowHumi.Size = new System.Drawing.Size(80, 22);
this.numericUpDownLowHumi.TabIndex = 8;
this.numericUpDownLowHumi.Value = new decimal(new int[] {
30,
0,
0,
0});
//
// numericUpDownHighHumi
//
this.numericUpDownHighHumi.DecimalPlaces = 1;
this.numericUpDownHighHumi.Increment = new decimal(new int[] {
5,
0,
0,
65536});
this.numericUpDownHighHumi.Location = new System.Drawing.Point(150, 85);
this.numericUpDownHighHumi.Name = "numericUpDownHighHumi";
this.numericUpDownHighHumi.Size = new System.Drawing.Size(80, 22);
this.numericUpDownHighHumi.TabIndex = 7;
this.numericUpDownHighHumi.Value = new decimal(new int[] {
80,
0,
0,
0});
//
// numericUpDownLowTemp
//
this.numericUpDownLowTemp.DecimalPlaces = 1;
this.numericUpDownLowTemp.Increment = new decimal(new int[] {
5,
0,
0,
65536});
this.numericUpDownLowTemp.Location = new System.Drawing.Point(150, 53);
this.numericUpDownLowTemp.Minimum = new decimal(new int[] {
100,
0,
0,
-2147483648});
this.numericUpDownLowTemp.Name = "numericUpDownLowTemp";
this.numericUpDownLowTemp.Size = new System.Drawing.Size(80, 22);
this.numericUpDownLowTemp.TabIndex = 6;
this.numericUpDownLowTemp.Value = new decimal(new int[] {
10,
0,
0,
0});
//
// numericUpDownHighTemp
//
this.numericUpDownHighTemp.DecimalPlaces = 1;
this.numericUpDownHighTemp.Increment = new decimal(new int[] {
5,
0,
0,
65536});
this.numericUpDownHighTemp.Location = new System.Drawing.Point(150, 21);
this.numericUpDownHighTemp.Name = "numericUpDownHighTemp";
this.numericUpDownHighTemp.Size = new System.Drawing.Size(80, 22);
this.numericUpDownHighTemp.TabIndex = 5;
this.numericUpDownHighTemp.Value = new decimal(new int[] {
30,
0,
0,
0});
//
// label12
//
this.label12.AutoSize = true;
this.label12.Location = new System.Drawing.Point(80, 123);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(54, 16);
this.label12.TabIndex = 4;
this.label12.Text = "低湿阈值:";
//
// label11
//
this.label11.AutoSize = true;
this.label11.Location = new System.Drawing.Point(80, 91);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(54, 16);
this.label11.TabIndex = 3;
this.label11.Text = "高湿阈值:";
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(80, 59);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(54, 16);
this.label10.TabIndex = 2;
this.label10.Text = "低温阈值:";
//
// label9
//
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(80, 27);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(54, 16);
this.label9.TabIndex = 1;
this.label9.Text = "高温阈值:";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label8.Location = new System.Drawing.Point(10, 27);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(32, 17);
this.label8.TabIndex = 0;
this.label8.Text = "温度";
//
// dataGridViewHistory
//
this.dataGridViewHistory.AllowUserToAddRows = false;
this.dataGridViewHistory.AllowUserToDeleteRows = false;
this.dataGridViewHistory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dataGridViewHistory.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridViewHistory.Location = new System.Drawing.Point(320, 434);
this.dataGridViewHistory.Name = "dataGridViewHistory";
this.dataGridViewHistory.ReadOnly = true;
this.dataGridViewHistory.RowTemplate.Height = 23;
this.dataGridViewHistory.Size = new System.Drawing.Size(469, 126);
this.dataGridViewHistory.TabIndex = 8;
//
// btnLoadHistory
//
this.btnLoadHistory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnLoadHistory.Location = new System.Drawing.Point(406, 563);
this.btnLoadHistory.Name = "btnLoadHistory";
this.btnLoadHistory.Size = new System.Drawing.Size(100, 30);
this.btnLoadHistory.TabIndex = 9;
this.btnLoadHistory.Text = "加载历史数据";
this.btnLoadHistory.UseVisualStyleBackColor = true;
this.btnLoadHistory.Click += new System.EventHandler(this.btnLoadHistory_Click);
//
// btnExport
//
this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnExport.Location = new System.Drawing.Point(584, 564);
this.btnExport.Name = "btnExport";
this.btnExport.Size = new System.Drawing.Size(100, 30);
this.btnExport.TabIndex = 10;
this.btnExport.Text = "导出Excel";
this.btnExport.UseVisualStyleBackColor = true;
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
//
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripStatusLabel1});
this.statusStrip1.Location = new System.Drawing.Point(0, 662);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(801, 22);
this.statusStrip1.TabIndex = 11;
this.statusStrip1.Text = "statusStrip1";
//
// toolStripStatusLabel1
//
this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
this.toolStripStatusLabel1.Size = new System.Drawing.Size(32, 17);
this.toolStripStatusLabel1.Text = "就绪";
//
// listBoxAlarms
//
this.listBoxAlarms.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listBoxAlarms.FormattingEnabled = true;
this.listBoxAlarms.ItemHeight = 16;
this.listBoxAlarms.Location = new System.Drawing.Point(320, 594);
this.listBoxAlarms.Name = "listBoxAlarms";
this.listBoxAlarms.Size = new System.Drawing.Size(469, 84);
this.listBoxAlarms.TabIndex = 12;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(801, 684);
this.Controls.Add(this.listBoxAlarms);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.btnExport);
this.Controls.Add(this.btnLoadHistory);
this.Controls.Add(this.dataGridViewHistory);
this.Controls.Add(this.chartData);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.btnConnect);
this.Controls.Add(this.comboBoxBaudRate);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.comboBoxPort);
this.Font = new System.Drawing.Font("微软雅黑", 8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.MinimumSize = new System.Drawing.Size(800, 680);
this.Name = "Form1";
this.Text = "智能温湿度监控系统";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
this.Load += new System.EventHandler(this.Form1_Load);
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.chartData)).EndInit();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownLowHumi)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownHighHumi)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownLowTemp)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numericUpDownHighTemp)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.dataGridViewHistory)).EndInit();
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ComboBox comboBoxPort;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ComboBox comboBoxBaudRate;
private System.Windows.Forms.Button btnConnect;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label labelTemp;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label labelHumi;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label labelStatus;
private System.Windows.Forms.DataVisualization.Charting.Chart chartData;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.NumericUpDown numericUpDownLowHumi;
private System.Windows.Forms.NumericUpDown numericUpDownHighHumi;
private System.Windows.Forms.NumericUpDown numericUpDownLowTemp;
private System.Windows.Forms.NumericUpDown numericUpDownHighTemp;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.Label label11;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.DataGridView dataGridViewHistory;
private System.Windows.Forms.Button btnLoadHistory;
private System.Windows.Forms.Button btnExport;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
private System.Windows.Forms.ListBox listBoxAlarms;
}
}
SqLiteHelper
using System;
using System.Data.SQLite;
namespace TempHumidityMonitor {
public static class DatabaseHelper {
private static string ConnectionString = "Data Source=sensor_data.db;Version=3;";
/// <summary>
/// 静态构造函数,在首次使用类时执行一次
/// </summary>
static DatabaseHelper() {
InitializeDatabase();
}
private static void InitializeDatabase() {
using (var connection = new SQLiteConnection(ConnectionString)) {
connection.Open();
string createTableSql = @"
CREATE TABLE IF NOT EXISTS SensorData (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Temperature REAL NOT NULL,
Humidity REAL NOT NULL,
Timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
);";
using (var command = new SQLiteCommand(createTableSql, connection)) {
command.ExecuteNonQuery();
}
}
}
/// <summary>
/// 保存一条数据到数据库
/// </summary>
public static void SaveData(float temperature, float humidity) {
using (var connection = new SQLiteConnection(ConnectionString)) {
connection.Open();
string insertSql = "INSERT INTO SensorData (Temperature, Humidity) VALUES (@temp, @humi)";
using (var command = new SQLiteCommand(insertSql, connection)) {
command.Parameters.AddWithValue("@temp", temperature);
command.Parameters.AddWithValue("@humi", humidity);
command.ExecuteNonQuery();
}
}
}
/// <summary>
/// 查询所有历史数据
/// </summary>
/// <returns>包含历史数据的DataTable</returns>
public static System.Data.DataTable GetAllData() {
var dataTable = new System.Data.DataTable();
using (var connection = new SQLiteConnection(ConnectionString)) {
connection.Open();
string selectSql = "SELECT Id, Temperature, Humidity, Timestamp FROM SensorData ORDER BY Timestamp DESC";
using (var adapter = new SQLiteDataAdapter(selectSql, connection)) {
adapter.Fill(dataTable);
}
}
return dataTable;
}
}
}
导出Excel
// 此方法目前只接收一个 DataTable,不再需要从 DataGridView 取数据
private void ExportToExcel(DataTable dt, string filePath) {
try {
NPOI.XSSF.UserModel.XSSFWorkbook workbook = new NPOI.XSSF.UserModel.XSSFWorkbook();
NPOI.SS.UserModel.ISheet sheet = workbook.CreateSheet("历史数据");
// 创建表头
NPOI.SS.UserModel.IRow headerRow = sheet.CreateRow(0);
for (int i = 0; i < dt.Columns.Count; i++) {
headerRow.CreateCell(i).SetCellValue(dt.Columns[i].ColumnName);
}
// 填充数据
for (int i = 0; i < dt.Rows.Count; i++) {
NPOI.SS.UserModel.IRow row = sheet.CreateRow(i + 1);
for (int j = 0; j < dt.Columns.Count; j++) {
row.CreateCell(j).SetCellValue(dt.Rows[i][j]?.ToString());
}
}
// 自动调整列宽
for (int i = 0; i < dt.Columns.Count; i++) {
sheet.AutoSizeColumn(i);
}
using (System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create, System.IO.FileAccess.Write)) {
workbook.Write(fs);
}
MessageBox.Show("导出成功!");
}
catch (Exception ex) {
MessageBox.Show($"导出失败: {ex.Message}");
}
}
Modbus通讯工具下载和测试
C# WinForm +Modbus RTU通讯
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...


