using Common.Attributes; using System; using System.IO.Ports; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Flexible { /// /// 灵活型串口通信类(免实例化,每次调用临时创建串口连接)。 /// 支持只发送指令、发送并读取应答两种最常用操作, /// 内部使用通信锁保证同一时刻只有一个串口事务在执行, /// 适用于偶发性、无需保持长连接的串口设备通信场景(如示波器、电源的 SCPI 指令)。 /// [ACPCommand] public static class FSerialPort { // 通信锁:保证同一时刻只有一个串口事务在执行 private static readonly SemaphoreSlim _commLock = new(1, 1); /// /// 创建串口实例并配置 UTF8 编码与超时参数。 /// private static SerialPort CreatePort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int sendTimeout,int receiveTimeout) { return new SerialPort(portName, baudRate, parity, dataBits, stopBits) { Encoding = Encoding.UTF8, WriteTimeout = sendTimeout > 0 ? sendTimeout : SerialPort.InfiniteTimeout, ReadTimeout = receiveTimeout > 0 ? receiveTimeout : SerialPort.InfiniteTimeout }; } #region 最常用:只发送字符串 /// /// 向串口发送一条字符串指令(只发送,不等待应答)。 /// /// 串口名称,如 "COM1" /// 波特率,如 9600、115200 /// 校验位 /// 数据位,常用 8 /// 停止位 /// 发送超时时间(毫秒) /// 接收超时时间(毫秒) /// 要发送的指令字符串 /// 异步取消令牌 public static async Task 发送指令(string portName,int baudRate,Parity parity,int dataBits,StopBits stopBits,int sendTimeout,int receiveTimeout,string command,CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { using var port = CreatePort( portName, baudRate, parity, dataBits, stopBits, sendTimeout, receiveTimeout); port.Open(); using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (sendTimeout > 0) cts.CancelAfter(sendTimeout); var bytes = port.Encoding.GetBytes(command); await port.BaseStream.WriteAsync(bytes, 0, bytes.Length, cts.Token); await port.BaseStream.FlushAsync(cts.Token); } finally { _commLock.Release(); } } #endregion #region 最常用:发送字符串并读取字符串 /// /// 向串口发送一条字符串指令并读取应答,读取到结束符为止(应答不含结束符)。 /// /// 串口名称,如 "COM1" /// 波特率,如 9600、115200 /// 校验位 /// 数据位,常用 8 /// 停止位 /// 发送超时时间(毫秒) /// 接收超时时间(毫秒),超时未收到结束符抛出超时异常 /// 要发送的指令字符串 /// 应答结束符,默认换行符 "\n" /// 异步取消令牌 /// 去除结束符并去除首尾空白后的应答字符串 public static async Task 发送并读取应答(string portName,int baudRate, Parity parity, int dataBits, StopBits stopBits,int sendTimeout, int receiveTimeout,string command,string delimiter = "\n", CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { using var port = CreatePort( portName, baudRate, parity, dataBits, stopBits, sendTimeout, receiveTimeout); port.Open(); // Send var sendBytes = port.Encoding.GetBytes(command); await port.BaseStream.WriteAsync(sendBytes, 0, sendBytes.Length, ct); await port.BaseStream.FlushAsync(ct); // Read delimiter ??= "\n"; var sb = new StringBuilder(); byte[] buffer = new byte[256]; using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (receiveTimeout > 0) cts.CancelAfter(receiveTimeout); while (!cts.Token.IsCancellationRequested) { int read = await port.BaseStream.ReadAsync( buffer, 0, buffer.Length, cts.Token); if (read == 0) break; sb.Append(port.Encoding.GetString(buffer, 0, read)); int index = sb.ToString().IndexOf(delimiter, StringComparison.Ordinal); if (index >= 0) return sb.ToString(0, index).Trim(); } throw new TimeoutException("串口读取超时"); } finally { _commLock.Release(); } } #endregion } }