您的位置:首页 > 汽车 > 时评 > 营业执照不干了不注销会怎样_沧州seo排名_满十八岁可以申请abc认证吗_百度搜图入口

营业执照不干了不注销会怎样_沧州seo排名_满十八岁可以申请abc认证吗_百度搜图入口

2025/4/19 14:27:30 来源:https://blog.csdn.net/weixin_44939430/article/details/147149449  浏览:    关键词:营业执照不干了不注销会怎样_沧州seo排名_满十八岁可以申请abc认证吗_百度搜图入口
营业执照不干了不注销会怎样_沧州seo排名_满十八岁可以申请abc认证吗_百度搜图入口

serialPort.DataReceived、串口优雅管理


完整《C#串口通信系统》功能清单


Part 1 — SerialPortManager.cs —— 串口核心管理类

using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Windows.Forms;/// <summary>
/// 专业版串口通信管理器
/// 支持:自动重连、自动超时检测、线程安全接收、发送失败重试、统一日志
/// </summary>
public class SerialPortManager
{private SerialPort _serialPort;private System.Timers.Timer _reconnectTimer; // 自动重连定时器public event Action<byte[]> DataReceived;  // 串口数据接收事件public event Action<string> LogMessage;    // 日志输出事件public bool IsOpen => _serialPort != null && _serialPort.IsOpen;public string PortName { get; private set; }public int BaudRate { get; private set; }public SerialPortManager(){_reconnectTimer = new System.Timers.Timer(5000); // 5秒检测一次串口状态_reconnectTimer.Elapsed += (sender, e) => ReconnectCheck();_reconnectTimer.Start();}public void Open(string portName, int baudRate = 115200){try{PortName = portName;BaudRate = baudRate;if (_serialPort == null){_serialPort = new SerialPort{PortName = portName,BaudRate = baudRate,Encoding = Encoding.UTF8};_serialPort.DataReceived += SerialPort_DataReceived;}if (!_serialPort.IsOpen){_serialPort.Open();Log($"✅ 串口 {portName} 打开成功");}}catch (Exception ex){Log($"❌ 打开串口失败:{ex.Message}");}}public void Close(){try{if (_serialPort != null){if (_serialPort.IsOpen){_serialPort.Close();Log($"❎ 串口 {PortName} 已关闭");}_serialPort.DataReceived -= SerialPort_DataReceived;_serialPort.Dispose();_serialPort = null;}}catch (Exception ex){Log($"❌ 关闭串口失败:{ex.Message}");}}public void Send(byte[] data){if (_serialPort == null || !_serialPort.IsOpen){Log("❗ 串口未打开,无法发送!");return;}int retryCount = 0;const int maxRetries = 3;while (retryCount < maxRetries){try{_serialPort.Write(data, 0, data.Length);Log("📤 数据发送成功");break;}catch (Exception ex){retryCount++;Log($"⚠️ 发送失败,重试{retryCount}/{maxRetries}次:{ex.Message}");Thread.Sleep(100); // 短暂等待}}if (retryCount == maxRetries){Log("❌ 发送失败,已达到最大重试次数!");}}private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){try{int bytesToRead = _serialPort.BytesToRead;byte[] buffer = new byte[bytesToRead];_serialPort.Read(buffer, 0, bytesToRead);DataReceived?.Invoke(buffer); // 抛给外部}catch (Exception ex){Log($"❌ 接收数据失败:{ex.Message}");}}private void ReconnectCheck(){if (_serialPort != null && !_serialPort.IsOpen && !string.IsNullOrEmpty(PortName)){try{_serialPort.Open();Log($"🔄 检测到串口断开,自动重连成功!");}catch{Log($"🔄 正在尝试重连串口 {PortName}...");}}}private void Log(string message){LogMessage?.Invoke($"[{DateTime.Now:HH:mm:ss}] {message}");}
}

Part 2 — Form1.cs —— 界面调用端逻辑

