BDU/DeviceCommand/Base/Tcp.cs

262 lines
9.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Common.Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using static Common.Attributes.ATSCommandAttribute;
namespace DeviceCommand.Base
{
[ATSCommand]
[DeviceCategory("全部驱动")] // 添加分类属性
public class Tcp
{
public string IPAddress { get; set; } = "127.0.0.1";
public int Port { get; set; } = 502;
public int SendTimeout { get; set; } = 3000;
public int ReceiveTimeout { get; set; } = 3000;
public TcpClient TcpClient { get; set; } = new();
public Tcp CreateDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
{
IPAddress = ipAddress;
Port = port;
SendTimeout = sendTimeout;
ReceiveTimeout = receiveTimeout;
return this;
}
/// <summary>
/// 修改TCP连接参数
/// </summary>
/// <param name="tcp"></param>
/// <param name="ipAddress">IP地址</param>
/// <param name="port">端口号</param>
/// <param name="sendTimeout">发送超时时间</param>
/// <param name="receiveTimeout">接收超时时间</param>
public static void ChangeDeviceConfig(Tcp tcp, string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
{
tcp.IPAddress = ipAddress;
tcp.Port = port;
if (sendTimeout > 0)
{
tcp.SendTimeout = sendTimeout;
}
if (receiveTimeout > 0)
{
tcp.ReceiveTimeout = receiveTimeout;
}
}
/// <summary>
/// 连接TCP设备
/// </summary>
/// <param name="tcp">TCP设备对象</param>
/// <param name="ct">取消令牌</param>
/// <returns>连接结果</returns>
public static async Task<bool> ConnectAsync(Tcp tcp, CancellationToken ct = default)
{
if (!tcp.TcpClient.Connected)
{
tcp.TcpClient.Close();
tcp.TcpClient.Dispose();
tcp.TcpClient = new TcpClient();
await tcp.TcpClient.ConnectAsync(tcp.IPAddress, tcp.Port, ct);
}
else
{
// 获取当前连接的远程端点
var remoteEndPoint = (IPEndPoint)tcp.TcpClient.Client.RemoteEndPoint!;
// 比较IP地址和端口
var ip = remoteEndPoint.Address.MapToIPv4().ToString();
bool isSameAddress = ip.Equals(tcp.IPAddress);
bool isSamePort = remoteEndPoint.Port == tcp.Port;
// 如果端点不匹配则断开重连
if (!isSameAddress || !isSamePort)
{
tcp.TcpClient.Close();
tcp.TcpClient.Dispose();
tcp.TcpClient = new TcpClient();
await tcp.TcpClient.ConnectAsync(tcp.IPAddress, tcp.Port, ct);
}
}
return true;
}
/// <summary>
/// 关闭TCP连接
/// </summary>
/// <param name="tcp">TCP设备对象</param>
public static void Close(Tcp tcp)
{
tcp.TcpClient.Close();
}
/// <summary>
/// 发送字节数组到TCP设备
/// </summary>
/// <param name="tcp">TCP设备对象</param>
/// <param name="bytes">要发送的字节数组</param>
/// <param name="ct">取消令牌</param>
public static async Task SendAsync(Tcp tcp, byte[] bytes, CancellationToken ct = default)
{
var timeoutMs = tcp.SendTimeout;
if (timeoutMs <= 0)
{
await tcp.TcpClient.Client.SendAsync(bytes, ct);
return;
}
var sendTask = tcp.TcpClient.Client.SendAsync(bytes, ct).AsTask();
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(sendTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"TCP通讯异常写入操作在 {timeoutMs} ms内未完成");
}
await sendTask;
}
/// <summary>
/// 发送字符串到TCP设备
/// </summary>
/// <param name="tcp">TCP设备对象</param>
/// <param name="str">要发送的字符串</param>
/// <param name="ct">取消令牌</param>
public static async Task SendAsync(Tcp tcp, string str, CancellationToken ct = default)
{
await SendAsync(tcp, Encoding.UTF8.GetBytes(str), ct);
}
/// <summary>
/// 接收指定长度的字节数组
/// </summary>
/// <param name="tcp">TCP设备对象</param>
/// <param name="buffer">接收缓冲区</param>
/// <param name="ct">取消令牌</param>
/// <returns>接收到的字节数组</returns>
public static async Task<byte[]> ReadAsync(Tcp tcp, byte[] buffer, CancellationToken ct = default)
{
if (!tcp.TcpClient.Connected) return null;
var timeoutMs = tcp.ReceiveTimeout;
if (timeoutMs <= 0)
{
return await ReadBytes(tcp, buffer, ct);
}
var readTask = ReadBytes(tcp, buffer, ct);
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(readTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"TCP通讯异常读取操作在 {timeoutMs} ms内未完成");
}
return await readTask;
}
private static async Task<byte[]> ReadBytes(Tcp tcp, byte[] buffer, CancellationToken ct)
{
NetworkStream stream = tcp.TcpClient.GetStream();
int bytesRead = 0;
while (bytesRead < buffer.Length)
{
int read = await stream.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead, ct);
if (read == 0) return null;
bytesRead += read;
}
return buffer;
}
/// <summary>
/// 接收字符串直到遇到分隔符
/// </summary>
/// <param name="tcp">TCP设备对象</param>
/// <param name="delimiter">分隔符</param>
/// <param name="ct">取消令牌</param>
/// <returns>接收到的字符串</returns>
public static async Task<string> ReadAsync(Tcp tcp, string delimiter = "\n", CancellationToken ct = default)
{
delimiter ??= "\n";
var timeoutMs = tcp.ReceiveTimeout;
if (timeoutMs <= 0)
{
return await ReadString(tcp, delimiter, ct);
}
var readTask = ReadString(tcp, delimiter, ct);
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(readTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"TCP通讯异常读取操作在 {timeoutMs} ms内未完成");
}
return await readTask;
}
private static async Task<string> ReadString(Tcp tcp, string delimiter, CancellationToken ct)
{
NetworkStream stream = tcp.TcpClient.GetStream();
MemoryStream memoryStream = new();
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, ct)) > 0)
{
memoryStream.Write(buffer, 0, bytesRead);
string data = Encoding.UTF8.GetString(memoryStream.ToArray());
int lineEndIndex = data.IndexOf(delimiter);
if (lineEndIndex >= 0)
{
return data[..lineEndIndex].Trim();
}
}
return null;
}
/// <summary>
/// 发送并接收数据
/// </summary>
/// <param name="tcp">TCP设备对象</param>
/// <param name="str">要发送的字符串</param>
/// <param name="endstr">结束符</param>
/// <param name="ct">取消令牌</param>
/// <returns>接收到的响应</returns>
public async Task<string> WriteRead(Tcp tcp, string str, string endstr, CancellationToken ct = default)
{
await SendAsync(tcp, str, ct);
return await ReadAsync(tcp, endstr, ct);
}
/// <summary>
/// 连接TCP设备
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>连接结果</returns>
public async Task<bool> ConnectAsync(CancellationToken ct = default)
{
return await ConnectAsync(this, ct);
}
}
}