核心功能
SendCommandAndWaitResponse
方法:
- 发送指令后持续读取串口数据
- 两个独立的超时参数:
noDataTimeoutMs
- 串口完全无响应的超时时间wrongDataTimeoutMs
- 有数据但不匹配期望响应的超时时间
- 使用
out string matchedResponse
返回结果或错误信息
超时逻辑
发送指令 → 等待数据
↓
没收到任何数据?
↓ 是
等待超过 noDataTimeoutMs? → 返回"串口无响应"错误
↓ 否
收到数据但不匹配?
↓ 是
距离上次收到数据超过 wrongDataTimeoutMs? → 返回"数据不匹配"错误
↓ 否
继续等待...
使用示例
csharp
string result;
bool success = manager.SendCommandAndWaitResponse(
command, // 要发送的指令
expectedResponses, // 期望的响应列表
3000, // 3秒无数据超时
2000, // 2秒错误数据超时
out result // 返回匹配的响应或错误信息
);
if (success) {
Console.WriteLine("成功: " + result);
} else {
Console.WriteLine("失败: " + result);
}
代码完全符合.NET Framework 4.0,没有使用任何新语法!
csharp
using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Collections.Generic;
namespace SerialPortDemo
{
/// <summary>
/// 串口通信管理类
/// </summary>
public class SerialPortManager
{
private SerialPort serialPort;
private Thread receiveThread;
private bool isRunning = false;
private object lockObject = new object();
// 用于存储接收到的所有数据
private List<string> receivedMessages = new List<string>();
/// <summary>
/// 构造函数
/// </summary>
public SerialPortManager(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
{
serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
serialPort.ReadTimeout = 500;
serialPort.WriteTimeout = 500;
}
/// <summary>
/// 打开串口并启动接收线程
/// </summary>
public bool Open()
{
try
{
if (!serialPort.IsOpen)
{
serialPort.Open();
isRunning = true;
// 启动后台接收线程
receiveThread = new Thread(new ThreadStart(ReceiveDataThread));
receiveThread.IsBackground = true;
receiveThread.Start();
return true;
}
return false;
}
catch (Exception ex)
{
throw new Exception("打开串口失败: " + ex.Message);
}
}
/// <summary>
/// 关闭串口
/// </summary>
public void Close()
{
isRunning = false;
if (receiveThread != null && receiveThread.IsAlive)
{
receiveThread.Join(1000); // 等待线程结束
}
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
}
}
/// <summary>
/// 后台接收数据线程
/// </summary>
private void ReceiveDataThread()
{
byte[] buffer = new byte[1024];
while (isRunning)
{
try
{
if (serialPort.IsOpen && serialPort.BytesToRead > 0)
{
int bytesRead = serialPort.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
// 提取实际读取的字节
byte[] data = new byte[bytesRead];
Array.Copy(buffer, 0, data, 0, bytesRead);
// 转换为16进制字符串
string hexString = ByteArrayToHexString(data);
lock (lockObject)
{
receivedMessages.Add(hexString);
// 打印接收到的数据(可选)
Console.WriteLine("[接收] " + hexString);
}
}
}
else
{
Thread.Sleep(10); // 避免CPU占用过高
}
}
catch (Exception ex)
{
// 记录异常但不中断线程
Console.WriteLine("接收数据异常: " + ex.Message);
Thread.Sleep(100);
}
}
}
/// <summary>
/// 将byte[]转换为16进制字符串(空格分隔)
/// 例如: 01 A2 34 54 12
/// </summary>
public static string ByteArrayToHexString(byte[] data)
{
if (data == null || data.Length == 0)
{
return string.Empty;
}
StringBuilder sb = new StringBuilder(data.Length * 3);
for (int i = 0; i < data.Length; i++)
{
sb.Append(data[i].ToString("X2"));
if (i < data.Length - 1)
{
sb.Append(" ");
}
}
return sb.ToString();
}
/// <summary>
/// 将16进制字符串转换为byte[]
/// 支持格式: "01A23454" 或 "01 A2 34 54"
/// </summary>
public static byte[] HexStringToByteArray(string hexString)
{
if (string.IsNullOrEmpty(hexString))
{
return new byte[0];
}
// 移除空格
hexString = hexString.Replace(" ", "").Replace("-", "");
if (hexString.Length % 2 != 0)
{
throw new ArgumentException("16进制字符串长度必须是偶数");
}
byte[] bytes = new byte[hexString.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
return bytes;
}
/// <summary>
/// 发送数据
/// </summary>
public void SendData(byte[] data)
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Write(data, 0, data.Length);
Console.WriteLine("[发送] " + ByteArrayToHexString(data));
}
else
{
throw new Exception("串口未打开");
}
}
/// <summary>
/// 发送16进制字符串
/// </summary>
public void SendHexString(string hexString)
{
byte[] data = HexStringToByteArray(hexString);
SendData(data);
}
/// <summary>
/// 执行指令并等待指定响应
/// </summary>
/// <param name="command">要发送的指令(byte数组)</param>
/// <param name="expectedResponses">期望的响应列表(16进制字符串)</param>
/// <param name="noDataTimeoutMs">没有任何数据的超时时间(毫秒)</param>
/// <param name="wrongDataTimeoutMs">有数据但不匹配的超时时间(毫秒)</param>
/// <param name="matchedResponse">返回匹配的响应内容</param>
/// <returns>成功返回true,超时返回false</returns>
public bool SendCommandAndWaitResponse(byte[] command, string[] expectedResponses,
int noDataTimeoutMs, int wrongDataTimeoutMs, out string matchedResponse)
{
matchedResponse = string.Empty;
// 清空之前的接收缓冲
lock (lockObject)
{
receivedMessages.Clear();
}
// 发送指令
SendData(command);
DateTime startTime = DateTime.Now;
DateTime lastDataTime = DateTime.Now;
bool hasReceivedData = false;
while (true)
{
// 检查是否有新数据
List<string> currentMessages = new List<string>();
lock (lockObject)
{
currentMessages.AddRange(receivedMessages);
}
// 检查每条接收到的消息
foreach (string receivedMsg in currentMessages)
{
// 检查是否匹配期望的响应
foreach (string expected in expectedResponses)
{
if (IsResponseMatch(receivedMsg, expected))
{
matchedResponse = receivedMsg;
return true; // 成功匹配
}
}
}
// 更新数据接收状态
if (currentMessages.Count > 0)
{
if (!hasReceivedData)
{
hasReceivedData = true;
lastDataTime = DateTime.Now; // 首次收到数据,记录时间
}
}
// 检查超时条件
if (!hasReceivedData)
{
// 一直没有收到任何数据
if ((DateTime.Now - startTime).TotalMilliseconds >= noDataTimeoutMs)
{
matchedResponse = "超时错误: 串口无响应,未收到任何数据";
return false;
}
}
else
{
// 已经收到数据,但不是期望的响应
if ((DateTime.Now - lastDataTime).TotalMilliseconds >= wrongDataTimeoutMs)
{
// 返回所有收到的数据用于调试
StringBuilder sb = new StringBuilder();
sb.Append("超时错误: 收到数据但不匹配期望响应。收到的数据: ");
foreach (string msg in currentMessages)
{
sb.Append("[" + msg + "] ");
}
matchedResponse = sb.ToString();
return false;
}
// 如果有新数据进来,更新lastDataTime
lock (lockObject)
{
if (receivedMessages.Count > currentMessages.Count)
{
lastDataTime = DateTime.Now;
}
}
}
Thread.Sleep(50); // 等待一小段时间再检查
}
}
/// <summary>
/// 检查响应是否匹配(支持部分匹配)
/// </summary>
private bool IsResponseMatch(string received, string expected)
{
if (string.IsNullOrEmpty(received) || string.IsNullOrEmpty(expected))
{
return false;
}
// 移除空格进行比较
string receivedNoSpace = received.Replace(" ", "").ToUpper();
string expectedNoSpace = expected.Replace(" ", "").ToUpper();
// 使用Contains判断部分匹配
return receivedNoSpace.Contains(expectedNoSpace);
}
/// <summary>
/// 清空接收缓冲区
/// </summary>
public void ClearBuffer()
{
lock (lockObject)
{
receivedMessages.Clear();
}
}
/// <summary>
/// 获取所有接收到的消息
/// </summary>
public List<string> GetReceivedMessages()
{
lock (lockObject)
{
return new List<string>(receivedMessages);
}
}
}
/// <summary>
/// 使用示例
/// </summary>
class Program
{
static void Main(string[] args)
{
// 创建串口管理器
SerialPortManager manager = new SerialPortManager("COM3", 9600, Parity.None, 8, StopBits.One);
try
{
// 打开串口
if (manager.Open())
{
Console.WriteLine("串口已打开");
}
// 等待串口稳定
Thread.Sleep(500);
// ==================== 示例1: 基本使用 ====================
Console.WriteLine("\n===== 示例1: 发送指令并等待响应 =====");
byte[] command1 = new byte[] { 0x01, 0xA2, 0x34, 0x54, 0x12 };
string[] expectedResponses1 = new string[]
{
"01 A2 FF", // 期望响应1
"01 A2 00", // 期望响应2
"AA BB CC" // 期望响应3
};
string matchedResponse;
bool success = manager.SendCommandAndWaitResponse(
command1,
expectedResponses1,
3000, // 3秒无数据超时
2000, // 2秒错误数据超时
out matchedResponse
);
if (success)
{
Console.WriteLine("成功! 匹配的响应: " + matchedResponse);
}
else
{
Console.WriteLine("失败! " + matchedResponse);
}
Thread.Sleep(1000);
// ==================== 示例2: 发送16进制字符串 ====================
Console.WriteLine("\n===== 示例2: 发送16进制字符串 =====");
string[] expectedResponses2 = new string[] { "BB CC DD" };
success = manager.SendCommandAndWaitResponse(
SerialPortManager.HexStringToByteArray("AA BB CC DD"),
expectedResponses2,
5000, // 5秒无数据超时
3000, // 3秒错误数据超时
out matchedResponse
);
if (success)
{
Console.WriteLine("成功! 匹配的响应: " + matchedResponse);
}
else
{
Console.WriteLine("失败! " + matchedResponse);
}
// ==================== 示例3: 测试超时情况 ====================
Console.WriteLine("\n===== 示例3: 测试超时(这个会失败) =====");
byte[] command3 = new byte[] { 0xFF, 0xFF };
string[] expectedResponses3 = new string[] { "99 99 99" };
success = manager.SendCommandAndWaitResponse(
command3,
expectedResponses3,
2000, // 2秒无数据超时
1500, // 1.5秒错误数据超时
out matchedResponse
);
if (success)
{
Console.WriteLine("成功! 匹配的响应: " + matchedResponse);
}
else
{
Console.WriteLine("失败! " + matchedResponse);
}
// 等待用户输入
Console.WriteLine("\n按任意键退出...");
Console.ReadKey();
// 关闭串口
manager.Close();
Console.WriteLine("串口已关闭");
}
catch (Exception ex)
{
Console.WriteLine("错误: " + ex.Message);
}
}
}
}