一、效果展示
启动界面
上位机采集
下位机模拟
导出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 name="message">要显示的消息</param> /// <param name="color">文本颜色</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 name="message">报警消息</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 name="disposing">如果应释放托管资源,为 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通讯