// 引入
private SerialPortManager spManager = new SerialPortManager();
private List<string> serialLogs = new List<string>();
private System.Timers.Timer timeoutTimer;private void Form1_Load(object sender, EventArgs e)
{RefreshPorts();spManager.DataReceived += OnDataReceived;spManager.LogMessage += Log;
}private void openPortBtn_Click(object sender, EventArgs e)
{spManager.Open(comboBox1.Text, 115200);
}private void closePortBtn_Click(object sender, EventArgs e)
{spManager.Close();
}private void sendBtn_Click(object sender, EventArgs e)
{byte[] frame = BuildFrame();spManager.Send(frame);// 开始超时监控StartTimeoutMonitor(1500);
}private void saveLogBtn_Click(object sender, EventArgs e)
{SaveSerialLog();
}// 接收到串口数据
private void OnDataReceived(byte[] data)
{this.Invoke((Action)(() =>{timeoutTimer?.Stop();timeoutTimer?.Dispose();timeoutTimer = null;Log($"📥 收到数据:{BitConverter.ToString(data).Replace("-", " ")}");}));
}// 日志打印+记录
private void Log(string message)
{if (this.InvokeRequired){this.Invoke(new Action(() => Log(message)));return;}string logMessage = $"[{DateTime.Now:HH:mm:ss}] {message}";textBoxLog.AppendText(logMessage + Environment.NewLine);serialLogs.Add(logMessage);
}// 保存日志
private void SaveSerialLog()
{if (serialLogs.Count == 0){MessageBox.Show("暂无日志可保存!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);return;}string exePath = AppDomain.CurrentDomain.BaseDirectory;string logFolder = Path.Combine(exePath, "logs");if (!Directory.Exists(logFolder)){Directory.CreateDirectory(logFolder);}string fileName = $"串口日志_{DateTime.Now:yyyyMMdd_HHmmss}.log";string fullPath = Path.Combine(logFolder, fileName);File.WriteAllLines(fullPath, serialLogs, Encoding.UTF8);MessageBox.Show($"日志保存成功!\n路径:{fullPath}", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}// 热插拔检测
protected override void WndProc(ref Message m)
{const int WM_DEVICECHANGE = 0x0219;const int DBT_DEVICEARRIVAL = 0x8000;const int DBT_DEVICEREMOVECOMPLETE = 0x8004;base.WndProc(ref m);if (m.Msg == WM_DEVICECHANGE){if (m.WParam.ToInt32() == DBT_DEVICEARRIVAL){Log("📥 串口设备已插入");RefreshPorts();}else if (m.WParam.ToInt32() == DBT_DEVICEREMOVECOMPLETE){Log("📤 串口设备已拔出");RefreshPorts();}}
}// 刷新串口列表
private void RefreshPorts()
{var ports = SerialPort.GetPortNames();comboBox1.Items.Clear();comboBox1.Items.AddRange(ports);if (ports.Length > 0){comboBox1.SelectedIndex = 0;}
}// 启动超时检测
private void StartTimeoutMonitor(int timeoutMs = 1000)
{if (timeoutTimer != null){timeoutTimer.Stop();timeoutTimer.Dispose();}timeoutTimer = new System.Timers.Timer(timeoutMs);timeoutTimer.Elapsed += (s, e) =>{timeoutTimer.Stop();timeoutTimer.Dispose();timeoutTimer = null;this.Invoke((Action)(() =>{Log("⚠️ 超时:设备未响应指令!");MessageBox.Show("设备未在限定时间内响应,请检查连接或设备状态。", "超时警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);}));};timeoutTimer.Start();
}

🔥 项目总览架构:

文件作用
SerialPortManager.cs串口统一管理(发送/接收/重连/日志)
Form1.cs上位机界面操作(开关串口/显示日志/保存日志)
resources/保存你的烧录数据/扫码数据等
logs/保存串口完整操作日志

🎯 小结

功能状态
自动检测热插拔刷新串口列表
自动重连串口
发送数据失败自动重试
接收数据线程安全处理
日志追踪全部收发
一键保存日志
超时无响应自动警告

这已经是一个工业级、企业上位机必备的完整串口通信系统


扩展

serialPort.DataReceived

C# 串口通信,为什么要写:
serialPort.DataReceived += new SerialDataReceivedEventHandler(Sp_DataReceived);
如果不写,会有什么后果?


✅ 1. serialPort.DataReceived += ... 是什么意思?

它的意思是:

注册事件监听器,告诉系统:

“嘿!每当串口有数据到达(收到数据)的时候,请自动调用我的 Sp_DataReceived 方法来处理!”


✅ 2. 如果不写这一行,会怎么样?

如果 不注册 DataReceived 事件,那么:

  • 即使串口收到了设备发来的数据
  • 程序 完全不会收到通知
  • Sp_DataReceived 方法 永远不会被调用
  • 你就接收不到任何回传数据❗
  • 整个上位机只能发送,没法接收数据

✅ 3. 这行代码详细拆解

serialPort.DataReceived += new SerialDataReceivedEventHandler(Sp_DataReceived);

也可以简化写成(完全一样效果):

serialPort.DataReceived += Sp_DataReceived;

意思就是:

Sp_DataReceived 方法绑到串口的 DataReceived 事件上。

以后只要串口有新数据来,就会自动执行 Sp_DataReceived() 方法!


总结成一句话:

不注册 DataReceived 事件,程序就完全收不到串口回来的数据。


✅ 最后标准流程回顾一下:

步骤必要操作作用
1打开串口(serialPort.Open())打通发送通道
2注册 DataReceived 事件监听器建立数据接收处理机制
3实现 Sp_DataReceived 处理函数真正处理收到的数据内容

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com