You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

410 lines
13 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Threading;
using HslCommunication.Core;
using HslCommunication.LogNet;
namespace HslCommunication.Serial
{
/// <summary>
/// 所有串行通信类的基类,提供了一些基础的服务
/// </summary>
public class SerialBase : IDisposable
{
#region Constructor
/// <summary>
/// 实例化一个无参的构造方法
/// </summary>
public SerialBase( )
{
SP_ReadData = new SerialPort( );
hybirdLock = new SimpleHybirdLock( );
}
#endregion
#region Public Method
/// <summary>
/// 初始化串口信息9600波特率8位数据位1位停止位无奇偶校验
/// </summary>
/// <param name="portName">端口号信息,例如"COM3"</param>
public void SerialPortInni( string portName )
{
SerialPortInni( portName, 9600 );
}
/// <summary>
/// 初始化串口信息波特率8位数据位1位停止位无奇偶校验
/// </summary>
/// <param name="portName">端口号信息,例如"COM3"</param>
/// <param name="baudRate">波特率</param>
public void SerialPortInni( string portName, int baudRate )
{
SerialPortInni( portName, baudRate, 8, StopBits.One, Parity.None );
}
/// <summary>
/// 初始化串口信息,波特率,数据位,停止位,奇偶校验需要全部自己来指定
/// </summary>
/// <param name="portName">端口号信息,例如"COM3"</param>
/// <param name="baudRate">波特率</param>
/// <param name="dataBits">数据位</param>
/// <param name="stopBits">停止位</param>
/// <param name="parity">奇偶校验</param>
public void SerialPortInni( string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity )
{
if (SP_ReadData.IsOpen)
{
return;
}
SP_ReadData.PortName = portName; // 串口
SP_ReadData.BaudRate = baudRate; // 波特率
SP_ReadData.DataBits = dataBits; // 数据位
SP_ReadData.StopBits = stopBits; // 停止位
SP_ReadData.Parity = parity; // 奇偶校验
PortName = SP_ReadData.PortName;
BaudRate = SP_ReadData.BaudRate;
}
/// <summary>
/// 根据自定义初始化方法进行初始化串口信息
/// </summary>
/// <param name="initi">初始化的委托方法</param>
public void SerialPortInni( Action<SerialPort> initi )
{
if (SP_ReadData.IsOpen)
{
return;
}
SP_ReadData.PortName = "COM5";
SP_ReadData.BaudRate = 9600;
SP_ReadData.DataBits = 8;
SP_ReadData.StopBits = StopBits.One;
SP_ReadData.Parity = Parity.None;
initi.Invoke( SP_ReadData );
PortName = SP_ReadData.PortName;
BaudRate = SP_ReadData.BaudRate;
}
/// <summary>
/// 打开一个新的串行端口连接
/// </summary>
public void Open( )
{
if (!SP_ReadData.IsOpen)
{
SP_ReadData.Open( );
InitializationOnOpen( );
}
}
/// <summary>
/// 获取一个值,指示串口是否处于打开状态
/// </summary>
/// <returns>是或否</returns>
public bool IsOpen( )
{
return SP_ReadData.IsOpen;
}
/// <summary>
/// 关闭端口连接
/// </summary>
public void Close( )
{
if(SP_ReadData.IsOpen)
{
ExtraOnClose( );
SP_ReadData.Close( );
}
}
/// <summary>
/// 读取串口的数据
/// </summary>
/// <param name="send">发送的原始字节数据</param>
/// <returns>带接收字节的结果对象</returns>
public OperateResult<byte[]> ReadBase(byte[] send)
{
hybirdLock.Enter( );
if (IsClearCacheBeforeRead) ClearSerialCache( );
OperateResult sendResult = SPSend( SP_ReadData, send );
if (!sendResult.IsSuccess)
{
hybirdLock.Leave( );
return OperateResult.CreateFailedResult<byte[]>( sendResult );
}
OperateResult<byte[]> receiveResult = SPReceived( SP_ReadData, true );
hybirdLock.Leave( );
return receiveResult;
}
/// <summary>
/// 清除串口缓冲区的数据并返回该数据如果缓冲区没有数据返回的字节数组长度为0
/// </summary>
/// <returns>是否操作成功的方法</returns>
public OperateResult<byte[]> ClearSerialCache( )
{
return SPReceived( SP_ReadData, false );
}
#endregion
#region virtual Method
/// <summary>
/// 检查当前接收的字节数据是否正确的
/// </summary>
/// <param name="rBytes">输入字节</param>
/// <returns>检查是否正确</returns>
protected virtual bool CheckReceiveBytes(byte[] rBytes )
{
return true;
}
#endregion
#region Initialization And Extra
/// <summary>
/// 在打开端口时的初始化方法,按照协议的需求进行必要的重写
/// </summary>
/// <returns>是否初始化成功</returns>
protected virtual OperateResult InitializationOnOpen( )
{
return OperateResult.CreateSuccessResult( );
}
/// <summary>
/// 在将要和服务器进行断开的情况下额外的操作,需要根据对应协议进行重写
/// </summary>
/// <returns>当断开连接时额外的操作结果</returns>
protected virtual OperateResult ExtraOnClose( )
{
return OperateResult.CreateSuccessResult( );
}
#endregion
#region Private Method
/// <summary>
/// 发送数据到串口里去
/// </summary>
/// <param name="serialPort">串口对象</param>
/// <param name="data">字节数据</param>
/// <returns>是否发送成功</returns>
protected virtual OperateResult SPSend( SerialPort serialPort, byte[] data )
{
if (data != null && data.Length > 0)
{
if (!Authorization.nzugaydgwadawdibbas( )) return new OperateResult<byte[]>( StringResources.Language.AuthorizationFailed );
try
{
serialPort.Write( data, 0, data.Length );
return OperateResult.CreateSuccessResult( );
}
catch(Exception ex)
{
return new OperateResult( ex.Message );
}
}
else
{
return OperateResult.CreateSuccessResult( );
}
}
/// <summary>
/// 从串口接收一串数据信息,可以指定是否一定要接收到数据
/// </summary>
/// <param name="serialPort">串口对象</param>
/// <param name="awaitData">是否必须要等待数据返回</param>
/// <returns>结果数据对象</returns>
protected virtual OperateResult<byte[]> SPReceived( SerialPort serialPort, bool awaitData )
{
if (!Authorization.nzugaydgwadawdibbas( )) return new OperateResult<byte[]>( StringResources.Language.AuthorizationFailed );
byte[] buffer = new byte[1024];
System.IO.MemoryStream ms = new System.IO.MemoryStream( );
DateTime start = DateTime.Now; // 开始时间,用于确认是否超时的信息
while (true)
{
Thread.Sleep( sleepTime );
try
{
if (serialPort.BytesToRead < 1)
{
if ((DateTime.Now - start).TotalMilliseconds > ReceiveTimeout)
{
ms.Dispose( );
return new OperateResult<byte[]>( $"Time out: {ReceiveTimeout}" );
}
else if (ms.Length > 0)
{
break;
}
else if (awaitData)
{
continue;
}
else
{
break;
}
}
// 继续接收数据
int sp_receive = serialPort.Read( buffer, 0, buffer.Length );
ms.Write( buffer, 0, sp_receive );
}
catch (Exception ex)
{
ms.Dispose( );
return new OperateResult<byte[]>( ex.Message );
}
}
// resetEvent.Set( );
byte[] result = ms.ToArray( );
ms.Dispose( );
return OperateResult.CreateSuccessResult( result );
}
#endregion
#region Object Override
/// <summary>
/// 返回表示当前对象的字符串
/// </summary>
/// <returns>字符串</returns>
public override string ToString()
{
return "SerialBase";
}
#endregion
#region Public Properties
/// <summary>
/// 当前的日志情况
/// </summary>
public ILogNet LogNet
{
get { return logNet; }
set { logNet = value; }
}
/// <summary>
/// 接收数据的超时时间默认5000ms
/// </summary>
public int ReceiveTimeout
{
get { return receiveTimeout; }
set { receiveTimeout = value; }
}
/// <summary>
/// 连续串口缓冲数据检测的间隔时间默认20ms
/// </summary>
public int SleepTime
{
get { return sleepTime; }
set { if (value > 0) sleepTime = value; }
}
/// <summary>
/// 是否在发送数据前清空缓冲数据默认是false
/// </summary>
public bool IsClearCacheBeforeRead
{
get { return isClearCacheBeforeRead; }
set { isClearCacheBeforeRead = value; }
}
/// <summary>
/// 本连接对象的端口号名称
/// </summary>
public string PortName { get; private set; }
/// <summary>
/// 本连接对象的波特率
/// </summary>
public int BaudRate { get; private set; }
#endregion
#region IDisposable Support
private bool disposedValue = false; // 要检测冗余调用
/// <summary>
/// 释放当前的对象
/// </summary>
/// <param name="disposing">是否在</param>
protected virtual void Dispose( bool disposing )
{
if (!disposedValue)
{
if (disposing)
{
// TODO: 释放托管状态(托管对象)。
hybirdLock?.Dispose( );
SP_ReadData?.Dispose( );
}
// TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
// TODO: 将大型字段设置为 null。
disposedValue = true;
}
}
// TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
// ~SerialBase()
// {
// // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
// Dispose(false);
// }
// 添加此代码以正确实现可处置模式。
/// <summary>
/// 释放当前的对象
/// </summary>
public void Dispose( )
{
// 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
Dispose( true );
// TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
// GC.SuppressFinalize(this);
}
#endregion
#region Private Member
private SerialPort SP_ReadData = null; // 串口交互的核心
private SimpleHybirdLock hybirdLock; // 数据交互的锁
private ILogNet logNet; // 日志存储
private int receiveTimeout = 5000; // 接收数据的超时时间
private int sleepTime = 20; // 睡眠的时间
private bool isClearCacheBeforeRead = false; // 是否在发送前清除缓冲
#endregion
}
}