基于.NET Framework 4.0的串口通信

核心功能

SendCommandAndWaitResponse 方法

  • 发送指令后持续读取串口数据
  • 两个独立的超时参数:
    1. noDataTimeoutMs - 串口完全无响应的超时时间
    2. 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);
            }
        }
    }
}
相关推荐
ss2732 小时前
手写MyBatis第107弹:@MapperScan原理与SqlSessionTemplate线程安全机制
java·开发语言·后端·mybatis
麦麦鸡腿堡3 小时前
Java的动态绑定机制(重要)
java·开发语言·算法
时间之里3 小时前
【c++】:Lambda 表达式介绍和使用
开发语言·c++
Tiger_shl3 小时前
C# 预处理指令 (# 指令) 详解
开发语言·c#
yuyuyui4 小时前
Roslyn 技术解析:如何利用它做代码规范检查与运行时代码生成?
.net
@Kerry~4 小时前
phpstudy .htaccess 文件内容
java·开发语言·前端
CRMEB系统商城4 小时前
CRMEB多商户系统(PHP)v3.3正式发布,同城配送上线[特殊字符]
java·开发语言·小程序·php
sali-tec4 小时前
C# 基于halcon的视觉工作流-章45-网格面划痕
开发语言·算法·计算机视觉·c#
一壶浊酒..4 小时前
python 爬取百度图片
开发语言·python·百度