| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- /*
- Pseudocode (详细计划):
- - 定义类 SerialPortProtocol,实现 ICommunicationProtocol。
- - 字段:
- - _client: SerialPortClient 实例,用于串口通信。
- - _waitClient: 等待客户端,用于发送后等待响应。
- - _config: 只读配置对象 SerialPortConfig。
- - 属性:
- - IsConnected: 检查 _client 是否在线。
- - Terminator: 终止符设置(默认 None)。
- - Encoding: 编码(默认 ASCII)。
- - 事件:
- - ConnectionChanged: 连接状态变化时触发。
- - DataReceived: 接收数据时触发(字符串形式)。
- - DataSent: 发送数据时触发(字符串形式)。
- - 构造函数:
- - 接收 SerialPortConfig 并保存到 _config。
- - ConnectAsync:
- - 创建 SerialPortClient。
- - 构建 TouchSocketConfig,设置串口选项(端口名、波特率、数据位、校验、停止位)。
- - 设置数据处理适配器 PeriodPackageAdapter(CacheTimeout 100ms)。
- - 调用 _client.Setup(config) 并 TryConnectAsync。
- - 若成功,创建等待客户端并触发 ConnectionChanged(true),返回 true;否则触发 ConnectionChanged(false),返回 false。
- - 捕获异常时也触发 ConnectionChanged(false) 并返回 false。
- - DisconnectAsync:
- - 触发 ConnectionChanged(false),关闭 _client。
- - Send/SendAsync(字节/字符串):
- - 检查 _client 是否存在,否则抛出 InvalidOperationException。
- - 触发 DataSent(字符串形式)。
- - 使用 _client.Send 或 _client.SendAsync 发送数据。
- - SendAndReceive/SendAndReceiveAsync(字节/字符串):
- - 检查 _waitClient,否则抛出 InvalidOperationException。
- - 触发 DataSent。
- - 使用 _waitClient.SendThenReturn(Sync/Async) 等待响应。
- - 触发 DataReceived 并返回响应(字节或字符串)。
- - Dispose:
- -释放 _client 并置空 _waitClient。
- - 为所有公开成员添加 XML 文档注释,描述参数、返回值与异常。
- */
- using System;
- using System.Text;
- using System.Threading.Tasks;
- using TeamAAS_VP.Enums;
- using TeamAAS_VP.Models;
- using TouchSocket.Core;
- using TouchSocket.SerialPorts;
- using TouchSocket.Sockets;
- namespace TeamAAS_VP.Core.Lights
- {
- /// <summary>
- /// 使用 TouchSocket 的串口通信协议封装。
- /// 提供同步/异步的发送与收发方法,并通过事件报告连接与数据状态。
- /// </summary>
- public class SerialPortProtocol : ICommunicationProtocol
- {
- /// <summary>
- /// 内部串口客户端实例。
- /// </summary>
- private SerialPortClient _client;
- /// <summary>
- /// 用于发送后等待响应的等待客户端。
- /// </summary>
- private IWaitingClient<ISerialPortClient, IReceiverResult> _waitClient;
- /// <summary>
- /// 串口配置,只读。
- /// </summary>
- private readonly SerialPortConfig _config;
- /// <summary>
- /// 获取是否已连接(客户端在线)。
- /// </summary>
- public bool IsConnected => _client?.Online == true;
- /// <summary>
- /// 数据终止符,默认为 <see cref="Terminator.None"/>。
- /// </summary>
- public Terminator Terminator { get; set; } = Terminator.None;
- /// <summary>
- /// 文本编码,默认使用 ASCII 编码。
- /// </summary>
- public Encoding Encoding { get; set; } = Encoding.ASCII;
- /// <summary>
- /// 当连接状态改变时触发。参数为触发对象和连接状态(true=已连接)。
- /// </summary>
- public event Action<object, bool> ConnectionChanged;
- /// <summary>
- /// 当接收到数据时触发,携带接收到的数据(字符串形式)。
- /// </summary>
- public event Action<object, string> DataReceived;
- /// <summary>
- /// 当发送数据时触发,携带发送的数据(字符串形式)。
- /// </summary>
- public event Action<object, string> DataSent;
- /// <summary>
- /// 使用指定的串口配置构建一个新的 <see cref="SerialPortProtocol"/> 实例。
- /// </summary>
- /// <param name="config">串口配置,不能为 null。</param>
- /// <exception cref="ArgumentNullException">当 <paramref name="config"/> 为 null 时抛出。</exception>
- public SerialPortProtocol(SerialPortConfig config)
- {
- _config = config ?? throw new ArgumentNullException(nameof(config));
- }
- /// <summary>
- /// 异步连接到串口并初始化等待客户端。
- /// </summary>
- /// <returns>连接成功返回 true,否则返回 false。</returns>
- public async Task<bool> ConnectAsync()
- {
- try
- {
- _client = new SerialPortClient();
- var config = new TouchSocketConfig()
- .SetSerialPortOption(new SerialPortOption()
- {
- PortName = _config.PortName,
- BaudRate = _config.BaudRate,
- DataBits = _config.DataBits,
- Parity = _config.Parity,
- StopBits = _config.StopBits
- })
- .SetSerialDataHandlingAdapter(() => new PeriodPackageAdapter()
- {
- CacheTimeout = TimeSpan.FromMilliseconds(100)
- });
- _client.Setup(config);
- var result = await _client.TryConnectAsync();
- if (result.IsSuccess)
- {
- _waitClient = _client.CreateWaitingClient(new WaitingOptions());
- ConnectionChanged?.Invoke(this, true);
- return true;
- }
- ConnectionChanged?.Invoke(this, false);
- return false;
- }
- catch
- {
- ConnectionChanged?.Invoke(this, false);
- return false;
- }
- }
- /// <summary>
- /// 异步断开连接并关闭串口。
- /// </summary>
- /// <returns>完成任务。</returns>
- public Task DisconnectAsync()
- {
- ConnectionChanged?.Invoke(this, false);
- _client?.Close();
- return Task.CompletedTask;
- }
- /// <summary>
- /// 异步发送字节数据并等待接收响应。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <param name="timeout">等待超时时间(毫秒),默认 5000ms。</param>
- /// <returns>收到的字节数组。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端为 null 时抛出。</exception>
- public async Task<byte[]> SendAndReceiveAsync(byte[] data, int timeout = 5000)
- {
- if (_waitClient == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, Encoding.GetString(data));
- var response = await _waitClient.SendThenReturnAsync(data, timeout);
- DataReceived?.Invoke(this, Encoding.GetString(response));
- return response;
- }
- /// <summary>
- /// 异步发送字节数据(不等待响应)。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <returns>完成任务。</returns>
- /// <exception cref="InvalidOperationException">当客户端未初始化时抛出。</exception>
- public Task SendAsync(byte[] data)
- {
- if (_client == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, Encoding.GetString(data));
- _client.Send(data);
- return Task.CompletedTask;
- }
- /// <summary>
- /// 异步发送文本并等待响应(字符串形式)。
- /// </summary>
- /// <param name="data">要发送的文本。</param>
- /// <param name="timeout">等待超时时间(毫秒),默认 5000ms。</param>
- /// <returns>收到的文本响应。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端为 null 时抛出。</exception>
- public async Task<string> SendAndReceiveAsync(string data, int timeout = 5000)
- {
- if (_waitClient == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, data);
- var response = await SendAndReceiveAsync(Encoding.GetBytes(data), timeout);
- DataReceived?.Invoke(this, Encoding.GetString(response));
- return Encoding.GetString(response);
- }
- /// <summary>
- /// 同步发送文本并等待响应(字符串形式)。
- /// </summary>
- /// <param name="data">要发送的文本。</param>
- /// <param name="timeout">等待超时时间(毫秒),默认 5000ms。</param>
- /// <returns>收到的文本响应。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端为 null 时抛出。</exception>
- public string SendAndReceive(string data, int timeout = 5000)
- {
- if (_waitClient == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, data);
- var response = _waitClient.SendThenReturn(Encoding.GetBytes(data), timeout);
- DataReceived?.Invoke(this, Encoding.GetString(response));
- return Encoding.GetString(response);
- }
- /// <summary>
- /// 同步发送字节数据并等待响应。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <param name="timeout">等待超时时间(毫秒),默认 5000ms。</param>
- /// <returns>收到的字节数组。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端为 null 时抛出。</exception>
- public byte[] SendAndReceive(byte[] data, int timeout = 5000)
- {
- if (_waitClient == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, Encoding.GetString(data));
- var response = _waitClient.SendThenReturn(data, timeout);
- DataReceived?.Invoke(this, Encoding.GetString(response));
- return response;
- }
- /// <summary>
- /// 同步发送文本(不等待响应)。
- /// </summary>
- /// <param name="data">要发送的文本。</param>
- /// <exception cref="InvalidOperationException">当客户端未初始化时抛出。</exception>
- public void Send(string data)
- {
- if (_client == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, data);
- _client.Send(Encoding.GetBytes(data));
- }
- /// <summary>
- /// 异步发送文本(不等待响应)。
- /// </summary>
- /// <param name="data">要发送的文本。</param>
- /// <returns>完成任务。</returns>
- /// <exception cref="InvalidOperationException">当客户端未初始化时抛出。</exception>
- public async Task SendAsync(string data)
- {
- if (_client == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, data);
- await _client.SendAsync(Encoding.GetBytes(data));
- }
- /// <summary>
- /// 同步发送字节数据(不等待响应)。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <exception cref="InvalidOperationException">当客户端未初始化时抛出。</exception>
- public void Send(byte[] data)
- {
- if (_client == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, Encoding.GetString(data));
- _client.Send(data);
- }
- /// <summary>
- /// 释放底层资源。调用后实例不应再被使用。
- /// </summary>
- public void Dispose()
- {
- _client?.Dispose();
- _waitClient = null;
- }
- }
- }
|