BDU/DeviceCommand/Base/SerialPort.cs

607 lines
23 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 DeviceCommand.Interface;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.Ports;
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 Serial_Port
{
public string PortName { get; set; } = "COM1";
public int BaudRate { get; set; } = 9600;
public int DataBits { get; set; } = 8;
public StopBits StopBits { get; set; } = StopBits.One;
public Parity Parity { get; set; } = Parity.None;
public int ReadTimeout { get; set; } = 3000;
public int WriteTimeout { get; set; } = 3000;
public SerialPort _SerialPort { get; set; } = new SerialPort();
/// <summary>
/// 创建并配置串口实例
/// </summary>
/// <param name="portName">串口名称(如"COM1"</param>
/// <param name="baudRate">波特率</param>
/// <param name="dataBits">数据位,配置默认值8</param>
/// <param name="stopBits">停止位,配置默认值StopBits.One</param>
/// <param name="parity">校验位(根据设备需求设置),配置默认值Parity.None</param>
/// <param name="readTimeout">读取超时</param>
/// <param name="writeTimeout">写入超时</param>
public Serial_Port CreateDevice(string portName, int baudRate
, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None
, int readTimeout = 3000, int writeTimeout = 3000)
{
PortName = portName;
BaudRate = baudRate;
DataBits = dataBits;
StopBits = stopBits;
Parity = parity;
ReadTimeout = readTimeout;
WriteTimeout = writeTimeout;
return this;
}
/// <summary>
/// 修改串口实例参数
/// </summary>
/// <param name="serialPort">串口实例</param>
/// <param name="PortName">串口名称(如"COM1"</param>
/// <param name="BaudRate">波特率</param>
/// <param name="dataBits">数据位</param>
/// <param name="stopBits">停止位</param>
/// <param name="parity">校验位(根据设备需求设置)</param>
/// <param name="ReadTimeout">读取超时</param>
/// <param name="WriteTimeout">写入超时</param>
public static void ChangeDeviceConfig(Serial_Port serialPort, string PortName, int BaudRate
, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None
, int ReadTimeout = 3000, int WriteTimeout = 3000)
{
// 更新配置串口实例参数
serialPort.PortName = PortName; // 串口名称(如"COM1"
serialPort.BaudRate = BaudRate; // 波特率
//serialPort.ReadTimeout = ReadTimeout;
//serialPort.WriteTimeout = WriteTimeout;
serialPort.Parity = parity; // 校验位(根据设备需求设置)
serialPort.DataBits = dataBits; // 数据位
serialPort.StopBits = stopBits; // 停止位
if (ReadTimeout > 0)
serialPort.ReadTimeout = ReadTimeout; // 读取超时
if (WriteTimeout > 0)
serialPort.WriteTimeout = WriteTimeout;// 写入超时
}
/// <summary>
/// 串口连接
/// </summary>
/// <param name="serialPort">串口实例</param>
/// <param name="ct">支持中途取消发送指令</param>
public static async Task<bool> ConnectAsync(Serial_Port serialPort, CancellationToken ct = default)
{
if (serialPort._SerialPort.PortName != serialPort.PortName
|| serialPort._SerialPort.BaudRate != serialPort.BaudRate
|| serialPort._SerialPort.Parity != serialPort.Parity
|| serialPort._SerialPort.DataBits != serialPort.DataBits
|| serialPort._SerialPort.StopBits != serialPort.StopBits
|| serialPort._SerialPort.ReadTimeout != serialPort.ReadTimeout
|| serialPort._SerialPort.WriteTimeout != serialPort.WriteTimeout)
{
// 关闭现有连接并重新配置
serialPort._SerialPort.Close();
//更新串口配置
serialPort._SerialPort.PortName = serialPort.PortName;
serialPort._SerialPort.BaudRate = serialPort.BaudRate;
serialPort._SerialPort.Parity = serialPort.Parity;
serialPort._SerialPort.DataBits = serialPort.DataBits;
serialPort._SerialPort.StopBits = serialPort.StopBits;
serialPort._SerialPort.ReadTimeout = serialPort.ReadTimeout;
serialPort._SerialPort.WriteTimeout = serialPort.WriteTimeout;
// 重新打开串口
serialPort._SerialPort.Open();
}
else
{
// 检查串口是否已打开
if (!serialPort._SerialPort.IsOpen)
{
// 打开串口
serialPort._SerialPort.Open();
}
}
return true;
}
/// <summary>
/// 串口关闭
/// </summary>
/// <param name="serialPort">串口实例</param>
public static void Close(Serial_Port serialPort)
{
if (serialPort._SerialPort.IsOpen)
{
// 关闭串口连接
serialPort._SerialPort.Close();
//Console.WriteLine("串口已关闭");
}
}
/// <summary>
/// 串口发送byte类型数据信息
/// </summary>
/// <param name="serialPort">串口实例</param>
/// <param name="bytes">发送的数据信息为byte类型</param>
/// <param name="ct">支持中途取消发送指令</param>
public static async Task SendAsync(Serial_Port serialPort, byte[] bytes, CancellationToken ct = default)
{
if (!serialPort._SerialPort.IsOpen) return;
// 获取写入超时时间
var timeoutMs = serialPort.WriteTimeout;
if (timeoutMs <= 0)
{
serialPort._SerialPort.Write(bytes, 0, bytes.Length); // 向串口写入字节数据
return;
}
var sendTask = Task.Run(() =>
{
// 向串口写入字节数据
serialPort._SerialPort.Write(bytes, 0, bytes.Length);
}, ct);
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(sendTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"串口通讯异常:写入操作在 {timeoutMs} ms内未完成");
}
await sendTask;
//if (serialPort._SerialPort.IsOpen)
//{
//try
//{
// // 异步发送数据到串口
// await Task.Run(() =>
// {
// serialPort._SerialPort.Write(bytes, 0, bytes.Length); // 向串口写入字节数据
// }, ct);
//}
//catch (Exception ex)
//{
// // 异常处理
// //Console.WriteLine($"发送数据失败: {ex.Message}");
// MessageBox.Show($"串口发送数据失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
//}
//}
//else
//{
// //Console.WriteLine("串口未打开");
// MessageBox.Show($"串口未打开", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
//}
}
/// <summary>
/// 串口发送字符串类型数据信息
/// </summary>
/// <param name="serialPort">串口实例</param>
/// <param name="str">发送的数据信息为字符串类型</param>
/// <param name="ct">支持中途取消发送指令</param>
public static async Task SendAsync(Serial_Port serialPort, string str, CancellationToken ct = default)
{
if (!serialPort._SerialPort.IsOpen) return;
// 将字符串转换为字节数组
byte[] bytes = Encoding.UTF8.GetBytes(str);
// 获取写入超时时间
var timeoutMs = serialPort.WriteTimeout;
if (timeoutMs <= 0)
{
serialPort._SerialPort.Write(bytes, 0, bytes.Length);
return;
}
var sendTask = Task.Run(() =>
{
// 向串口写入字节数据
serialPort._SerialPort.Write(bytes, 0, bytes.Length);
}, ct);
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(sendTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"串口通讯异常:写入操作在 {timeoutMs} ms内未完成");
}
await sendTask;
}
/// <summary>
/// 串口读取byte类型数据信息
/// </summary>
/// <param name="serialPort">串口实例</param>
/// <param name="buffer">读取的数据信息为byte数组类型</param>
/// <param name="ct">支持中途取消发送指令</param>
public static async Task<byte[]> ReadAsync(Serial_Port serialPort, byte[] buffer, CancellationToken ct = default)
{
if (!serialPort._SerialPort.IsOpen) return null;
int bytesRead = 0;
// 获取写入超时时间
var timeoutMs = serialPort.ReadTimeout;
if (timeoutMs <= 0)
{
return await ReadByte(serialPort, buffer, ct);
//while (bytesRead < buffer.Length)
//{
// if (serialPort._SerialPort.BytesToRead > 0)
// {
// bytesRead += serialPort._SerialPort.Read(buffer, bytesRead, buffer.Length - bytesRead);
// }
//}
//return buffer;
}
//var readTask = Task.Run(() =>
//{
// while (bytesRead < buffer.Length)
// {
// if (serialPort._SerialPort.BytesToRead > 0)
// {
// bytesRead += serialPort._SerialPort.Read(buffer, bytesRead, buffer.Length - bytesRead);
// }
// }
//}, ct);
var readTask = ReadByte(serialPort, buffer, ct);
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(readTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"串口通讯异常:读取操作在 {timeoutMs} ms内未完成");
}
return buffer;
//try
//{
// // 异步读取串口数据
// await Task.Run(() =>
// {
// while (bytesRead < buffer.Length)
// {
// if (serialPort._SerialPort.BytesToRead > 0)
// {
// bytesRead += serialPort._SerialPort.Read(buffer, bytesRead, buffer.Length - bytesRead);
// }
// }
// }, ct);
//}
//catch (Exception ex)
//{
// //Console.WriteLine($"读取数据失败: {ex.Message}");
// MessageBox.Show($"读取串口数据失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
// return null;
//}
//return buffer;
}
public static async Task<byte[]> ReadByte(Serial_Port serialPort, byte[] buffer, CancellationToken ct)
{
int bytesRead = 0;
while (bytesRead < buffer.Length)
{
if (serialPort._SerialPort.BytesToRead > 0)
{
bytesRead += serialPort._SerialPort.Read(buffer, bytesRead, buffer.Length - bytesRead);
}
}
return buffer;
}
/// <summary>
/// 串口读取字符串类型数据信息
/// </summary>
/// <param name="serialPort">串口实例</param>
/// <param name="delimiter">读取的数据信息为字符串类型,string delimiter = "\n"</param>
/// <param name="ct">支持中途取消发送指令</param>
public static async Task<string> ReadAsync(Serial_Port serialPort, string delimiter = "\n", CancellationToken ct = default)
{
if (!serialPort._SerialPort.IsOpen) return null;
// 获取写入超时时间
var timeoutMs = serialPort.ReadTimeout;
if (timeoutMs <= 0)
{
return await ReadDefaultString(serialPort, delimiter, ct);
}
var readTask = ReadDefaultString(serialPort, delimiter, ct);
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(readTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"串口通讯异常:读取操作在 {timeoutMs} ms内未完成");
}
return await readTask;
//try
//{
// // 异步读取串口数据
// await Task.Run(() =>
// {
// while (true)
// {
// if (ct.IsCancellationRequested)
// return;
// if (serialPort._SerialPort.BytesToRead > 0)
// {
// bytesRead = serialPort._SerialPort.Read(buffer, 0, buffer.Length);
// memoryStream.Write(buffer, 0, bytesRead);
// data = Encoding.UTF8.GetString(memoryStream.ToArray());
// int lineEndIndex = data.IndexOf(delimiter);
// // 找到分隔符,则返回数据
// if (lineEndIndex >= 0)
// {
// data = data.Substring(0, lineEndIndex).Trim();
// return;
// }
// }
// }
// }, ct);
//}
//catch (Exception ex)
//{
// //Console.WriteLine($"读取串口数据失败: {ex.Message}");
// MessageBox.Show($"读取串口数据失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
// return null;
//}
//return data;
}
private static async Task<string> ReadDefaultString(Serial_Port serialPort, string delimiter, CancellationToken ct)
{
delimiter ??= "\n";
MemoryStream memoryStream = new();
byte[] buffer = new byte[2048];
int bytesRead = 0;
string data = string.Empty;
// 异步读取串口数据
await Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
return;
if (serialPort._SerialPort.BytesToRead > 0)
{
bytesRead = serialPort._SerialPort.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, bytesRead);
data = Encoding.UTF8.GetString(memoryStream.ToArray());
int lineEndIndex = data.IndexOf(delimiter);
// 找到分隔符,则返回数据
if (lineEndIndex >= 0)
{
data = data.Substring(0, lineEndIndex).Trim();
return;
}
}
}
}, ct);
return data;
}
/// <summary>
/// 串口读取字符串类型数据信息
/// </summary>
/// <param name="serialPort">串口实例</param>
/// <param name="delimiter">读取的数据信息为字符串类型,string delimiter</param>
/// <param name="ct">支持中途取消发送指令</param>
public static async Task<string> ReadStrAsync(Serial_Port serialPort, string delimiter, CancellationToken ct = default)
{
if (!serialPort._SerialPort.IsOpen)
{
return null; // 串口未打开,返回 null
}
// 获取写入超时时间
var timeoutMs = serialPort.ReadTimeout;
if (timeoutMs <= 0)
{
return await ReadString(serialPort, delimiter, ct);
}
var readTask = ReadString(serialPort, delimiter, ct);
var timeoutTask = Task.Delay(timeoutMs, ct);
var completedTask = await Task.WhenAny(readTask, timeoutTask);
if (completedTask == timeoutTask)
{
throw new TimeoutException($"串口通讯异常:读取操作在 {timeoutMs} ms内未完成");
}
return await readTask;
////delimiter ??= "\n"; // 默认分隔符为换行符
//MemoryStream memoryStream = new();
//byte[] buffer = new byte[2048];
//int bytesRead;
//string data = string.Empty;
//// 异步读取串口数据
//await Task.Run(() =>
//{
// while (true)
// {
// if (ct.IsCancellationRequested)
// return;
// if (serialPort._SerialPort.BytesToRead > 0)
// {
// // 从串口读取数据
// bytesRead = serialPort._SerialPort.Read(buffer, 0, buffer.Length);
// memoryStream.Write(buffer, 0, bytesRead);
// // 获取当前已读取的数据
// data = Encoding.UTF8.GetString(memoryStream.ToArray());
// // 查找分隔符
// int delimiterIndex = data.IndexOf(delimiter);
// if (delimiterIndex >= 0)
// {
// // 如果找到分隔符,则返回读取到的字符串
// data = data.Substring(0, delimiterIndex).Trim();
// return;
// }
// }
// }
//}, ct);
//try
//{
// // 异步读取串口数据
// await Task.Run(() =>
// {
// while (true)
// {
// if (ct.IsCancellationRequested)
// return;
// if (serialPort._SerialPort.BytesToRead > 0)
// {
// // 从串口读取数据
// bytesRead = serialPort._SerialPort.Read(buffer, 0, buffer.Length);
// memoryStream.Write(buffer, 0, bytesRead);
// // 获取当前已读取的数据
// data = Encoding.UTF8.GetString(memoryStream.ToArray());
// // 查找分隔符
// int delimiterIndex = data.IndexOf(delimiter);
// if (delimiterIndex >= 0)
// {
// // 如果找到分隔符,则返回读取到的字符串
// data = data.Substring(0, delimiterIndex).Trim();
// return;
// }
// }
// }
// }, ct);
//}
//catch (Exception ex)
//{
// //Console.WriteLine($"读取串口数据时发生错误: {ex.Message}");
// MessageBox.Show($"读取串口数据时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
// return null;
//}
//return data; // 如果没有数据返回,返回 null
}
private static async Task<string> ReadString(Serial_Port serialPort, string delimiter, CancellationToken ct)
{
//delimiter ??= "\n";
MemoryStream memoryStream = new();
byte[] buffer = new byte[2048];
int bytesRead = 0;
string data = string.Empty;
// 异步读取串口数据
await Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
return;
if (serialPort._SerialPort.BytesToRead > 0)
{
bytesRead = serialPort._SerialPort.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, bytesRead);
data = Encoding.UTF8.GetString(memoryStream.ToArray());
int lineEndIndex = data.IndexOf(delimiter);
// 找到分隔符,则返回数据
if (lineEndIndex >= 0)
{
data = data.Substring(0, lineEndIndex).Trim();
return;
}
}
}
}, ct);
return data;
}
/// <summary>
/// 串口连接
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>连接结果</returns>
public async Task<bool> ConnectAsync(CancellationToken ct = default)
{
return await ConnectAsync(this, ct);
}
/// <summary>
/// 断开串口连接
/// </summary>
public void Disconnect()
{
Close(this);
}
/// <summary>
/// 发送字符串到串口
/// </summary>
/// <param name="str">要发送的字符串</param>
/// <param name="ct">取消令牌</param>
public async Task SendStringAsync(string str, CancellationToken ct = default)
{
await SendAsync(this, str, ct);
}
/// <summary>
/// 接收字符串直到遇到指定分隔符
/// </summary>
/// <param name="delimiter">分隔符</param>
/// <param name="ct">取消令牌</param>
/// <returns>接收到的字符串</returns>
public async Task<string> ReceiveStringToAsync(string delimiter, CancellationToken ct = default)
{
return await ReadAsync(this, delimiter, ct);
}
}
}