mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 10:50:48 +08:00
!67 更新依赖
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
|
||||
<div align="center"><h1 align="center">ThingsBlazor</a></h1></div>
|
||||
<div align="center"><h1 align="center">ThingsBlazor</h1></div>
|
||||
<div align="center"><h3 align="center">权限管理框架</h3></div>
|
||||
|
||||
|
||||
|
@@ -28,6 +28,10 @@ public class WebsiteOptions : IConfigurableOptions
|
||||
public bool Demo { get; set; }
|
||||
|
||||
public bool WebPageEnable { get; set; } = true;
|
||||
|
||||
public int MaxBlazorConnections { get; set; } = 5;
|
||||
public bool BlazorConnectionLimitEnable { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示关于页面
|
||||
/// </summary>
|
||||
|
@@ -43,12 +43,12 @@ public class SugarAopService : ISugarAopService
|
||||
}
|
||||
if (sql.StartsWith("INSERT"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
DbContext.WriteLog($"添加{config.ConfigId}库操作");
|
||||
}
|
||||
if (sql.StartsWith("DELETE"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
DbContext.WriteLog($"删除{config.ConfigId}库操作");
|
||||
}
|
||||
DbContext.WriteLogWithSql(UtilMethods.GetNativeSql(sql, pars));
|
||||
|
@@ -31,6 +31,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="$(NET9Version)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
@@ -43,6 +44,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(NET9Version)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(NET9Version)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(NET9Version)" />
|
||||
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.9.70</PluginVersion>
|
||||
<ProPluginVersion>10.9.70</ProPluginVersion>
|
||||
<DefaultVersion>10.9.70</DefaultVersion>
|
||||
<AuthenticationVersion>2.9.28</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.9.25</SourceGeneratorVersion>
|
||||
<PluginVersion>10.9.91</PluginVersion>
|
||||
<ProPluginVersion>10.9.91</ProPluginVersion>
|
||||
<DefaultVersion>10.9.91</DefaultVersion>
|
||||
<AuthenticationVersion>2.9.29</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.9.29</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.18</NET8Version>
|
||||
<NET9Version>9.0.7</NET9Version>
|
||||
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
||||
|
@@ -84,7 +84,7 @@ public partial class DeviceComponent : DeviceComponentBase
|
||||
{
|
||||
try
|
||||
{
|
||||
var result1 = item.VariableRuntimes.PraseStructContent(Plc, result.Content, exWhenAny: true);
|
||||
var result1 = item.VariableRuntimes.PraseStructContent(Plc, result.Content.Span, exWhenAny: true);
|
||||
if (!result1.IsSuccess)
|
||||
{
|
||||
item.LastErrorMessage = result1.ErrorMessage;
|
||||
|
@@ -87,7 +87,7 @@ public abstract class DeviceComponentBase : ComponentBase, IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await Plc.ReadAsync(RegisterAddress, ArrayLength, DataType);
|
||||
var data = await Plc.ReadArrayAsync(RegisterAddress, ArrayLength, DataType);
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
Plc.Logger?.LogInformation(data.Content.ToSystemTextJsonString());
|
||||
@@ -111,7 +111,7 @@ public abstract class DeviceComponentBase : ComponentBase, IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await Plc.WriteAsync(RegisterAddress, WriteValue.GetJTokenFromString(), DataType);
|
||||
var data = await Plc.WriteJTokenAsync(RegisterAddress, WriteValue.GetJTokenFromString(), DataType);
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
Plc.Logger?.LogInformation($" {WriteValue.GetJTokenFromString()} {Localizer["WriteSuccess"]}");
|
||||
|
@@ -83,7 +83,7 @@ public abstract class VariableObject
|
||||
/// GetBytes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual byte[] GetBytes(Expression<Func<object>> accessor)
|
||||
public virtual ReadOnlyMemory<byte> GetBytes(Expression<Func<object>> accessor)
|
||||
{
|
||||
if (accessor.Body == null)
|
||||
{
|
||||
@@ -107,8 +107,8 @@ public abstract class VariableObject
|
||||
}
|
||||
|
||||
var func = accessor.Compile();
|
||||
|
||||
return variable.VariableClass.ThingsGatewayBitConverter.GetBytesFormData(GetExpressionsValue(func(), variable), variable.VariableClass.DataType);
|
||||
var data = GetExpressionsValue(func(), variable);
|
||||
return variable.VariableClass.ThingsGatewayBitConverter.GetBytesFormData(data, variable.VariableClass.DataType, data is JArray jArray && jArray.Count > 1 ? true : false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -149,7 +149,7 @@ public abstract class VariableObject
|
||||
var result = await Device.ReadAsync(item.RegisterAddress, item.Length, cancellationToken).ConfigureAwait(false);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
var result1 = item.VariableRuntimes.PraseStructContent(Device, result.Content, exWhenAny: true);
|
||||
var result1 = item.VariableRuntimes.PraseStructContent(Device, result.Content.Span, exWhenAny: true);
|
||||
if (!result1.IsSuccess)
|
||||
{
|
||||
item.LastErrorMessage = result1.ErrorMessage;
|
||||
@@ -221,7 +221,7 @@ public abstract class VariableObject
|
||||
|
||||
JToken jToken = GetExpressionsValue(value, variableRuntimeProperty);
|
||||
|
||||
var result = await Device.WriteAsync(variableRuntimeProperty.VariableClass.RegisterAddress, jToken, variableRuntimeProperty.VariableClass.DataType, cancellationToken).ConfigureAwait(false);
|
||||
var result = await Device.WriteJTokenAsync(variableRuntimeProperty.VariableClass.RegisterAddress, jToken, variableRuntimeProperty.VariableClass.DataType, cancellationToken).ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -30,8 +30,8 @@ public abstract class DDPMessage : MessageBase, IResultMessage
|
||||
|
||||
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
var code = byteBlock.ReadByte();
|
||||
Type = byteBlock.ReadByte();
|
||||
var code = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||
Type = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||
|
||||
if (code != 0x7B)
|
||||
{
|
||||
@@ -44,15 +44,15 @@ public abstract class DDPMessage : MessageBase, IResultMessage
|
||||
}
|
||||
}
|
||||
|
||||
public abstract int GetBodyLength<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock;
|
||||
public abstract byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock;
|
||||
public abstract int GetBodyLength<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
||||
public abstract byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
||||
}
|
||||
|
||||
public class DDPTcpMessage : DDPMessage
|
||||
{
|
||||
public override int GetBodyLength<TByteBlock>(ref TByteBlock byteBlock)
|
||||
{
|
||||
return byteBlock.ReadUInt16(EndianType.Big) - 4;
|
||||
return ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 4;
|
||||
}
|
||||
|
||||
public override byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <summary>
|
||||
@@ -30,40 +32,37 @@ public class DDPSend : ISendMessage
|
||||
Id = id;
|
||||
Command = command;
|
||||
}
|
||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
|
||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter
|
||||
{
|
||||
byteBlock.WriteByte(0x7b);
|
||||
byteBlock.WriteByte(Command);
|
||||
byteBlock.WriteUInt16(0x10, EndianType.Big);//len
|
||||
byteBlock.Write(PadTo11Byte(Id.Remove(0, 3)));
|
||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x7b);
|
||||
WriterExtension.WriteValue(ref byteBlock, (byte)Command);
|
||||
var id = PadTo11Byte(Id.Remove(0, 3));
|
||||
if (Tcp)
|
||||
{
|
||||
byteBlock.Write(ReadOnlyMemory.Span);
|
||||
byteBlock.WriteByte(0x7b);
|
||||
byteBlock.Position = 2;
|
||||
byteBlock.WriteUInt16((ushort)byteBlock.Length, EndianType.Big);//len
|
||||
WriterExtension.WriteValue(ref byteBlock, (ushort)(id.Length + ReadOnlyMemory.Length + 3), EndianType.Big);//len
|
||||
}
|
||||
else
|
||||
{
|
||||
byteBlock.WriteByte(0x7b);
|
||||
WriterExtension.WriteValue(ref byteBlock, (ushort)0x10, EndianType.Big);//len
|
||||
}
|
||||
byteBlock.Write(id);
|
||||
if (Tcp)
|
||||
{
|
||||
byteBlock.Write(ReadOnlyMemory.Span);
|
||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x7b);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x7b);
|
||||
byteBlock.Write(ReadOnlyMemory.Span);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] PadTo11Byte(string id)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(id);
|
||||
|
||||
if (bytes.Length < 11)
|
||||
{
|
||||
byte[] newBytes = new byte[11];
|
||||
Array.Copy(bytes, newBytes, bytes.Length);
|
||||
for (int i = bytes.Length; i < 11; i++)
|
||||
{
|
||||
newBytes[i] = 0;
|
||||
}
|
||||
return newBytes;
|
||||
}
|
||||
return bytes;
|
||||
var buffer = new byte[11];
|
||||
var str = id.AsSpan();
|
||||
int byteCount = Encoding.UTF8.GetBytes(str.Slice(0, Math.Min(str.Length, 11)), buffer);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -42,17 +42,17 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
||||
DataHandlingAdapter.SendAsyncCallBack = DefaultSendAsync;
|
||||
return base.OnTcpConnected(e);
|
||||
}
|
||||
protected Task DefaultSendAsync(ReadOnlyMemory<byte> memory)
|
||||
protected Task DefaultSendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
||||
{
|
||||
return DDPAdapter.SendInputAsync(new DDPSend(memory, Id, true));
|
||||
return DDPAdapter.SendInputAsync(new DDPSend(memory, Id, true), cancellationToken);
|
||||
}
|
||||
protected Task DDPSendAsync(ReadOnlyMemory<byte> memory)
|
||||
protected Task DDPSendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
||||
{
|
||||
return base.ProtectedDefaultSendAsync(memory);
|
||||
return base.ProtectedDefaultSendAsync(memory, cancellationToken);
|
||||
}
|
||||
|
||||
private DDPMessage DDPMessage { get; set; }
|
||||
private Task DDPHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
|
||||
private Task DDPHandleReceivedData(IByteBlockReader byteBlock, IRequestInfo requestInfo)
|
||||
{
|
||||
if (requestInfo is DDPMessage dDPMessage)
|
||||
DDPMessage = dDPMessage;
|
||||
@@ -62,13 +62,14 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
||||
|
||||
private DeviceSingleStreamDataHandleAdapter<DDPTcpMessage> DDPAdapter = new();
|
||||
private WaitLock _waitLock = new(nameof(DDPTcpSessionClientChannel));
|
||||
protected override async ValueTask<bool> OnTcpReceiving(ByteBlock byteBlock)
|
||||
|
||||
protected override async ValueTask<bool> OnTcpReceiving(IByteBlockReader byteBlock)
|
||||
{
|
||||
DDPMessage? message = null;
|
||||
try
|
||||
{
|
||||
await _waitLock.WaitAsync().ConfigureAwait(false);
|
||||
await DDPAdapter.ReceivedInputAsync(byteBlock).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await DDPAdapter.ReceivedInputAsync(byteBlock).ConfigureAwait(false);
|
||||
|
||||
message = DDPMessage;
|
||||
DDPMessage = null;
|
||||
@@ -85,9 +86,18 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
||||
var id = $"ID={message.Id}";
|
||||
if (message.Type == 0x09)
|
||||
{
|
||||
byteBlock.Reset();
|
||||
byteBlock.Write(message.Content);
|
||||
return false;
|
||||
var reader = new ByteBlockReader(message.Content);
|
||||
|
||||
if (this.DataHandlingAdapter == null)
|
||||
{
|
||||
await this.OnTcpReceived(new ReceivedDataEventArgs(reader, default)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this.DataHandlingAdapter.ReceivedInputAsync(reader).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -102,7 +112,7 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
||||
Logger?.Debug($"Old socket connections with the same ID {id} will be closed");
|
||||
try
|
||||
{
|
||||
await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
//await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
await oldClient.CloseAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@@ -120,13 +130,13 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
||||
await ResetIdAsync(id).ConfigureAwait(false);
|
||||
|
||||
//发送成功
|
||||
await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, id, true, 0x81)).ConfigureAwait(false);
|
||||
await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, id, true, 0x81), ClosedToken).ConfigureAwait(false);
|
||||
if (log)
|
||||
Logger?.Info(string.Format(AppResource.DtuConnected, Id));
|
||||
}
|
||||
else if (message.Type == 0x02)
|
||||
{
|
||||
await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, Id, true, 0x82)).ConfigureAwait(false);
|
||||
await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, Id, true, 0x82), ClosedToken).ConfigureAwait(false);
|
||||
Logger?.Info(string.Format(AppResource.DtuDisconnecting, Id));
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
await this.CloseAsync().ConfigureAwait(false);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -49,11 +49,12 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
||||
DataHandlingAdapter.SendCallBackAsync = DefaultSendAsync;
|
||||
}
|
||||
|
||||
protected Task DefaultSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory)
|
||||
|
||||
protected Task DefaultSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory, CancellationToken token)
|
||||
{
|
||||
if (TryGetId(endPoint, out var id))
|
||||
{
|
||||
return DDPAdapter.SendInputAsync(endPoint, new DDPSend(memory, id, false));
|
||||
return DDPAdapter.SendInputAsync(endPoint, new DDPSend(memory, id, false), token);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -61,14 +62,14 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
||||
}
|
||||
}
|
||||
|
||||
protected Task DDPSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory)
|
||||
protected Task DDPSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory, CancellationToken token)
|
||||
{
|
||||
//获取endpoint
|
||||
return base.ProtectedDefaultSendAsync(endPoint, memory);
|
||||
return base.ProtectedDefaultSendAsync(endPoint, memory, token);
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<EndPoint, DDPMessage> DDPMessageDict { get; set; } = new();
|
||||
private Task DDPHandleReceivedData(EndPoint endPoint, ByteBlock byteBlock, IRequestInfo requestInfo)
|
||||
private Task DDPHandleReceivedData(EndPoint endPoint, IByteBlockReader byteBlock, IRequestInfo requestInfo)
|
||||
{
|
||||
if (requestInfo is DDPMessage dDPMessage)
|
||||
{
|
||||
@@ -126,9 +127,18 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
||||
var id = $"ID={message.Id}";
|
||||
if (message.Type == 0x09)
|
||||
{
|
||||
byteBlock.Reset();
|
||||
byteBlock.Write(message.Content);
|
||||
return false;
|
||||
var reader = new ByteBlockReader(message.Content);
|
||||
|
||||
if (this.DataHandlingAdapter == null)
|
||||
{
|
||||
await this.OnUdpReceived(new UdpReceivedDataEventArgs(endPoint, reader, default)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this.DataHandlingAdapter.ReceivedInput(endPoint, reader).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -155,13 +165,13 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
||||
}
|
||||
|
||||
//发送成功
|
||||
await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory<byte>.Empty, id, false, 0x81)).ConfigureAwait(false);
|
||||
await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory<byte>.Empty, id, false, 0x81), ClosedToken).ConfigureAwait(false);
|
||||
if (log)
|
||||
Logger?.Info(string.Format(AppResource.DtuConnected, id));
|
||||
}
|
||||
else if (message.Type == 0x02)
|
||||
{
|
||||
await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory<byte>.Empty, id, false, 0x82)).ConfigureAwait(false);
|
||||
await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory<byte>.Empty, id, false, 0x82), ClosedToken).ConfigureAwait(false);
|
||||
Logger?.Info(string.Format(AppResource.DtuDisconnecting, id));
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
IdDict.TryRemove(endPoint, out _);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -37,10 +37,7 @@ public interface IChannel : ISetupConfigObject, IDisposable, IClosableClient, IC
|
||||
/// </summary>
|
||||
public ConcurrentList<IDevice> Collects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// MaxSign
|
||||
/// </summary>
|
||||
int MaxSign { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通道启动成功后
|
||||
@@ -66,6 +63,11 @@ public interface IChannel : ISetupConfigObject, IDisposable, IClosableClient, IC
|
||||
/// 主动请求时的等待池
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; }
|
||||
|
||||
void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -12,6 +12,8 @@ using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
|
||||
using TouchSocket.SerialPorts;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <summary>
|
||||
@@ -25,15 +27,17 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
public OtherChannel(IChannelOptions channelOptions)
|
||||
{
|
||||
ChannelOptions = channelOptions;
|
||||
|
||||
WaitHandlePool.MaxSign = ushort.MaxValue;
|
||||
}
|
||||
|
||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
|
||||
|
||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||
{
|
||||
var pool = WaitHandlePool;
|
||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||
pool?.CancelAll();
|
||||
pool?.SafeDispose();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
||||
|
||||
@@ -60,7 +64,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
/// <summary>
|
||||
/// 等待池
|
||||
/// </summary>
|
||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; } = new();
|
||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
||||
@@ -109,7 +113,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
m_dataHandlingAdapter = adapter;
|
||||
}
|
||||
|
||||
private async Task PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
|
||||
private async Task PrivateHandleReceivedData(IByteBlockReader byteBlock, IRequestInfo requestInfo)
|
||||
{
|
||||
LastReceivedTime = DateTime.Now;
|
||||
await this.OnChannelReceivedEvent(new ReceivedDataEventArgs(byteBlock, requestInfo), ChannelReceived).ConfigureAwait(false);
|
||||
@@ -119,8 +123,9 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
/// 异步发送数据,保护方法。
|
||||
/// </summary>
|
||||
/// <param name="memory">待发送的字节数据内存。</param>
|
||||
/// <param name="cancellationToken">cancellationToken</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
protected Task ProtectedDefaultSendAsync(ReadOnlyMemory<byte> memory)
|
||||
protected Task ProtectedDefaultSendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
||||
{
|
||||
LastSentTime = DateTime.Now;
|
||||
return Task.CompletedTask;
|
||||
@@ -134,77 +139,58 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
|
||||
public bool IsClient => true;
|
||||
|
||||
public bool Online => true;
|
||||
|
||||
public bool Online => online;
|
||||
public CancellationToken ClosedToken => this.m_transport == null ? new CancellationToken(true) : this.m_transport.Token;
|
||||
private CancellationTokenSource m_transport;
|
||||
public Task<Result> CloseAsync(string msg, CancellationToken token)
|
||||
{
|
||||
var cts = m_transport;
|
||||
m_transport = null;
|
||||
cts?.SafeCancel();
|
||||
cts?.SafeDispose();
|
||||
online = false;
|
||||
|
||||
return Task.FromResult(Result.Success);
|
||||
}
|
||||
|
||||
public volatile bool online;
|
||||
public Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
||||
{
|
||||
var cts = m_transport;
|
||||
m_transport = new();
|
||||
cts?.SafeCancel();
|
||||
cts?.SafeDispose();
|
||||
online = true;
|
||||
if (this.m_dataHandlingAdapter == null)
|
||||
{
|
||||
var adapter = this.Config.GetValue(SerialPortConfigExtension.SerialDataHandlingAdapterProperty)?.Invoke();
|
||||
if (adapter != null)
|
||||
{
|
||||
this.SetAdapter(adapter);
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task SendAsync(IList<ArraySegment<byte>> transferBytes)
|
||||
{
|
||||
// 检查数据处理适配器是否存在且支持拼接发送
|
||||
if (m_dataHandlingAdapter?.CanSplicingSend != true)
|
||||
{
|
||||
// 如果不支持拼接发送,则计算所有字节片段的总长度
|
||||
var length = 0;
|
||||
foreach (var item in transferBytes)
|
||||
{
|
||||
length += item.Count;
|
||||
}
|
||||
// 使用计算出的总长度创建一个连续的内存块
|
||||
using (var byteBlock = new ByteBlock(length))
|
||||
{
|
||||
// 将每个字节片段写入连续的内存块
|
||||
foreach (var item in transferBytes)
|
||||
{
|
||||
byteBlock.Write(new ReadOnlySpan<byte>(item.Array, item.Offset, item.Count));
|
||||
}
|
||||
// 根据数据处理适配器的存在与否,选择不同的发送方式
|
||||
if (m_dataHandlingAdapter == null)
|
||||
{
|
||||
// 如果没有数据处理适配器,则使用默认方式发送
|
||||
await ProtectedDefaultSendAsync(byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果有数据处理适配器,则通过适配器发送
|
||||
await m_dataHandlingAdapter.SendInputAsync(byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果数据处理适配器支持拼接发送,则直接发送字节列表
|
||||
await m_dataHandlingAdapter.SendInputAsync(transferBytes).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
|
||||
public Task SendAsync(ReadOnlyMemory<byte> memory)
|
||||
public Task SendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
||||
{
|
||||
if (m_dataHandlingAdapter == null)
|
||||
{
|
||||
return ProtectedDefaultSendAsync(memory);
|
||||
return ProtectedDefaultSendAsync(memory, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 否则,使用适配器的发送方法进行数据发送。
|
||||
return m_dataHandlingAdapter.SendInputAsync(memory);
|
||||
return m_dataHandlingAdapter.SendInputAsync(memory, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public Task SendAsync(IRequestInfo requestInfo)
|
||||
public Task SendAsync(IRequestInfo requestInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
// 检查是否具备发送请求的条件,如果不具备则抛出异常
|
||||
ThrowIfCannotSendRequestInfo();
|
||||
|
||||
// 使用数据处理适配器异步发送输入请求
|
||||
return m_dataHandlingAdapter.SendInputAsync(requestInfo);
|
||||
return m_dataHandlingAdapter.SendInputAsync(requestInfo, cancellationToken);
|
||||
}
|
||||
private void ThrowIfCannotSendRequestInfo()
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -31,13 +31,13 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
{
|
||||
_heartbeat = value;
|
||||
if (!heartbeatHex)
|
||||
HeartbeatByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
else
|
||||
HeartbeatByte = new ArraySegment<byte>(_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
}
|
||||
}
|
||||
private string _heartbeat;
|
||||
private ArraySegment<byte> HeartbeatByte = new();
|
||||
private Memory<byte> HeartbeatByte = new();
|
||||
|
||||
|
||||
private bool heartbeatHex;
|
||||
@@ -51,9 +51,9 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
{
|
||||
heartbeatHex = value;
|
||||
if (!heartbeatHex)
|
||||
HeartbeatByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
else
|
||||
HeartbeatByte = new ArraySegment<byte>(_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
}
|
||||
}
|
||||
public bool DtuIdHex { get; set; }
|
||||
@@ -61,7 +61,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
/// <inheritdoc/>
|
||||
public async Task OnTcpReceiving(ITcpSession client, ByteBlockEventArgs e)
|
||||
{
|
||||
var len = HeartbeatByte.Count;
|
||||
var len = HeartbeatByte.Length;
|
||||
if (client is TcpSessionClientChannel socket && socket.Service is ITcpServiceChannel tcpServiceChannel)
|
||||
{
|
||||
if (!socket.Id.StartsWith("ID="))
|
||||
@@ -71,7 +71,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
{
|
||||
try
|
||||
{
|
||||
await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
//await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
await oldClient.CloseAsync().ConfigureAwait(false);
|
||||
oldClient.Dispose();
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
{
|
||||
try
|
||||
{
|
||||
await socket.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
//await socket.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
await socket.CloseAsync().ConfigureAwait(false);
|
||||
socket.Dispose();
|
||||
}
|
||||
@@ -102,14 +102,14 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
if (HeartbeatByte.SequenceEqual(e.ByteBlock.AsSegment(0, len)))
|
||||
if (HeartbeatByte.Span.SequenceEqual(e.ByteBlock.Memory.Slice(0, len).Span))
|
||||
{
|
||||
if (DateTimeOffset.Now - socket.LastSentTime < TimeSpan.FromMilliseconds(200))
|
||||
{
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
//回应心跳包
|
||||
await socket.SendAsync(HeartbeatByte).ConfigureAwait(false);
|
||||
await socket.SendAsync(HeartbeatByte, socket.ClosedToken).ConfigureAwait(false);
|
||||
e.Handled = true;
|
||||
if (socket.Logger?.LogLevel <= LogLevel.Trace)
|
||||
socket.Logger?.Trace($"{socket}- Heartbeat");
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -15,7 +15,7 @@ using ThingsGateway.Foundation.Extension.String;
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
[PluginOption(Singleton = true)]
|
||||
internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugin, ITcpReceivingPlugin, ITcpClosingPlugin
|
||||
internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugin, ITcpReceivingPlugin, ITcpClosedPlugin
|
||||
{
|
||||
public string DtuId
|
||||
{
|
||||
@@ -27,13 +27,13 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
{
|
||||
_dtuId = value;
|
||||
if (!dtuIdHex)
|
||||
DtuIdByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(_dtuId ?? string.Empty));
|
||||
DtuIdByte = (Encoding.UTF8.GetBytes(_dtuId ?? string.Empty));
|
||||
else
|
||||
DtuIdByte = new ArraySegment<byte>(_dtuId?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
DtuIdByte = (_dtuId?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
}
|
||||
}
|
||||
private string _dtuId;
|
||||
private ArraySegment<byte> DtuIdByte;
|
||||
private Memory<byte> DtuIdByte;
|
||||
|
||||
/// <summary>
|
||||
/// 心跳字符串
|
||||
@@ -48,13 +48,13 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
{
|
||||
_heartbeat = value;
|
||||
if (!heartbeatHex)
|
||||
HeartbeatByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
else
|
||||
HeartbeatByte = new ArraySegment<byte>(_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
}
|
||||
}
|
||||
private string _heartbeat;
|
||||
private ArraySegment<byte> HeartbeatByte;
|
||||
private Memory<byte> HeartbeatByte;
|
||||
|
||||
|
||||
|
||||
@@ -73,12 +73,12 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
heartbeatHex = value;
|
||||
if (!heartbeatHex)
|
||||
{
|
||||
HeartbeatByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
HeartbeatByte = new ArraySegment<byte>(_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -95,12 +95,12 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
dtuIdHex = value;
|
||||
if (!dtuIdHex)
|
||||
{
|
||||
DtuIdByte = new ArraySegment<byte>(Encoding.UTF8.GetBytes(_dtuId ?? string.Empty));
|
||||
DtuIdByte = (Encoding.UTF8.GetBytes(_dtuId ?? string.Empty));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
DtuIdByte = new ArraySegment<byte>(_dtuId?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
DtuIdByte = (_dtuId?.HexStringToBytes() ?? Array.Empty<byte>());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
|
||||
if (client is ITcpClient tcpClient)
|
||||
{
|
||||
await tcpClient.SendAsync(DtuIdByte).ConfigureAwait(false);
|
||||
await tcpClient.SendAsync(DtuIdByte, tcpClient.ClosedToken).ConfigureAwait(false);
|
||||
|
||||
if (Task == null)
|
||||
{
|
||||
@@ -145,7 +145,7 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await tcpClient.SendAsync(HeartbeatByte).ConfigureAwait(false);
|
||||
await tcpClient.SendAsync(HeartbeatByte, tcpClient.ClosedToken).ConfigureAwait(false);
|
||||
tcpClient.Logger?.Trace($"{tcpClient}- Heartbeat");
|
||||
failedCount = 0;
|
||||
}
|
||||
@@ -178,10 +178,10 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
|
||||
if (client is ITcpClient tcpClient)
|
||||
{
|
||||
var len = HeartbeatByte.Count;
|
||||
var len = HeartbeatByte.Length;
|
||||
if (len > 0)
|
||||
{
|
||||
if (HeartbeatByte.SequenceEqual(e.ByteBlock.AsSegment(0, len)))
|
||||
if (HeartbeatByte.Span.SequenceEqual(e.ByteBlock.Memory.Slice(0, len).Span))
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
}
|
||||
}
|
||||
|
||||
public Task OnTcpClosing(ITcpSession client, ClosingEventArgs e)
|
||||
public Task OnTcpClosed(ITcpSession client, ClosedEventArgs e)
|
||||
{
|
||||
SendHeartbeat = false;
|
||||
return EasyTask.CompletedTask;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -79,7 +79,7 @@ public static class PluginUtil
|
||||
.SetTick(TimeSpan.FromMilliseconds(channelOptions.CheckClearTime))
|
||||
.SetOnClose(async (c, t) =>
|
||||
{
|
||||
await c.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
//await c.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
await c.CloseAsync($"{channelOptions.CheckClearTime}ms Timeout").ConfigureAwait(false);
|
||||
});
|
||||
};
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -24,15 +24,17 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
public SerialPortChannel(IChannelOptions channelOptions)
|
||||
{
|
||||
ChannelOptions = channelOptions;
|
||||
|
||||
WaitHandlePool.MaxSign = ushort.MaxValue;
|
||||
}
|
||||
|
||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
|
||||
|
||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||
{
|
||||
var pool = WaitHandlePool;
|
||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||
pool?.CancelAll();
|
||||
pool?.SafeDispose();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
||||
|
||||
@@ -62,7 +64,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
/// <summary>
|
||||
/// 等待池
|
||||
/// </summary>
|
||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; } = new();
|
||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
||||
@@ -82,6 +84,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
//await _connectLock.WaitAsync().ConfigureAwait(false);
|
||||
if (Online)
|
||||
{
|
||||
PortName = null;
|
||||
var result = await base.CloseAsync(msg, token).ConfigureAwait(false);
|
||||
if (!Online)
|
||||
{
|
||||
@@ -99,7 +102,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public new async Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
||||
public override async Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
||||
{
|
||||
if (!Online)
|
||||
{
|
||||
@@ -109,6 +112,12 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
if (!Online)
|
||||
{
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
|
||||
var port = Config?.GetValue(SerialPortConfigExtension.SerialPortOptionProperty);
|
||||
if (port != null)
|
||||
PortName = $"{port.PortName}";
|
||||
|
||||
await base.ConnectAsync(millisecondsTimeout, token).ConfigureAwait(false);
|
||||
if (Online)
|
||||
{
|
||||
@@ -130,20 +139,16 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||
SetAdapter(singleStreamDataHandlingAdapter);
|
||||
}
|
||||
|
||||
private string PortName { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public override string? ToString()
|
||||
{
|
||||
if (ProtectedMainSerialPort != null)
|
||||
{
|
||||
return $"{ProtectedMainSerialPort.PortName}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var port = Config?.GetValue(SerialPortConfigExtension.SerialPortOptionProperty);
|
||||
if (port != null)
|
||||
return $"{port.PortName}";
|
||||
}
|
||||
if (!PortName.IsNullOrEmpty())
|
||||
return PortName;
|
||||
|
||||
var port = Config?.GetValue(SerialPortConfigExtension.SerialPortOptionProperty);
|
||||
if (port != null)
|
||||
return $"{port.PortName}";
|
||||
return base.ToString();
|
||||
}
|
||||
|
||||
@@ -192,9 +197,9 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void SafetyDispose(bool disposing)
|
||||
{
|
||||
WaitHandlePool.SafeDispose();
|
||||
base.Dispose(disposing);
|
||||
base.SafetyDispose(disposing);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -24,10 +24,15 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
||||
{
|
||||
ChannelOptions = channelOptions;
|
||||
|
||||
WaitHandlePool.MaxSign = ushort.MaxValue;
|
||||
}
|
||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
|
||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||
{
|
||||
var pool = WaitHandlePool;
|
||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||
pool?.CancelAll();
|
||||
pool?.SafeDispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||
@@ -57,7 +62,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
||||
/// <summary>
|
||||
/// 等待池
|
||||
/// </summary>
|
||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; } = new();
|
||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new();
|
||||
public virtual WaitLock GetLock(string key) => WaitLock;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -181,9 +186,9 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void SafetyDispose(bool disposing)
|
||||
{
|
||||
WaitHandlePool.SafeDispose();
|
||||
base.Dispose(disposing);
|
||||
base.SafetyDispose(disposing);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -23,10 +23,10 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentList<IDevice> Collects { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 停止时是否发送ShutDown
|
||||
/// </summary>
|
||||
public bool ShutDownEnable { get; set; } = true;
|
||||
///// <summary>
|
||||
///// 停止时是否发送ShutDown
|
||||
///// </summary>
|
||||
//public bool ShutDownEnable { get; set; } = true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task ClearAsync()
|
||||
@@ -35,8 +35,8 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ShutDownEnable)
|
||||
await client.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
//if (ShutDownEnable)
|
||||
// await client.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
|
||||
await client.CloseAsync().ConfigureAwait(false);
|
||||
client.SafeDispose();
|
||||
@@ -51,8 +51,8 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
||||
{
|
||||
if (this.TryGetClient(id, out var client))
|
||||
{
|
||||
if (ShutDownEnable)
|
||||
await client.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
//if (ShutDownEnable)
|
||||
// await client.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
||||
await client.CloseAsync().ConfigureAwait(false);
|
||||
client.SafeDispose();
|
||||
}
|
||||
@@ -85,6 +85,10 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
||||
}
|
||||
finally
|
||||
{
|
||||
var cts = m_transport;
|
||||
m_transport = new();
|
||||
cts?.SafeCancel();
|
||||
cts?.SafeDispose();
|
||||
_connectLock.Release();
|
||||
}
|
||||
}
|
||||
@@ -110,6 +114,10 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
||||
}
|
||||
finally
|
||||
{
|
||||
var cts = m_transport;
|
||||
m_transport = null;
|
||||
cts?.SafeCancel();
|
||||
cts?.SafeDispose();
|
||||
_connectLock.Release();
|
||||
}
|
||||
}
|
||||
@@ -121,6 +129,14 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public CancellationToken ClosedToken => this.m_transport == null ? new CancellationToken(true) : this.m_transport.Token;
|
||||
private CancellationTokenSource m_transport;
|
||||
|
||||
protected override void SafetyDispose(bool disposing)
|
||||
{
|
||||
m_transport?.SafeDispose();
|
||||
base.SafetyDispose(disposing);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override Task OnTcpClosed(TClient socketClient, ClosedEventArgs e)
|
||||
{
|
||||
@@ -208,10 +224,16 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
||||
protected override TClient NewClient()
|
||||
{
|
||||
var data = new TClient();
|
||||
data.WaitHandlePool.MaxSign = MaxSign;
|
||||
data.ResetSign(MinSign, MaxSign);
|
||||
return data;
|
||||
}
|
||||
public int MaxSign { get; set; }
|
||||
public int MinSign { get; set; }
|
||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||
{
|
||||
MinSign = minSign;
|
||||
MaxSign = maxSign;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpClosing(TClient socketClient, ClosingEventArgs e)
|
||||
{
|
||||
@@ -264,6 +286,7 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
||||
|
||||
IEnumerable<TcpSessionClientChannel> ITcpServiceChannel.Clients => base.Clients;
|
||||
|
||||
|
||||
protected override void ClientInitialized(TClient client)
|
||||
{
|
||||
client.ChannelOptions = ChannelOptions;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -22,11 +22,15 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
||||
/// <inheritdoc/>
|
||||
public TcpSessionClientChannel()
|
||||
{
|
||||
WaitHandlePool.MaxSign = ushort.MaxValue;
|
||||
}
|
||||
|
||||
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
|
||||
|
||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||
{
|
||||
var pool = WaitHandlePool;
|
||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||
pool?.CancelAll();
|
||||
pool?.SafeDispose();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
||||
|
||||
@@ -92,10 +96,10 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void SafetyDispose(bool disposing)
|
||||
{
|
||||
WaitHandlePool.SafeDispose();
|
||||
base.Dispose(disposing);
|
||||
base.SafetyDispose(disposing);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -25,11 +25,16 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
public UdpSessionChannel(IChannelOptions channelOptions)
|
||||
{
|
||||
ChannelOptions = channelOptions;
|
||||
WaitHandlePool.MaxSign = ushort.MaxValue;
|
||||
}
|
||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||
|
||||
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
|
||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||
{
|
||||
var pool = WaitHandlePool;
|
||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||
pool?.CancelAll();
|
||||
pool?.SafeDispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
||||
@@ -92,7 +97,8 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
|
||||
SetAdapter(udpDataHandlingAdapter);
|
||||
}
|
||||
|
||||
public CancellationToken ClosedToken => this.m_transport == null ? new CancellationToken(true) : this.m_transport.Token;
|
||||
private CancellationTokenSource m_transport;
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync()
|
||||
{
|
||||
@@ -120,6 +126,10 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
}
|
||||
finally
|
||||
{
|
||||
var cts = m_transport;
|
||||
m_transport = new();
|
||||
cts?.SafeCancel();
|
||||
cts?.SafeDispose();
|
||||
_connectLock.Release();
|
||||
}
|
||||
}
|
||||
@@ -152,6 +162,10 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
}
|
||||
finally
|
||||
{
|
||||
var cts = m_transport;
|
||||
m_transport = null;
|
||||
cts?.SafeCancel();
|
||||
cts?.SafeDispose();
|
||||
_connectLock.Release();
|
||||
}
|
||||
}
|
||||
@@ -188,9 +202,10 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void SafetyDispose(bool disposing)
|
||||
{
|
||||
m_transport?.SafeDispose();
|
||||
WaitHandlePool.SafeDispose();
|
||||
base.Dispose(disposing);
|
||||
base.SafetyDispose(disposing);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -27,9 +27,6 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSendRequestInfo => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSplicingSend => false;
|
||||
|
||||
/// <summary>
|
||||
/// 报文输出时采用字符串还是HexString
|
||||
/// </summary>
|
||||
@@ -171,23 +168,24 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task PreviewSendAsync(ReadOnlyMemory<byte> memory)
|
||||
protected override async Task PreviewSendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString() : (memory.Span.ToString(Encoding.UTF8)))}");
|
||||
|
||||
//发送
|
||||
await GoSendAsync(memory).ConfigureAwait(false);
|
||||
await GoSendAsync(memory, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task PreviewSendAsync(IRequestInfo requestInfo)
|
||||
protected override async Task PreviewSendAsync(IRequestInfo requestInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!(requestInfo is ISendMessage sendMessage))
|
||||
{
|
||||
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var byteBlock = new ValueByteBlock(sendMessage.MaxLength);
|
||||
try
|
||||
{
|
||||
@@ -199,7 +197,7 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
||||
{
|
||||
SetRequest(sendMessage, ref byteBlock);
|
||||
}
|
||||
await GoSendAsync(byteBlock.Memory).ConfigureAwait(false);
|
||||
await GoSendAsync(byteBlock.Memory, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -21,9 +21,6 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSendRequestInfo => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSplicingSend => false;
|
||||
|
||||
/// <summary>
|
||||
/// 报文输出时采用字符串还是HexString
|
||||
/// </summary>
|
||||
@@ -61,7 +58,7 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task PreviewReceived(EndPoint remoteEndPoint, ByteBlock byteBlock)
|
||||
protected override async Task PreviewReceived(EndPoint remoteEndPoint, IByteBlockReader byteBlock)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -149,23 +146,24 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task PreviewSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory)
|
||||
protected override async Task PreviewSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString() : (memory.Span.ToString(Encoding.UTF8)))}");
|
||||
|
||||
//发送
|
||||
await GoSendAsync(endPoint, memory).ConfigureAwait(false);
|
||||
await GoSendAsync(endPoint, memory, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task PreviewSendAsync(EndPoint endPoint, IRequestInfo requestInfo)
|
||||
protected override async Task PreviewSendAsync(EndPoint endPoint, IRequestInfo requestInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!(requestInfo is ISendMessage sendMessage))
|
||||
{
|
||||
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var byteBlock = new ValueByteBlock(sendMessage.MaxLength);
|
||||
try
|
||||
{
|
||||
@@ -177,7 +175,7 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
|
||||
{
|
||||
SetRequest(sendMessage, ref byteBlock);
|
||||
}
|
||||
await GoSendAsync(endPoint, byteBlock.Memory).ConfigureAwait(false);
|
||||
await GoSendAsync(endPoint, byteBlock.Memory, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 定义了字节块构建器的接口,用于从内存池中构建和管理字节块。
|
||||
/// </summary>
|
||||
public interface IByteBlockWriterBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// 构建数据时,指示内存池的申请长度。
|
||||
/// <para>
|
||||
/// 建议:该值可以尽可能的设置大一些,这样可以避免内存池扩容。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
int MaxLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 构建对象到<see cref="ByteBlock"/>
|
||||
/// </summary>
|
||||
/// <param name="writer">要构建的字节块对象引用。</param>
|
||||
void Build<TWriter>(ref TWriter writer) where TWriter : IByteBlockWriter
|
||||
#if AllowsRefStruct
|
||||
,allows ref struct
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 指示<see cref="IRequestInfo"/>应当如何构建
|
||||
/// </summary>
|
||||
public interface IRequestInfoByteBlockWriterBuilder : IRequestInfo, IByteBlockWriterBuilder
|
||||
{
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -23,7 +23,7 @@ public interface IResultMessage : IOperResult, IRequestInfo
|
||||
/// <summary>
|
||||
/// 解析的字节信息
|
||||
/// </summary>
|
||||
byte[] Content { get; set; }
|
||||
ReadOnlyMemory<byte> Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息头的指令长度,不固定时返回0
|
||||
@@ -42,14 +42,14 @@ public interface IResultMessage : IOperResult, IRequestInfo
|
||||
/// <para>然后返回<see cref="FilterResult.GoOn"/></para>
|
||||
/// </summary>
|
||||
/// <returns>是否成功有效</returns>
|
||||
FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock;
|
||||
FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
||||
|
||||
/// <summary>
|
||||
/// 检查头子节的合法性,并赋值<see cref="BodyLength"/><br />
|
||||
/// <para>如果返回false,意味着放弃本次解析的所有数据,包括已经解析完成的Header</para>
|
||||
/// </summary>
|
||||
/// <returns>是否成功的结果</returns>
|
||||
bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock;
|
||||
bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
||||
|
||||
/// <summary>
|
||||
/// 发送前的信息处理,例如存储某些特征信息:站号/功能码等等用于验证后续的返回信息是否合法
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -13,6 +13,6 @@ namespace ThingsGateway.Foundation;
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
public interface ISendMessage : IRequestInfo, IWaitHandle, IRequestInfoBuilder
|
||||
public interface ISendMessage : IRequestInfo, IWaitHandle, IRequestInfoByteBlockWriterBuilder
|
||||
{
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <inheritdoc cref="IResultMessage"/>
|
||||
public class MessageBase : OperResultClass<byte[]>, IResultMessage, IWaitHandle
|
||||
public class MessageBase : OperResultClass<ReadOnlyMemory<byte>>, IResultMessage, IWaitHandle
|
||||
{
|
||||
#region 构造
|
||||
|
||||
@@ -42,13 +42,13 @@ public class MessageBase : OperResultClass<byte[]>, IResultMessage, IWaitHandle
|
||||
public virtual int Sign { get; set; } = -1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
|
||||
public virtual FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return FilterResult.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
|
||||
public virtual bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
@@ -13,14 +13,14 @@
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <summary>
|
||||
/// 用户自定义数据处理适配器,使用该适配器时,接收方收到的数据中,<see cref="ByteBlock"/>将为null,
|
||||
/// 用户自定义数据处理适配器,使用该适配器时,接收方收到的数据中,<see cref="ByteBlock"/>将为<see langword="null"/>,
|
||||
/// 同时<see cref="IRequestInfo"/>将实现为TRequest,发送数据直接发送。
|
||||
/// </summary>
|
||||
public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataHandlingAdapter where TRequest : IRequestInfo
|
||||
{
|
||||
private ValueByteBlock m_tempByteBlock;
|
||||
|
||||
private readonly Type m_requestType;
|
||||
private ValueByteBlock m_tempByteBlock;
|
||||
private TRequest m_tempRequest;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化自定义数据处理适配器。
|
||||
@@ -33,16 +33,9 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
this.m_requestType = typeof(TRequest);
|
||||
}
|
||||
|
||||
private TRequest m_tempRequest;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSendRequestInfo => false;
|
||||
|
||||
/// <summary>
|
||||
/// 默认不支持拼接发送
|
||||
/// </summary>
|
||||
public override bool CanSplicingSend => false;
|
||||
|
||||
/// <summary>
|
||||
/// 指示需要解析当前包的剩余长度。如果不能得知,请赋值<see cref="int.MaxValue"/>。
|
||||
/// </summary>
|
||||
@@ -52,10 +45,10 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
/// 尝试解析请求数据块。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块类型,必须实现IByteBlock接口。</typeparam>
|
||||
/// <param name="byteBlock">待解析的字节块。</param>
|
||||
/// <param name="reader">待解析的字节块。</param>
|
||||
/// <param name="request">解析出的请求对象。</param>
|
||||
/// <returns>解析是否成功。</returns>
|
||||
public bool TryParseRequest<TByteBlock>(ref TByteBlock byteBlock, out TRequest request) where TByteBlock : IByteBlock
|
||||
public bool TryParseRequest<TByteBlock>(ref TByteBlock reader, out TRequest request) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
// 检查缓存是否超时,如果超时则清除缓存。
|
||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
@@ -66,7 +59,7 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
// 如果临时字节块为空,则尝试直接解析。
|
||||
if (this.m_tempByteBlock.IsEmpty)
|
||||
{
|
||||
return this.Single(ref byteBlock, out request) == FilterResult.Success;
|
||||
return this.Single(ref reader, out request) == FilterResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -77,16 +70,16 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
}
|
||||
|
||||
// 计算本次可以读取的长度。
|
||||
var len = Math.Min(this.SurLength, byteBlock.CanReadLength);
|
||||
var len = Math.Min(this.SurLength, reader.CanReadLength);
|
||||
|
||||
// 从输入字节块中读取数据到临时字节块中。
|
||||
var block = this.m_tempByteBlock;
|
||||
block.Write(byteBlock.Span.Slice(byteBlock.Position, len));
|
||||
byteBlock.Position += len;
|
||||
block.Write(reader.Span.Slice(reader.Position, len));
|
||||
reader.Position += len;
|
||||
this.SurLength -= len;
|
||||
|
||||
// 重置临时字节块并准备下一次使用。
|
||||
this.m_tempByteBlock = ValueByteBlock.Empty;
|
||||
this.m_tempByteBlock = default;
|
||||
|
||||
// 回到字节块的起始位置。
|
||||
block.SeekToStart();
|
||||
@@ -101,7 +94,7 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
// 如果临时字节块不为空,则继续缓存。
|
||||
if (!this.m_tempByteBlock.IsEmpty)
|
||||
{
|
||||
byteBlock.Position += this.m_tempByteBlock.Length;
|
||||
reader.Position += this.m_tempByteBlock.Length;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -110,7 +103,7 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
// 如果字节块中还有剩余数据,则回退指针。
|
||||
if (block.CanReadLength > 0)
|
||||
{
|
||||
byteBlock.Position -= block.CanReadLength;
|
||||
reader.Position -= block.CanReadLength;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -119,7 +112,7 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
// 对于需要继续解析的情况,也回退指针。
|
||||
if (block.CanReadLength > 0)
|
||||
{
|
||||
byteBlock.Position -= block.CanReadLength;
|
||||
reader.Position -= block.CanReadLength;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -138,13 +131,23 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
/// <para>当数据部分异常时,请移动<see cref="ByteBlock.Position"/>到指定位置,然后返回<see cref="FilterResult.GoOn"/></para>
|
||||
/// <para>当完全满足解析条件时,请返回<see cref="FilterResult.Success"/>最后将<see cref="ByteBlock.Position"/>移至指定位置。</para>
|
||||
/// </summary>
|
||||
/// <param name="byteBlock">字节块</param>
|
||||
/// <param name="reader">字节块</param>
|
||||
/// <param name="beCached">是否为上次遗留对象,当该参数为<see langword="true"/>时,request也将是上次实例化的对象。</param>
|
||||
/// <param name="request">对象。</param>
|
||||
/// <param name="tempCapacity">缓存容量。当需要首次缓存时,指示申请的ByteBlock的容量。合理的值可避免ByteBlock扩容带来的性能消耗。</param>
|
||||
/// <returns></returns>
|
||||
protected abstract FilterResult Filter<TByteBlock>(ref TByteBlock byteBlock, bool beCached, ref TRequest request, ref int tempCapacity)
|
||||
where TByteBlock : IByteBlock;
|
||||
protected abstract FilterResult Filter<TByteBlock>(ref TByteBlock reader, bool beCached, ref TRequest request, ref int tempCapacity)
|
||||
where TByteBlock : IByteBlockReader;
|
||||
|
||||
/// <summary>
|
||||
/// 判断请求对象是否应该被缓存。
|
||||
/// </summary>
|
||||
/// <param name="request">请求对象。</param>
|
||||
/// <returns>返回布尔值,指示请求对象是否应该被缓存。</returns>
|
||||
protected virtual bool IsBeCached(in TRequest request)
|
||||
{
|
||||
return this.m_requestType.IsValueType ? request.GetHashCode() != default(TRequest).GetHashCode() : request != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 成功执行接收以后。
|
||||
@@ -155,7 +158,7 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 即将执行<see cref="SingleStreamDataHandlingAdapter.GoReceivedAsync(ByteBlock, IRequestInfo)"/>。
|
||||
/// 即将执行<see cref="SingleStreamDataHandlingAdapter.GoReceivedAsync(IByteBlockReader, IRequestInfo)"/>。
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns>返回值标识是否继续执行</returns>
|
||||
@@ -166,7 +169,7 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <param name="byteBlock"></param>
|
||||
protected override async Task PreviewReceivedAsync(ByteBlock byteBlock)
|
||||
protected override async Task PreviewReceivedAsync(IByteBlockReader byteBlock)
|
||||
{
|
||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
@@ -174,14 +177,16 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
}
|
||||
if (this.m_tempByteBlock.IsEmpty)
|
||||
{
|
||||
await this.SingleAsync(byteBlock, false).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.SingleAsync(byteBlock).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_tempByteBlock.Write(byteBlock.Span);
|
||||
var block = this.m_tempByteBlock;
|
||||
this.m_tempByteBlock = ValueByteBlock.Empty;
|
||||
await this.SingleAsync(block, true).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
using (var block = this.m_tempByteBlock)
|
||||
{
|
||||
this.m_tempByteBlock = default;
|
||||
await this.SingleAsync(block).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,37 +194,25 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
protected override void Reset()
|
||||
{
|
||||
this.m_tempByteBlock.SafeDispose();
|
||||
this.m_tempByteBlock = ValueByteBlock.Empty;
|
||||
this.m_tempByteBlock = default;
|
||||
this.m_tempRequest = default;
|
||||
this.SurLength = 0;
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断请求对象是否应该被缓存。
|
||||
/// </summary>
|
||||
/// <param name="request">请求对象。</param>
|
||||
/// <returns>返回布尔值,指示请求对象是否应该被缓存。</returns>
|
||||
protected virtual bool IsBeCached(in TRequest request)
|
||||
{
|
||||
// 如果请求对象类型是值类型,则判断其哈希码是否与默认值不同;
|
||||
// 如果是引用类型,则判断对象本身是否为null。
|
||||
return this.m_requestType.IsValueType ? request.GetHashCode() != default(TRequest).GetHashCode() : request != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理单个字节块,提取请求对象。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">字节块类型,需要实现IByteBlock接口。</typeparam>
|
||||
/// <param name="byteBlock">字节块,将被解析以提取请求对象。</param>
|
||||
/// <param name="reader">字节块,将被解析以提取请求对象。</param>
|
||||
/// <param name="request">输出参数,提取出的请求对象。</param>
|
||||
/// <returns>返回过滤结果,指示处理的状态。</returns>
|
||||
protected FilterResult Single<TByteBlock>(ref TByteBlock byteBlock, out TRequest request) where TByteBlock : IByteBlock
|
||||
protected FilterResult Single<TByteBlock>(ref TByteBlock reader, out TRequest request) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
// 初始化临时缓存容量。
|
||||
var tempCapacity = 1024 * 64;
|
||||
// 执行过滤操作,根据是否应该缓存来决定如何处理字节块和请求对象。
|
||||
var filterResult = this.Filter(ref byteBlock, this.IsBeCached(this.m_tempRequest), ref this.m_tempRequest, ref tempCapacity);
|
||||
var filterResult = this.Filter(ref reader, this.IsBeCached(this.m_tempRequest), ref this.m_tempRequest, ref tempCapacity);
|
||||
switch (filterResult)
|
||||
{
|
||||
case FilterResult.Success:
|
||||
@@ -230,10 +223,10 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
|
||||
case FilterResult.Cache:
|
||||
// 如果过滤结果需要缓存,则创建一个新的字节块来缓存数据。
|
||||
if (byteBlock.CanReadLength > 0)
|
||||
if (reader.CanReadLength > 0)
|
||||
{
|
||||
this.m_tempByteBlock = new ValueByteBlock(tempCapacity);
|
||||
this.m_tempByteBlock.Write(byteBlock.Span.Slice(byteBlock.Position, byteBlock.CanReadLength));
|
||||
this.m_tempByteBlock.Write(reader.Span.Slice(reader.Position, reader.CanReadLength));
|
||||
|
||||
// 如果缓存的数据长度超过设定的最大包大小,则抛出异常。
|
||||
if (this.m_tempByteBlock.Length > this.MaxPackageSize)
|
||||
@@ -242,7 +235,7 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
}
|
||||
|
||||
// 将字节块指针移到末尾。
|
||||
byteBlock.SeekToEnd();
|
||||
reader.Advance((int)reader.BytesRemaining);
|
||||
}
|
||||
// 更新缓存时间。
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
@@ -264,17 +257,17 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SingleAsync<TByteBlock>(TByteBlock byteBlock, bool temp) where TByteBlock : IByteBlock
|
||||
private async Task SingleAsync<TByteBlock>(TByteBlock reader) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
byteBlock.Position = 0;
|
||||
while (byteBlock.Position < byteBlock.Length)
|
||||
reader.Position = 0;
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
if (this.DisposedValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var tempCapacity = 1024 * 64;
|
||||
var filterResult = this.Filter(ref byteBlock, this.IsBeCached(this.m_tempRequest), ref this.m_tempRequest, ref tempCapacity);
|
||||
var filterResult = this.Filter(ref reader, this.IsBeCached(this.m_tempRequest), ref this.m_tempRequest, ref tempCapacity);
|
||||
|
||||
switch (filterResult)
|
||||
{
|
||||
@@ -288,26 +281,12 @@ public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataH
|
||||
break;
|
||||
|
||||
case FilterResult.Cache:
|
||||
//if (byteBlock.CanReadLength > 0)
|
||||
{
|
||||
if (temp)
|
||||
{
|
||||
this.m_tempByteBlock = new ValueByteBlock(tempCapacity);
|
||||
this.m_tempByteBlock.Write(byteBlock.Span);
|
||||
//this.m_tempByteBlock.Write(byteBlock.Span.Slice(byteBlock.Position, byteBlock.CanReadLength));
|
||||
byteBlock.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_tempByteBlock = new ValueByteBlock(tempCapacity);
|
||||
this.m_tempByteBlock.Write(byteBlock.Span);
|
||||
//this.m_tempByteBlock.Write(byteBlock.Span.Slice(byteBlock.Position, byteBlock.CanReadLength));
|
||||
}
|
||||
this.m_tempByteBlock = new ValueByteBlock(tempCapacity);
|
||||
this.m_tempByteBlock.Write(reader.Span);
|
||||
|
||||
if (this.m_tempByteBlock.Length > this.MaxPackageSize)
|
||||
{
|
||||
this.OnError(default, $"The parsed signal was not received when the cached data length {m_tempByteBlock.Length} exceeds the set value {MaxPackageSize}", true, true);
|
||||
}
|
||||
if (this.m_tempByteBlock.Length > this.MaxPackageSize)
|
||||
{
|
||||
this.OnError(default, $"The parsed signal was not received when the cached data length {m_tempByteBlock.Length} exceeds the set value {MaxPackageSize}", true, true);
|
||||
}
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -17,6 +17,8 @@ using ThingsGateway.Foundation.Extension.String;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
using TouchSocket.SerialPorts;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <summary>
|
||||
@@ -70,13 +72,17 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.Config.SetSerialDataHandlingAdapter(() =>
|
||||
{
|
||||
var adapter = GetDataAdapter() as SingleStreamDataHandlingAdapter;
|
||||
return adapter;
|
||||
});
|
||||
channel.Config.SetTcpDataHandlingAdapter(() =>
|
||||
{
|
||||
var adapter = GetDataAdapter() as SingleStreamDataHandlingAdapter;
|
||||
return adapter;
|
||||
});
|
||||
}
|
||||
clientChannel.SetDataHandlingAdapter(GetDataAdapter());
|
||||
}
|
||||
else if (Channel is ITcpServiceChannel serviceChannel)
|
||||
{
|
||||
@@ -345,11 +351,11 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
|
||||
if (channel is IDtuUdpSessionChannel udpSession)
|
||||
{
|
||||
await udpSession.SendAsync(endPoint, sendMessage).ConfigureAwait(false);
|
||||
await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendAsync(sendMessage).ConfigureAwait(false);
|
||||
await channel.SendAsync(sendMessage, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return OperResult.Success;
|
||||
@@ -487,28 +493,22 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult<ReadOnlyMemory<byte>>> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var channelResult = GetChannel(this is IDtu dtu ? dtu.DtuId : null);
|
||||
if (!channelResult.IsSuccess) return EasyValueTask.FromResult(new OperResult<byte[]>(channelResult));
|
||||
if (!channelResult.IsSuccess) return EasyValueTask.FromResult(new OperResult<ReadOnlyMemory<byte>>(channelResult));
|
||||
return SendThenReturnAsync(sendMessage, channelResult.Content, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected virtual async ValueTask<MessageBase> SendThenReturnMessageAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var channelResult = GetChannel(this is IDtu dtu ? dtu.DtuId : null);
|
||||
if (!channelResult.IsSuccess) return new MessageBase(channelResult);
|
||||
return await SendThenReturnMessageBaseAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken cancellationToken = default)
|
||||
public virtual async ValueTask<OperResult<ReadOnlyMemory<byte>>> SendThenReturnAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await SendThenReturnMessageBaseAsync(sendMessage, channel, cancellationToken).ConfigureAwait(false);
|
||||
return new OperResult<byte[]>(result) { Content = result.Content };
|
||||
var result = await SendThenReturnMessageAsync(sendMessage, channel, cancellationToken).ConfigureAwait(false);
|
||||
return new OperResult<ReadOnlyMemory<byte>>(result) { Content = result.Content };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -517,7 +517,15 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected virtual ValueTask<MessageBase> SendThenReturnMessageBaseAsync(ISendMessage command, IClientChannel clientChannel = default, CancellationToken cancellationToken = default)
|
||||
protected virtual async ValueTask<MessageBase> SendThenReturnMessageAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var channelResult = GetChannel(this is IDtu dtu ? dtu.DtuId : null);
|
||||
if (!channelResult.IsSuccess) return new MessageBase(channelResult);
|
||||
return await SendThenReturnMessageAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected virtual ValueTask<MessageBase> SendThenReturnMessageAsync(ISendMessage command, IClientChannel clientChannel, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetResponsedDataAsync(command, clientChannel, Timeout, cancellationToken);
|
||||
}
|
||||
@@ -612,13 +620,13 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
#region 动态类型读写
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<IOperResult<Array>> ReadAsync(string address, int length, DataTypeEnum dataType, CancellationToken cancellationToken = default)
|
||||
public virtual async ValueTask<IOperResult<Array>> ReadArrayAsync(string address, int length, DataTypeEnum dataType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return dataType switch
|
||||
{
|
||||
DataTypeEnum.String => await ReadStringAsync(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Boolean => await ReadBooleanAsync(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Byte => await ReadAsync(address, length, cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Byte => (await ReadAsync(address, length, cancellationToken).ConfigureAwait(false)).OperResultFrom(a => a.ToArray()),
|
||||
DataTypeEnum.Int16 => await ReadInt16Async(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.UInt16 => await ReadUInt16Async(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Int32 => await ReadInt32Async(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
@@ -627,12 +635,13 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
DataTypeEnum.UInt64 => await ReadUInt64Async(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Single => await ReadSingleAsync(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Double => await ReadDoubleAsync(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Decimal => await ReadDecimalAsync(address, length, cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
_ => new OperResult<Array>(string.Format(AppResource.DataTypeNotSupported, dataType)),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<OperResult> WriteAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default)
|
||||
public virtual async ValueTask<OperResult> WriteJTokenAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -641,17 +650,18 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
return dataType switch
|
||||
{
|
||||
DataTypeEnum.String => await WriteAsync(address, jArray.ToObject<String[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Boolean => await WriteAsync(address, jArray.ToObject<Boolean[]>(), cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Byte => await WriteAsync(address, jArray.ToObject<Byte[]>(), dataType, cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Int16 => await WriteAsync(address, jArray.ToObject<Int16[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.UInt16 => await WriteAsync(address, jArray.ToObject<UInt16[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Int32 => await WriteAsync(address, jArray.ToObject<Int32[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.UInt32 => await WriteAsync(address, jArray.ToObject<UInt32[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Int64 => await WriteAsync(address, jArray.ToObject<Int64[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.UInt64 => await WriteAsync(address, jArray.ToObject<UInt64[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Single => await WriteAsync(address, jArray.ToObject<Single[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Double => await WriteAsync(address, jArray.ToObject<Double[]>(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.String => await WriteAsync(address, jArray.ToObject<String[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Boolean => await WriteAsync(address, jArray.ToObject<Boolean[]>().AsMemory(), cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Byte => await WriteAsync(address, jArray.ToObject<Byte[]>().AsMemory(), dataType, cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Int16 => await WriteAsync(address, jArray.ToObject<Int16[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.UInt16 => await WriteAsync(address, jArray.ToObject<UInt16[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Int32 => await WriteAsync(address, jArray.ToObject<Int32[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.UInt32 => await WriteAsync(address, jArray.ToObject<UInt32[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Int64 => await WriteAsync(address, jArray.ToObject<Int64[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.UInt64 => await WriteAsync(address, jArray.ToObject<UInt64[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Single => await WriteAsync(address, jArray.ToObject<Single[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Double => await WriteAsync(address, jArray.ToObject<Double[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Decimal => await WriteAsync(address, jArray.ToObject<Decimal[]>().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false),
|
||||
_ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)),
|
||||
};
|
||||
}
|
||||
@@ -670,6 +680,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
DataTypeEnum.UInt64 => await WriteAsync(address, value.ToObject<UInt64>(), bitConverter, cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Single => await WriteAsync(address, value.ToObject<Single>(), bitConverter, cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Double => await WriteAsync(address, value.ToObject<Double>(), bitConverter, cancellationToken).ConfigureAwait(false),
|
||||
DataTypeEnum.Decimal => await WriteAsync(address, value.ToObject<Decimal>(), bitConverter, cancellationToken).ConfigureAwait(false),
|
||||
_ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)),
|
||||
};
|
||||
}
|
||||
@@ -685,16 +696,16 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
#region 读取
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract ValueTask<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default);
|
||||
public abstract ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadAsync(string address, int length, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<OperResult<Boolean[]>> ReadBooleanAsync(string address, int length, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual async ValueTask<OperResult<bool[]>> ReadBooleanAsync(string address, int length, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
|
||||
var result = await ReadAsync(address, GetLength(address, length, RegisterByteLength, true), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return result.OperResultFrom(() => bitConverter.ToBoolean(result.Content, GetBitOffsetDefault(address), length, BitReverse(address)));
|
||||
return result.OperResultFrom(() => bitConverter.ToBoolean(result.Content.Span, GetBitOffsetDefault(address), length, BitReverse(address)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -702,7 +713,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 2), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToInt16(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToInt16(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -710,7 +721,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 2), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToUInt16(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToUInt16(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -718,7 +729,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 4), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToInt32(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToInt32(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -726,7 +737,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 4), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToUInt32(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToUInt32(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -734,7 +745,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 8), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToInt64(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToInt64(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -742,7 +753,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 8), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToUInt64(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToUInt64(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -750,7 +761,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 4), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToSingle(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToSingle(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -758,9 +769,15 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 8), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToDouble(result.Content, 0, length));
|
||||
return result.OperResultFrom(() => bitConverter.ToDouble(result.Content.Span, 0, length));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<OperResult<Decimal[]>> ReadDecimalAsync(string address, int length, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
var result = await ReadAsync(address, GetLength(address, length, 8), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() => bitConverter.ToDecimal(result.Content.Span, 0, length));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<OperResult<String[]>> ReadStringAsync(string address, int length, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -771,13 +788,13 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
var result = await ReadAsync(address, GetLength(address, len.Value, 1), cancellationToken).ConfigureAwait(false);
|
||||
return result.OperResultFrom(() =>
|
||||
{
|
||||
List<String> strings = new();
|
||||
String[] strings = new String[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
var data = bitConverter.ToString(result.Content, i * bitConverter.StringLength.Value, bitConverter.StringLength.Value);
|
||||
strings.Add(data);
|
||||
var data = bitConverter.ToString(result.Content.Span, i * bitConverter.StringLength.Value, bitConverter.StringLength.Value);
|
||||
strings[i] = data;
|
||||
}
|
||||
return strings.ToArray();
|
||||
return strings;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -787,10 +804,10 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
#region 写入
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract ValueTask<OperResult> WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
public abstract ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<byte> value, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract ValueTask<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default);
|
||||
public abstract ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<bool> value, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, bool value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
@@ -802,7 +819,7 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, byte value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, new byte[] { value }, DataTypeEnum.Byte, cancellationToken);
|
||||
return WriteAsync(address, new byte[] { value }.AsMemory(), DataTypeEnum.Byte, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -860,7 +877,12 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Double, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, decimal value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Decimal, cancellationToken);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, string value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -874,73 +896,81 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
#region 写入数组
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, short[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<short> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Int16, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.Int16, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ushort[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<ushort> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.UInt16, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.UInt16, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, int[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<int> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Int32, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.Int32, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, uint[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<uint> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.UInt32, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.UInt32, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, long[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<long> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Int64, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.Int64, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ulong[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<ulong> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.UInt64, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.UInt64, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, float[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<float> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Single, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.Single, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, double[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<double> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Double, cancellationToken);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.Double, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<OperResult> WriteAsync(string address, string[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<decimal> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.Decimal, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<string> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||
if (bitConverter.StringLength == null) return new OperResult(AppResource.StringAddressError);
|
||||
List<byte> bytes = new();
|
||||
foreach (var a in value)
|
||||
List<ReadOnlyMemory<byte>> bytes = new();
|
||||
foreach (var a in value.Span)
|
||||
{
|
||||
var data = bitConverter.GetBytes(a);
|
||||
bytes.AddRange(data.ArrayExpandToLength(bitConverter.StringLength ?? data.Length));
|
||||
bytes.Add(data.ArrayExpandToLength(bitConverter.StringLength ?? data.Length));
|
||||
}
|
||||
return await WriteAsync(address, bytes.ToArray(), DataTypeEnum.String, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return await WriteAsync(address, bytes.CombineMemoryBlocks(), DataTypeEnum.String, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#endregion 写入数组
|
||||
@@ -1015,5 +1045,5 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
||||
}
|
||||
return a => { };
|
||||
}
|
||||
public abstract ValueTask<OperResult<byte[]>> ReadAsync(object state, CancellationToken cancellationToken = default);
|
||||
public abstract ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadAsync(object state, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -99,7 +99,7 @@ public static partial class DeviceExtension
|
||||
/// <param name="buffer">返回的字节数组</param>
|
||||
/// <param name="exWhenAny">任意一个失败时抛出异常</param>
|
||||
/// <returns>解析结果</returns>
|
||||
public static OperResult PraseStructContent<T>(this IEnumerable<T> variables, IDevice device, byte[] buffer, bool exWhenAny) where T : IVariable
|
||||
public static OperResult PraseStructContent<T>(this IEnumerable<T> variables, IDevice device, ReadOnlySpan<byte> buffer, bool exWhenAny) where T : IVariable
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
var result = OperResult.Success;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -121,7 +121,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="dataType">数据类型</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
ValueTask<IOperResult<Array>> ReadAsync(string address, int length, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
ValueTask<IOperResult<Array>> ReadArrayAsync(string address, int length, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 根据数据类型,写入类型值
|
||||
@@ -131,7 +131,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="dataType">数据类型</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteJTokenAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion 动态类型读写
|
||||
|
||||
@@ -144,7 +144,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="length">读取寄存器数量,对于不同PLC,对应的字节数量可能不一样</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns></returns>
|
||||
ValueTask<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadAsync(string address, int length, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 读取布尔量数组
|
||||
@@ -253,12 +253,12 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <summary>
|
||||
/// 写入原始的byte数组数据到指定的地址,返回结果
|
||||
/// </summary>
|
||||
ValueTask<OperResult> WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<byte> value, DataTypeEnum dataType, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入bool数组数据,返回结果
|
||||
/// </summary>
|
||||
ValueTask<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<bool> value, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入bool数据,返回结果
|
||||
@@ -327,7 +327,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, string[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<string> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入Double数组
|
||||
@@ -337,7 +337,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, double[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<double> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入Single数组
|
||||
@@ -347,7 +347,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, float[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<float> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入Int32数组
|
||||
@@ -357,7 +357,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, int[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<int> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入Int64数组
|
||||
@@ -367,7 +367,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, long[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<long> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入Int16数组
|
||||
@@ -377,7 +377,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, short[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<short> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入UInt32数组
|
||||
@@ -387,7 +387,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, uint[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<uint> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入UInt64数组
|
||||
@@ -397,7 +397,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, ulong[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<ulong> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 写入UInt16数组
|
||||
@@ -407,7 +407,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="bitConverter">转换规则</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>写入结果</returns>
|
||||
ValueTask<OperResult> WriteAsync(string address, ushort[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<ushort> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion 写入数组
|
||||
|
||||
@@ -445,7 +445,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <param name="channel">通道</param>
|
||||
/// <returns>返回消息体</returns>
|
||||
ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage command, IClientChannel channel = default, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult<ReadOnlyMemory<byte>>> SendThenReturnAsync(ISendMessage command, IClientChannel channel = default, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 发送并等待返回,会经过适配器,可传入socketId,如果为空,则默认通道必须为<see cref="IClientChannel"/>类型
|
||||
@@ -453,7 +453,7 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="sendMessage">发送字节数组</param>
|
||||
/// <param name="cancellationToken">取消令箭</param>
|
||||
/// <returns>返回消息体</returns>
|
||||
ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult<ReadOnlyMemory<byte>>> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 支持通道多设备
|
||||
@@ -467,6 +467,6 @@ public interface IDevice : IDisposable, IDisposableObject
|
||||
/// <param name="channel">通道</param>
|
||||
/// <param name="deviceLog">单独设备日志</param>
|
||||
void InitChannel(IChannel channel, ILog? deviceLog = null);
|
||||
ValueTask<OperResult<byte[]>> ReadAsync(object state, CancellationToken cancellationToken = default);
|
||||
ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadAsync(object state, CancellationToken cancellationToken = default);
|
||||
Task ConnectAsync(CancellationToken token);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -50,4 +50,7 @@ public enum DataTypeEnum
|
||||
|
||||
/// <inheritdoc/>
|
||||
Double,
|
||||
|
||||
/// <inheritdoc/>
|
||||
Decimal,
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -16,23 +16,32 @@ namespace ThingsGateway.Foundation;
|
||||
public static class BoolExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将bool数组转换到byte数组
|
||||
/// 将布尔数组转换为压缩的字节数组(每 8 位布尔值压缩为 1 个字节,低位在前)。
|
||||
/// </summary>
|
||||
public static byte[] BoolArrayToByte(this bool[] array)
|
||||
/// <param name="array">布尔数组</param>
|
||||
/// <returns>压缩后的只读字节内存</returns>
|
||||
public static byte[] BoolArrayToByte(this ReadOnlySpan<bool> array)
|
||||
{
|
||||
if (array.IsEmpty)
|
||||
return Array.Empty<byte>();
|
||||
|
||||
int byteLength = (array.Length + 7) / 8;
|
||||
byte[] byteArray = new byte[byteLength];
|
||||
byte[] result = new byte[byteLength];
|
||||
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
if (array[i])
|
||||
{
|
||||
int byteIndex = i / 8;
|
||||
int bitOffset = i % 8;
|
||||
byteArray[byteIndex] |= (byte)(1 << bitOffset);
|
||||
result[i / 8] |= (byte)(1 << (i % 8));
|
||||
}
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将布尔数组转换为压缩的字节数组(每 8 位布尔值压缩为 1 个字节,低位在前)。
|
||||
/// </summary>
|
||||
public static byte[] BoolArrayToByte(this Span<bool> array)
|
||||
=> ((ReadOnlySpan<bool>)array).BoolArrayToByte();
|
||||
}
|
||||
|
@@ -0,0 +1,239 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 提供字节块扩展方法的静态类。
|
||||
/// </summary>
|
||||
public static class ByteBlockExtension
|
||||
{
|
||||
public static void WriteBackAddValue<TWriter>(ref TWriter writer, byte value, int pos)
|
||||
where TWriter : IByteBlockWriter
|
||||
{
|
||||
int nowPos = writer.Position;
|
||||
writer.Position = pos;
|
||||
WriterExtension.WriteValue(ref writer, (byte)(writer.Span[pos] + value));
|
||||
writer.Position = nowPos;
|
||||
}
|
||||
|
||||
|
||||
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, int pos)
|
||||
where T : unmanaged
|
||||
where TWriter : IByteBlockWriter
|
||||
{
|
||||
int nowPos = writer.Position;
|
||||
writer.Position = pos;
|
||||
WriterExtension.WriteValue(ref writer, value);
|
||||
writer.Position = nowPos;
|
||||
}
|
||||
|
||||
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, EndianType endianType, int pos)
|
||||
where T : unmanaged
|
||||
where TWriter : IByteBlockWriter
|
||||
{
|
||||
int nowPos = writer.Position;
|
||||
writer.Position = pos;
|
||||
WriterExtension.WriteValue(ref writer, value, endianType);
|
||||
writer.Position = nowPos;
|
||||
}
|
||||
|
||||
public static void WriteBackNormalString<TWriter>(ref TWriter writer, string value, Encoding encoding, int pos)
|
||||
where TWriter : IByteBlockWriter
|
||||
{
|
||||
|
||||
int nowPos = writer.Position;
|
||||
writer.Position = pos;
|
||||
WriterExtension.WriteNormalString(ref writer, value, encoding);
|
||||
writer.Position = nowPos;
|
||||
}
|
||||
|
||||
|
||||
public static string ReadNormalString<TReader>(ref TReader reader, int length)
|
||||
where TReader : IBytesReader
|
||||
{
|
||||
var span = reader.GetSpan(length).Slice(0, length);
|
||||
var str = span.ToString(Encoding.UTF8);
|
||||
reader.Advance(length);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将值类型的字节块转换为普通的字节块。
|
||||
/// </summary>
|
||||
/// <param name="valueByteBlock">要转换的值类型字节块。</param>
|
||||
/// <returns>一个新的字节块对象。</returns>
|
||||
public static ByteBlock AsByteBlock(this ValueByteBlock valueByteBlock)
|
||||
{
|
||||
ByteBlock byteBlock = new ByteBlock(valueByteBlock.TotalMemory.Slice(0, valueByteBlock.Length));
|
||||
byteBlock.Position = valueByteBlock.Position;
|
||||
byteBlock.SetLength(valueByteBlock.Length);
|
||||
return byteBlock;
|
||||
}
|
||||
|
||||
#region ToArray
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的字节块转换为【新】字节数组。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlock"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">字节块对象。</param>
|
||||
/// <param name="offset">起始偏移量。</param>
|
||||
/// <param name="length">要转换为数组的长度。</param>
|
||||
/// <returns>包含指定长度的【新】字节数组。</returns>
|
||||
public static byte[] ToArray<TByteBlock>(this TByteBlock byteBlock, int offset, int length) where TByteBlock : IByteBlockCore
|
||||
{
|
||||
return byteBlock.Span.Slice(offset, length).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的字节块转换为【新】字节数组,从指定偏移量开始,直到字节块的末尾。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlock"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">字节块对象。</param>
|
||||
/// <param name="offset">起始偏移量。</param>
|
||||
/// <returns>从指定偏移量到字节块末尾的【新】字节数组。</returns>
|
||||
public static byte[] ToArray<TByteBlock>(this TByteBlock byteBlock, int offset) where TByteBlock : IByteBlockCore
|
||||
{
|
||||
return ToArray(byteBlock, offset, byteBlock.Length - offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的字节块转换为【新】字节数组,从索引0开始,直到字节块的末尾。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlock"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">字节块对象。</param>
|
||||
/// <returns>整个字节块的【新】字节数组。</returns>
|
||||
public static byte[] ToArray<TByteBlock>(this TByteBlock byteBlock) where TByteBlock : IByteBlockCore
|
||||
{
|
||||
return ToArray(byteBlock, 0, byteBlock.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的字节块从当前位置<see cref="IByteBlockCore.Position"/>转换为【新】字节数组,直到字节块的末尾。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlock"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">字节块对象。</param>
|
||||
/// <returns>从当前位置到字节块末尾的【新】字节数组。</returns>
|
||||
public static byte[] ToArrayTake<TByteBlock>(this TByteBlock byteBlock) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return ToArray(byteBlock, byteBlock.Position, byteBlock.CanReadLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的字节块从当前位置<see cref="IByteBlockCore.Position"/>转换为【新】字节数组,指定长度。
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlock"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">字节块对象。</param>
|
||||
/// <param name="length">要转换为数组的长度。</param>
|
||||
/// <returns>从当前位置开始,指定长度的【新】字节数组。</returns>
|
||||
public static byte[] ToArrayTake<TByteBlock>(this TByteBlock byteBlock, int length) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return ToArray(byteBlock, byteBlock.Position, length);
|
||||
}
|
||||
|
||||
#endregion ToArray
|
||||
|
||||
#region AsSegment
|
||||
|
||||
/// <summary>
|
||||
/// 将字节块【作为】数组段。
|
||||
/// <para>
|
||||
/// 【作为】的意思是,导出的数据内存实际上依旧是<see cref="IByteBlockReader"/>生命周期内的,不能脱离生命周期使用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlockReader"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">要转换的字节块实例。</param>
|
||||
/// <param name="offset">数组段的起始偏移量。</param>
|
||||
/// <param name="length">数组段的长度。</param>
|
||||
/// <returns>一个包含指定偏移量和长度的数组段。</returns>
|
||||
public static ArraySegment<byte> AsSegment<TByteBlock>(this TByteBlock byteBlock, int offset, int length) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return byteBlock.Memory.Slice(offset, length).GetArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节块【作为】数组段,从指定偏移量开始,长度为可读长度。
|
||||
/// <para>
|
||||
/// 【作为】的意思是,导出的数据内存实际上依旧是<see cref="IByteBlockReader"/>生命周期内的,不能脱离生命周期使用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlockReader"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">要转换的字节块实例。</param>
|
||||
/// <param name="offset">数组段的起始偏移量。</param>
|
||||
/// <returns>一个从指定偏移量开始,长度为可读长度的数组段。</returns>
|
||||
public static ArraySegment<byte> AsSegment<TByteBlock>(this TByteBlock byteBlock, int offset) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return AsSegment(byteBlock, offset, byteBlock.Length - offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节块【作为】数组段,从头开始,长度为指定长度。
|
||||
/// <para>
|
||||
/// 【作为】的意思是,导出的数据内存实际上依旧是<see cref="IByteBlockReader"/>生命周期内的,不能脱离生命周期使用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlockReader"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">要转换的字节块实例。</param>
|
||||
/// <returns>一个从头开始,长度为字节块长度的数组段。</returns>
|
||||
public static ArraySegment<byte> AsSegment<TByteBlock>(this TByteBlock byteBlock) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return AsSegment(byteBlock, 0, byteBlock.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节块【作为】数组段,从当前位置开始,指定长度。
|
||||
/// <para>
|
||||
/// 【作为】的意思是,导出的数据内存实际上依旧是<see cref="IByteBlockReader"/>生命周期内的,不能脱离生命周期使用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlockReader"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">要转换的字节块实例。</param>
|
||||
/// <param name="length">数组段的长度。</param>
|
||||
/// <returns>一个从当前位置开始,指定长度的数组段。</returns>
|
||||
public static ArraySegment<byte> AsSegmentTake<TByteBlock>(this TByteBlock byteBlock, int length) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return AsSegment(byteBlock, byteBlock.Position, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节块【作为】数组段,从当前位置开始,长度为可读长度。
|
||||
/// <para>
|
||||
/// 【作为】的意思是,导出的数据内存实际上依旧是<see cref="IByteBlockReader"/>生命周期内的,不能脱离生命周期使用。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <typeparam name="TByteBlock">实现<see cref="IByteBlockReader"/>接口的字节块类型。</typeparam>
|
||||
/// <param name="byteBlock">要转换的字节块实例。</param>
|
||||
/// <returns>一个从当前位置开始,长度为可读长度的数组段。</returns>
|
||||
public static ArraySegment<byte> AsSegmentTake<TByteBlock>(this TByteBlock byteBlock) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return AsSegment(byteBlock, byteBlock.Position, byteBlock.CanReadLength);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public static string ToString<TByteBlock>(this TByteBlock byteBlock, int offset, int length) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return byteBlock.Span.Slice(offset, length).ToString(Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public static string ToString<TByteBlock>(this TByteBlock byteBlock, int offset) where TByteBlock : IByteBlockReader
|
||||
{
|
||||
return byteBlock.Span.Slice(offset, byteBlock.Length - offset).ToString(Encoding.UTF8);
|
||||
|
||||
}
|
||||
#endregion AsSegment
|
||||
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -21,6 +19,8 @@ public static class ByteExtensions
|
||||
{
|
||||
return DataTransUtil.SpliceArray<T>(array, values);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取byte数据类型的第offset位,是否为True<br />
|
||||
/// </summary>
|
||||
@@ -38,6 +38,24 @@ public static class ByteExtensions
|
||||
return (value & mask) == mask;
|
||||
}
|
||||
|
||||
public static byte[] BoolToByte(this ReadOnlySpan<bool> value, byte trueData = 0xff)
|
||||
{
|
||||
byte[] bytes = new byte[value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
bytes[i] = value[i] ? (byte)trueData : (byte)0;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
public static bool[] ByteToBool(this ReadOnlySpan<byte> value)
|
||||
{
|
||||
bool[] bytes = new bool[value.Length];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
bytes[i] = value[i] > 0 ? true : false;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
/// <summary>
|
||||
/// 数组内容分别相加某个数字
|
||||
/// </summary>
|
||||
@@ -46,28 +64,27 @@ public static class ByteExtensions
|
||||
/// <returns></returns>
|
||||
public static byte[] BytesAdd(this byte[] bytes, int value)
|
||||
{
|
||||
if (bytes == null || bytes.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Input byte array is null or empty");
|
||||
}
|
||||
|
||||
byte[] result = new byte[bytes.Length];
|
||||
for (int index = 0; index < bytes.Length; index++)
|
||||
{
|
||||
result[index] = (byte)(bytes[index] + value);
|
||||
}
|
||||
|
||||
return result;
|
||||
return BytesAdd((ReadOnlySpan<byte>)bytes, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数组内容分别相加某个数字
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static ReadOnlySpan<byte> BytesAdd(this ReadOnlySpan<byte> bytes, int value)
|
||||
public static byte[] BytesAdd(this Span<byte> bytes, int value)
|
||||
{
|
||||
return BytesAdd((ReadOnlySpan<byte>)bytes, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// 数组内容分别相加某个数字
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] BytesAdd(this ReadOnlySpan<byte> bytes, int value)
|
||||
{
|
||||
if (bytes.IsEmpty) return Array.Empty<byte>();
|
||||
byte[] result = new byte[bytes.Length];
|
||||
for (int index = 0; index < bytes.Length; index++)
|
||||
{
|
||||
@@ -82,104 +99,143 @@ public static class ByteExtensions
|
||||
/// </summary>
|
||||
/// <param name="inBytes">输入的字节信息</param>
|
||||
/// <returns>反转后的数据</returns>
|
||||
public static byte[] BytesReverseByWord(this byte[] inBytes)
|
||||
/// <summary>
|
||||
/// 将字节数组按“字(2字节)”为单位反转高低位,奇数长度自动补齐 0。
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<byte> BytesReverseByWord(this ReadOnlySpan<byte> inBytes)
|
||||
{
|
||||
if (inBytes.Length == 0)
|
||||
int len = inBytes.Length;
|
||||
if (len == 0)
|
||||
return ReadOnlySpan<byte>.Empty;
|
||||
|
||||
// 如果是奇数,自动补齐 0
|
||||
int evenLen = (len % 2 == 0) ? len : len + 1;
|
||||
if (evenLen == len) return inBytes;
|
||||
|
||||
Span<byte> result = new byte[evenLen];
|
||||
inBytes.CopyTo(result);
|
||||
|
||||
// 逐字(2 字节)交换
|
||||
for (int i = 0; i < evenLen; i += 2)
|
||||
{
|
||||
byte temp = result[i];
|
||||
result[i] = result[i + 1];
|
||||
result[i + 1] = temp;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将byte数组按照双字节进行反转,如果为单数的情况,则自动补齐<br />
|
||||
/// </summary>
|
||||
/// <param name="inBytes">输入的字节信息</param>
|
||||
/// <returns>反转后的数据</returns>
|
||||
/// <summary>
|
||||
/// 将字节数组按“字(2字节)”为单位反转高低位,奇数长度自动补齐 0。
|
||||
/// </summary>
|
||||
public static Memory<byte> BytesReverseByWord(this Memory<byte> inBytes)
|
||||
{
|
||||
int len = inBytes.Length;
|
||||
if (len == 0)
|
||||
return Memory<byte>.Empty;
|
||||
|
||||
// 如果是奇数,自动补齐 0
|
||||
int evenLen = (len % 2 == 0) ? len : len + 1;
|
||||
if (evenLen == len) return inBytes;
|
||||
|
||||
byte[] result = new byte[evenLen];
|
||||
inBytes.CopyTo(result);
|
||||
|
||||
// 逐字(2 字节)交换
|
||||
for (int i = 0; i < evenLen; i += 2)
|
||||
{
|
||||
byte temp = result[i];
|
||||
result[i] = result[i + 1];
|
||||
result[i + 1] = temp;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字节数组中提取位数组,length 代表位数
|
||||
/// </summary>
|
||||
/// <param name="inBytes">原始的字节数组</param>
|
||||
/// <param name="length">想要转换的位数,如果超出字节数组长度 * 8,则自动缩小为数组最大长度</param>
|
||||
/// <returns>转换后的布尔数组</returns>
|
||||
public static ReadOnlySpan<bool> ByteToBoolArray(this ReadOnlySpan<byte> inBytes, int length)
|
||||
{
|
||||
// 计算字节数组能够提供的最大位数
|
||||
int maxBitLength = inBytes.Length * 8;
|
||||
|
||||
// 如果指定长度超出最大位数,则将长度缩小为最大位数
|
||||
if (length > maxBitLength)
|
||||
{
|
||||
length = maxBitLength;
|
||||
}
|
||||
|
||||
// 创建对应长度的布尔数组
|
||||
bool[] boolArray = new bool[length];
|
||||
|
||||
// 从字节数组中提取位信息并转换为布尔值存储到布尔数组中
|
||||
for (int index = 0; index < length; ++index)
|
||||
{
|
||||
boolArray[index] = inBytes[index / 8].BoolOnByteIndex(index % 8);
|
||||
}
|
||||
|
||||
return boolArray;
|
||||
}
|
||||
|
||||
|
||||
public static ReadOnlyMemory<byte> CombineMemoryBlocks(this List<ReadOnlyMemory<byte>> blocks)
|
||||
{
|
||||
if (blocks == null || blocks.Count == 0)
|
||||
return ReadOnlyMemory<byte>.Empty;
|
||||
|
||||
// 计算总长度
|
||||
int totalLength = 0;
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
totalLength += block.Length;
|
||||
}
|
||||
|
||||
if (totalLength == 0)
|
||||
return ReadOnlyMemory<byte>.Empty;
|
||||
|
||||
// 分配目标数组
|
||||
byte[] result = new byte[totalLength];
|
||||
int offset = 0;
|
||||
|
||||
// 拷贝每一段内存
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
block.Span.CopyTo(result.AsSpan(offset));
|
||||
offset += block.Length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取异或校验,返回ASCII十六进制字符串的字节数组<br />
|
||||
/// </summary>
|
||||
public static byte[] GetAsciiXOR(this ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (data.IsEmpty)
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
// 创建新数组进行补齐
|
||||
byte[] lengthEven = inBytes.CopyArray<byte>().ArrayExpandToLengthEven();
|
||||
// 进行双字节反转
|
||||
for (int index = 0; index < lengthEven.Length / 2; ++index)
|
||||
|
||||
byte xor = data[0];
|
||||
for (int i = 1; i < data.Length; i++)
|
||||
{
|
||||
byte num = lengthEven[index * 2];
|
||||
lengthEven[index * 2] = lengthEven[index * 2 + 1];
|
||||
lengthEven[index * 2 + 1] = num;
|
||||
xor ^= data[i];
|
||||
}
|
||||
|
||||
return lengthEven;
|
||||
// 将结果转换为 2 位 ASCII 十六进制字符串,如 "3F" -> [0x33, 0x46]
|
||||
byte[] result = Encoding.ASCII.GetBytes(xor.ToString("X2"));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字节数组中提取位数组,length 代表位数
|
||||
/// </summary>
|
||||
/// <param name="inBytes">原始的字节数组</param>
|
||||
/// <param name="length">想要转换的位数,如果超出字节数组长度 * 8,则自动缩小为数组最大长度</param>
|
||||
/// <returns>转换后的布尔数组</returns>
|
||||
public static bool[] ByteToBoolArray(this byte[] inBytes, int length)
|
||||
{
|
||||
// 计算字节数组能够提供的最大位数
|
||||
int maxBitLength = inBytes.Length * 8;
|
||||
|
||||
// 如果指定长度超出最大位数,则将长度缩小为最大位数
|
||||
if (length > maxBitLength)
|
||||
{
|
||||
length = maxBitLength;
|
||||
}
|
||||
|
||||
// 创建对应长度的布尔数组
|
||||
bool[] boolArray = new bool[length];
|
||||
|
||||
// 从字节数组中提取位信息并转换为布尔值存储到布尔数组中
|
||||
for (int index = 0; index < length; ++index)
|
||||
{
|
||||
boolArray[index] = inBytes[index / 8].BoolOnByteIndex(index % 8);
|
||||
}
|
||||
|
||||
return boolArray;
|
||||
}
|
||||
/// <summary>
|
||||
/// 从字节数组中提取位数组,length 代表位数
|
||||
/// </summary>
|
||||
/// <param name="inBytes">原始的字节数组</param>
|
||||
/// <param name="length">想要转换的位数,如果超出字节数组长度 * 8,则自动缩小为数组最大长度</param>
|
||||
/// <returns>转换后的布尔数组</returns>
|
||||
public static bool[] ByteToBoolArray(this Span<byte> inBytes, int length)
|
||||
{
|
||||
// 计算字节数组能够提供的最大位数
|
||||
int maxBitLength = inBytes.Length * 8;
|
||||
|
||||
// 如果指定长度超出最大位数,则将长度缩小为最大位数
|
||||
if (length > maxBitLength)
|
||||
{
|
||||
length = maxBitLength;
|
||||
}
|
||||
|
||||
// 创建对应长度的布尔数组
|
||||
bool[] boolArray = new bool[length];
|
||||
|
||||
// 从字节数组中提取位信息并转换为布尔值存储到布尔数组中
|
||||
for (int index = 0; index < length; ++index)
|
||||
{
|
||||
boolArray[index] = inBytes[index / 8].BoolOnByteIndex(index % 8);
|
||||
}
|
||||
|
||||
return boolArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取异或校验
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] GetAsciiXOR(this byte[] data, int left, int right)
|
||||
{
|
||||
if (data == null || left < 0 || right < 0 || left >= data.Length || right >= data.Length || left > right)
|
||||
{
|
||||
throw new ArgumentException("Invalid input parameters");
|
||||
}
|
||||
|
||||
byte tmp = data[left];
|
||||
for (int i = left + 1; i <= right; i++)
|
||||
{
|
||||
tmp ^= data[i];
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetBytes(tmp.ToString("X2"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Byte数组的第 boolIndex 偏移的bool值,这个偏移值可以为 10,就是第 1 个字节的 第3位 <br />
|
||||
@@ -187,7 +243,7 @@ public static class ByteExtensions
|
||||
/// <param name="bytes">字节数组信息</param>
|
||||
/// <param name="boolIndex">指定字节的位偏移</param>
|
||||
/// <returns>bool值</returns>
|
||||
public static bool GetBoolByIndex(this byte[] bytes, int boolIndex)
|
||||
public static bool GetBoolByIndex(this ReadOnlySpan<byte> bytes, int boolIndex)
|
||||
{
|
||||
return bytes[boolIndex / 8].BoolOnByteIndex(boolIndex % 8);
|
||||
}
|
||||
@@ -195,42 +251,34 @@ public static class ByteExtensions
|
||||
/// <summary>
|
||||
/// 字节数组默认转16进制字符
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="splite"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToHexString(this ArraySegment<byte> buffer, char splite = ' ')
|
||||
public static string ToHexString(this ArraySegment<byte> buffer, char splite = ' ', int newLineCount = 0)
|
||||
{
|
||||
return DataTransUtil.ByteToHexString(buffer, splite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字节数组默认转16进制字符
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="splite"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToHexString(this ReadOnlySpan<byte> buffer, char splite = ' ')
|
||||
{
|
||||
return DataTransUtil.ByteToHexString(buffer, splite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字节数组默认转16进制字符
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="splite"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToHexString(this byte[] buffer, char splite = default)
|
||||
{
|
||||
return DataTransUtil.ByteToHexString(buffer, splite);
|
||||
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字节数组默认转16进制字符
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string ToHexString(this byte[] buffer, int offset, int length, char splite = ' ', int newLineCount = 0)
|
||||
public static string ToHexString(this byte[] buffer, char splite = ' ', int newLineCount = 0)
|
||||
{
|
||||
return DataTransUtil.ByteToHexString(buffer, offset, length, splite, newLineCount);
|
||||
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
|
||||
}
|
||||
/// <summary>
|
||||
/// 字节数组默认转16进制字符
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string ToHexString(this Span<byte> buffer, char splite = ' ', int newLineCount = 0)
|
||||
{
|
||||
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
|
||||
}
|
||||
/// <summary>
|
||||
/// 字节数组默认转16进制字符
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string ToHexString(this ReadOnlySpan<byte> buffer, char splite = ' ', int newLineCount = 0)
|
||||
{
|
||||
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -34,6 +34,7 @@ public static class DataTypeExtensions
|
||||
DataTypeEnum.UInt64 => 8,
|
||||
DataTypeEnum.Single => 4,
|
||||
DataTypeEnum.Double => 8,
|
||||
DataTypeEnum.Decimal => 16,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
@@ -58,6 +59,7 @@ public static class DataTypeExtensions
|
||||
TypeCode.UInt64 => DataTypeEnum.UInt64,
|
||||
TypeCode.Single => DataTypeEnum.Single,
|
||||
TypeCode.Double => DataTypeEnum.Double,
|
||||
TypeCode.Decimal => DataTypeEnum.Decimal,
|
||||
_ => DataTypeEnum.Object,
|
||||
};
|
||||
}
|
||||
@@ -82,6 +84,7 @@ public static class DataTypeExtensions
|
||||
DataTypeEnum.UInt64 => typeof(ulong),
|
||||
DataTypeEnum.Single => typeof(float),
|
||||
DataTypeEnum.Double => typeof(double),
|
||||
DataTypeEnum.Decimal => typeof(decimal),
|
||||
_ => typeof(object),
|
||||
};
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -32,6 +32,66 @@ public static class GenericExtensions
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个数组进行扩充到指定长度,或是缩短到指定长度<br />
|
||||
/// </summary>
|
||||
public static Memory<T> ArrayExpandToLength<T>(this Memory<T> data, int length)
|
||||
{
|
||||
if (data.IsEmpty)
|
||||
{
|
||||
return Memory<T>.Empty;
|
||||
}
|
||||
|
||||
if (data.Length == length)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
var result = new T[length];
|
||||
data.Slice(0, Math.Min(data.Length, length)).CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个数组进行扩充到指定长度,或是缩短到指定长度<br />
|
||||
/// </summary>
|
||||
public static ReadOnlyMemory<T> ArrayExpandToLength<T>(this ReadOnlyMemory<T> data, int length)
|
||||
{
|
||||
if (data.IsEmpty)
|
||||
{
|
||||
return ReadOnlyMemory<T>.Empty;
|
||||
}
|
||||
|
||||
if (data.Length == length)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
var result = new T[length];
|
||||
data.Slice(0, Math.Min(data.Length, length)).CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个数组进行扩充到指定长度,或是缩短到指定长度<br />
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<T> ArrayExpandToLength<T>(this ReadOnlySpan<T> data, int length)
|
||||
{
|
||||
if (data.IsEmpty)
|
||||
{
|
||||
return ReadOnlySpan<T>.Empty;
|
||||
}
|
||||
|
||||
if (data.Length == length)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
var result = new T[length];
|
||||
data.Slice(0, Math.Min(data.Length, length)).CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个数组进行扩充到偶数长度<br />
|
||||
/// </summary>
|
||||
@@ -44,11 +104,41 @@ public static class GenericExtensions
|
||||
|
||||
return data.Length % 2 == 1 ? data.ArrayExpandToLength(data.Length + 1) : data;
|
||||
}
|
||||
/// <summary>
|
||||
/// 将一个数组进行扩充到偶数长度<br />
|
||||
/// </summary>
|
||||
public static ReadOnlyMemory<T> ArrayExpandToLengthEven<T>(this ReadOnlyMemory<T> data)
|
||||
{
|
||||
if (data.IsEmpty)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
return data.Length % 2 == 1 ? data.ArrayExpandToLength(data.Length + 1) : data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="ArrayRemoveDouble{T}(T[], int, int)"/>
|
||||
/// 将一个数组进行扩充到偶数长度<br />
|
||||
/// </summary>
|
||||
public static T[] ArrayRemoveBegin<T>(T[] value, int length) => ArrayRemoveDouble(value, length, 0);
|
||||
public static Memory<T> ArrayExpandToLengthEven<T>(this Memory<T> data)
|
||||
{
|
||||
if (data.IsEmpty)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
return data.Length % 2 == 1 ? data.ArrayExpandToLength(data.Length + 1) : data;
|
||||
}
|
||||
|
||||
|
||||
public static T[] ArrayRemoveBegin<T>(this T[] value, int length) => ArrayRemoveDouble(value, length, 0);
|
||||
public static T[] ArrayRemoveLast<T>(this T[] value, int length) => ArrayRemoveDouble(value, 0, length);
|
||||
|
||||
public static ReadOnlySpan<T> ArrayRemoveBegin<T>(ReadOnlySpan<T> value, int length) => ArrayRemoveDouble(value, length, 0);
|
||||
public static ReadOnlySpan<T> ArrayRemoveLast<T>(ReadOnlySpan<T> value, int length) => ArrayRemoveDouble(value, 0, length);
|
||||
|
||||
public static ReadOnlyMemory<T> ArrayRemoveBegin<T>(ReadOnlyMemory<T> value, int length) => ArrayRemoveDouble(value, length, 0);
|
||||
public static ReadOnlyMemory<T> ArrayRemoveLast<T>(ReadOnlyMemory<T> value, int length) => ArrayRemoveDouble(value, 0, length);
|
||||
|
||||
/// <summary>
|
||||
/// 从数组中移除指定数量的元素,并返回新的数组
|
||||
@@ -59,9 +149,21 @@ public static class GenericExtensions
|
||||
/// <param name="rightLength">从右侧移除的元素个数</param>
|
||||
/// <returns>移除元素后的新数组</returns>
|
||||
public static T[] ArrayRemoveDouble<T>(T[] value, int leftLength, int rightLength)
|
||||
{
|
||||
return ArrayRemoveDouble((ReadOnlySpan<T>)value, leftLength, rightLength).ToArray();
|
||||
}
|
||||
/// <summary>
|
||||
/// 从数组中移除指定数量的元素,并返回新的数组
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型</typeparam>
|
||||
/// <param name="value">要移除元素的数组</param>
|
||||
/// <param name="leftLength">从左侧移除的元素个数</param>
|
||||
/// <param name="rightLength">从右侧移除的元素个数</param>
|
||||
/// <returns>移除元素后的新数组</returns>
|
||||
public static ReadOnlySpan<T> ArrayRemoveDouble<T>(ReadOnlySpan<T> value, int leftLength, int rightLength)
|
||||
{
|
||||
// 如果输入数组为空或者剩余长度不足以移除左右两侧指定的元素,则返回空数组
|
||||
if (value == null || value.Length <= leftLength + rightLength)
|
||||
if (value.IsEmpty || value.Length <= leftLength + rightLength)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
@@ -69,13 +171,28 @@ public static class GenericExtensions
|
||||
// 计算新数组的长度
|
||||
int newLength = value.Length - leftLength - rightLength;
|
||||
|
||||
// 创建新数组
|
||||
T[] result = new T[newLength];
|
||||
return value.Slice(leftLength, newLength);
|
||||
}
|
||||
/// <summary>
|
||||
/// 从数组中移除指定数量的元素,并返回新的数组
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组元素类型</typeparam>
|
||||
/// <param name="value">要移除元素的数组</param>
|
||||
/// <param name="leftLength">从左侧移除的元素个数</param>
|
||||
/// <param name="rightLength">从右侧移除的元素个数</param>
|
||||
/// <returns>移除元素后的新数组</returns>
|
||||
public static ReadOnlyMemory<T> ArrayRemoveDouble<T>(ReadOnlyMemory<T> value, int leftLength, int rightLength)
|
||||
{
|
||||
// 如果输入数组为空或者剩余长度不足以移除左右两侧指定的元素,则返回空数组
|
||||
if (value.IsEmpty || value.Length <= leftLength + rightLength)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
// 将剩余的元素复制到新数组中
|
||||
Array.Copy(value, leftLength, result, 0, newLength);
|
||||
// 计算新数组的长度
|
||||
int newLength = value.Length - leftLength - rightLength;
|
||||
|
||||
return result;
|
||||
return value.Slice(leftLength, newLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,7 +237,13 @@ public static class GenericExtensions
|
||||
yield return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<ReadOnlyMemory<T>> ChunkBetter<T>(this ReadOnlyMemory<T> span, int groupSize)
|
||||
{
|
||||
for (int i = 0; i < span.Length; i += groupSize)
|
||||
{
|
||||
yield return span.Slice(i, Math.Min(groupSize, span.Length - i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>拷贝当前的实例数组,是基于引用层的浅拷贝,如果类型为值类型,那就是深度拷贝,如果类型为引用类型,就是浅拷贝</summary>
|
||||
public static T[] CopyArray<T>(this T[] value)
|
||||
@@ -153,37 +276,7 @@ public static class GenericExtensions
|
||||
return arrayFromOneArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个数组的前后移除指定位数,返回新的一个数组<br />
|
||||
/// </summary>
|
||||
public static T[] RemoveArray<T>(this T[] value, int leftLength, int rightLength)
|
||||
{
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
int newLength = value.Length - leftLength - rightLength;
|
||||
if (newLength <= 0)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
T[] result = new T[newLength];
|
||||
Array.Copy(value, leftLength, result, 0, newLength);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个数组的前面指定位数移除,返回新的一个数组<br />
|
||||
/// </summary>
|
||||
public static T[] RemoveBegin<T>(this T[] value, int length) => value.RemoveArray(length, 0);
|
||||
|
||||
/// <summary>
|
||||
/// 将一个数组的后面指定位数移除,返回新的一个数组<br />
|
||||
/// </summary>
|
||||
public static T[] RemoveLast<T>(this T[] value, int length) => value.RemoveArray(0, length);
|
||||
|
||||
/// <summary>
|
||||
/// 选择数组中的最后几个元素组成新的数组
|
||||
@@ -192,25 +285,7 @@ public static class GenericExtensions
|
||||
/// <param name="value">输入数组</param>
|
||||
/// <param name="length">选择的元素个数</param>
|
||||
/// <returns>由最后几个元素组成的新数组</returns>
|
||||
public static T[] SelectLast<T>(this T[] value, int length)
|
||||
{
|
||||
// 如果输入数组为空,则返回空数组
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
// 计算实际需要复制的元素个数,取输入数组长度和指定长度的较小值
|
||||
int count = Math.Min(value.Length, length);
|
||||
|
||||
// 创建新数组来存储选择的元素
|
||||
T[] result = new T[count];
|
||||
|
||||
// 复制最后几个元素到新数组中
|
||||
Array.Copy(value, value.Length - count, result, 0, count);
|
||||
|
||||
return result;
|
||||
}
|
||||
public static T[] SelectLast<T>(this T[] value, int length) => ArrayRemoveBegin(value, value.Length - length);
|
||||
|
||||
/// <summary>
|
||||
/// 从数组中获取指定索引开始的中间一段长度的子数组
|
||||
@@ -220,23 +295,5 @@ public static class GenericExtensions
|
||||
/// <param name="index">起始索引</param>
|
||||
/// <param name="length">选择的元素个数</param>
|
||||
/// <returns>中间指定长度的子数组</returns>
|
||||
public static T[] SelectMiddle<T>(this T[] value, int index, int length)
|
||||
{
|
||||
// 如果输入数组为空,则返回空数组
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
// 计算实际需要复制的元素个数,取输入数组剩余元素和指定长度的较小值
|
||||
int count = Math.Min(value.Length - index, length);
|
||||
|
||||
// 创建新数组来存储选择的元素
|
||||
T[] result = new T[count];
|
||||
|
||||
// 复制中间指定长度的元素到新数组中
|
||||
Array.Copy(value, index, result, 0, count);
|
||||
|
||||
return result;
|
||||
}
|
||||
public static T[] SelectMiddle<T>(this T[] value, int index, int length) => ArrayRemoveDouble(value, index, value.Length - index - length);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
public static class PackHelpers
|
||||
{
|
||||
public static List<T> GetSourceRead<T>(this IEnumerable<IVariable> deviceVariables, IDevice device, string defaultIntervalTime) where T : IVariableSource, new()
|
||||
{
|
||||
var byteConverter = device.ThingsGatewayBitConverter;
|
||||
var result = new List<T>();
|
||||
//需要先剔除额外信息,比如dataformat等
|
||||
foreach (var item in deviceVariables)
|
||||
{
|
||||
var address = item.RegisterAddress;
|
||||
if (address == null)
|
||||
continue;
|
||||
IThingsGatewayBitConverter transformParameter = byteConverter.GetTransByAddress(address);
|
||||
item.ThingsGatewayBitConverter = transformParameter;
|
||||
item.Index = 0;
|
||||
if (item.DataType == DataTypeEnum.Boolean)
|
||||
item.Index = device.GetBitOffsetDefault(item.RegisterAddress);
|
||||
}
|
||||
var group = deviceVariables.GroupBy(a => a.RegisterAddress);
|
||||
foreach (var item in group)
|
||||
{
|
||||
var r = new T()
|
||||
{
|
||||
RegisterAddress = item.Key!,
|
||||
Length = 1,
|
||||
IntervalTime = string.IsNullOrWhiteSpace(item.FirstOrDefault().IntervalTime) ? defaultIntervalTime : item.FirstOrDefault().IntervalTime,
|
||||
};
|
||||
r.AddVariableRange(item);
|
||||
result.Add(r);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -184,7 +184,7 @@ public static class StringExtensions
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] HexStringToBytes(this string str) => DataTransUtil.HexStringToBytes(str);
|
||||
public static Memory<byte> HexStringToBytes(this string str) => DataTransUtil.HexStringToBytes(str);
|
||||
private static readonly char[] DotSeparator = new char[] { '.' };
|
||||
private static readonly char[] SlashSeparator = new char[] { '/' };
|
||||
private static readonly char[] CommaSeparator = new char[] { ',' };
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -18,7 +18,7 @@ public static class OperResultExtension
|
||||
/// <summary>
|
||||
/// 转换对应类型
|
||||
/// </summary>
|
||||
public static OperResult<TResult> GetResultFromBytes<TResult>(this OperResult<byte[]> result, Func<byte[]?, TResult> translator)
|
||||
public static OperResult<TResult> GetResultFromBytes<TResult>(this OperResult<ReadOnlyMemory<byte>> result, Func<ReadOnlyMemory<byte>, TResult> translator)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -26,7 +26,35 @@ public static class OperResultExtension
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<TResult>(string.Format(AppResource.TransBytesError, result.Content?.ToHexString(' '), result.Content?.Length), ex);
|
||||
return new OperResult<TResult>(string.Format(AppResource.TransBytesError, result.Content.Span.ToHexString(' '), result.Content.Span.Length), ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 转换对应类型
|
||||
/// </summary>
|
||||
public static OperResult<TResult> GetResultFromBytes<TResult>(this OperResult<Memory<byte>> result, Func<Memory<byte>, TResult> translator)
|
||||
{
|
||||
try
|
||||
{
|
||||
return result.IsSuccess ? new() { Content = translator(result.Content) } : new OperResult<TResult>(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<TResult>(string.Format(AppResource.TransBytesError, result.Content.Span.ToHexString(' '), result.Content.Span.Length), ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 转换对应类型
|
||||
/// </summary>
|
||||
public static OperResult<TResult> GetResultFromBytes<TResult>(this OperResult<byte[]> result, Func<byte[], TResult> translator)
|
||||
{
|
||||
try
|
||||
{
|
||||
return result.IsSuccess ? new() { Content = translator(result.Content) } : new OperResult<TResult>(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<TResult>(string.Format(AppResource.TransBytesError, result.Content.ToHexString(' '), result.Content.Length), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +69,16 @@ public static class OperResultExtension
|
||||
return new OperResult<T1>(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 操作成功则继续,并返回对应结果值
|
||||
/// </summary>
|
||||
public static OperResult<T1> OperResultFrom<T1, T2>(this OperResult<T2> result, Func<T2, T1> func)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
return new OperResult<T1>() { Content = func(result.Content) };
|
||||
else
|
||||
return new OperResult<T1>(result);
|
||||
}
|
||||
/// <summary>
|
||||
/// 操作成功则继续,并返回对应结果值
|
||||
/// </summary>
|
||||
@@ -51,7 +89,6 @@ public static class OperResultExtension
|
||||
else
|
||||
return new OperResult<T1>(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 操作成功则继续
|
||||
/// </summary>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -10,8 +10,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(NET9Version)" />
|
||||
<PackageReference Include="TouchSocket" Version="3.1.14" />
|
||||
<PackageReference Include="TouchSocket.SerialPorts" Version="3.1.14" />
|
||||
<PackageReference Include="TouchSocket" Version="4.0.0-Alpha.12" />
|
||||
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-Alpha.12" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -95,7 +95,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// </summary>
|
||||
/// <param name="values">等待转化的数组</param>
|
||||
/// <returns>buffer数据</returns>
|
||||
byte[] GetBytes(bool[] values);
|
||||
byte[] GetBytes(ReadOnlySpan<bool> values);
|
||||
|
||||
/// <summary>
|
||||
/// short变量转化缓存数据,一个short数据可以转为2个字节的byte数组<br />
|
||||
@@ -105,29 +105,30 @@ public interface IThingsGatewayBitConverter
|
||||
byte[] GetBytes(short value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(short[] value);
|
||||
byte[] GetBytes(ReadOnlySpan<short> value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(ushort[] value);
|
||||
byte[] GetBytes(ReadOnlySpan<ushort> value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(int[] value);
|
||||
byte[] GetBytes(ReadOnlySpan<int> value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(uint[] value);
|
||||
byte[] GetBytes(ReadOnlySpan<uint> value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(long[] value);
|
||||
byte[] GetBytes(ReadOnlySpan<long> value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(ulong[] value);
|
||||
byte[] GetBytes(ReadOnlySpan<ulong> value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(float[] value);
|
||||
byte[] GetBytes(ReadOnlySpan<float> value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(double[] value);
|
||||
|
||||
byte[] GetBytes(ReadOnlySpan<double> value);
|
||||
/// <inheritdoc/>
|
||||
byte[] GetBytes(ReadOnlySpan<decimal> value);
|
||||
/// <summary>
|
||||
/// ushort变量转化缓存数据,一个ushort数据可以转为2个字节的Byte数组<br />
|
||||
/// </summary>
|
||||
@@ -182,7 +183,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// </summary>
|
||||
/// <param name="value">等待转化的数据</param>
|
||||
/// <returns>buffer数据</returns>
|
||||
byte[] GetBytes(string value);
|
||||
Memory<byte> GetBytes(string value);
|
||||
|
||||
#endregion GetBytes
|
||||
|
||||
@@ -194,16 +195,16 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">等待提取的缓存数据</param>
|
||||
/// <param name="offset">位的索引,注意:是从0开始的位索引,10则表示 buffer[1] 的第二位。</param>
|
||||
/// <param name="isReverse">是否需要按字反转</param>
|
||||
bool ToBoolean(byte[] buffer, int offset, bool isReverse);
|
||||
bool ToBoolean(ReadOnlySpan<byte> buffer, int offset, bool isReverse);
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool[] ToBoolean(byte[] buffer, int offset, int len, bool isReverse);
|
||||
bool[] ToBoolean(ReadOnlySpan<byte> buffer, int offset, int len, bool isReverse);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte ToByte(byte[] buffer, int offset);
|
||||
byte ToByte(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
byte[] ToByte(byte[] buffer, int offset, int length);
|
||||
byte[] ToByte(ReadOnlySpan<byte> buffer, int offset, int length);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取double结果,需要指定起始的字节索引,按照字节为单位,一个double占用八个字节<br />
|
||||
@@ -211,10 +212,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存对象</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>double对象</returns>
|
||||
double ToDouble(byte[] buffer, int offset);
|
||||
double ToDouble(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
double[] ToDouble(byte[] buffer, int offset, int len);
|
||||
double[] ToDouble(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取short结果,需要指定起始的字节索引,按照字节为单位,一个short占用两个字节<br />
|
||||
@@ -222,10 +223,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存数据</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>short对象</returns>
|
||||
short ToInt16(byte[] buffer, int offset);
|
||||
short ToInt16(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
short[] ToInt16(byte[] buffer, int offset, int len);
|
||||
short[] ToInt16(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取int结果,需要指定起始的字节索引,按照字节为单位,一个int占用四个字节<br />
|
||||
@@ -233,10 +234,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存数据</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>int对象</returns>
|
||||
int ToInt32(byte[] buffer, int offset);
|
||||
int ToInt32(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
int[] ToInt32(byte[] buffer, int offset, int len);
|
||||
int[] ToInt32(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取long结果,需要指定起始的字节索引,按照字节为单位,一个long占用八个字节<br />
|
||||
@@ -244,10 +245,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存数据</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>long对象</returns>
|
||||
long ToInt64(byte[] buffer, int offset);
|
||||
long ToInt64(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
long[] ToInt64(byte[] buffer, int offset, int len);
|
||||
long[] ToInt64(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取float结果,需要指定起始的字节索引,按照字节为单位,一个float占用四个字节<b />
|
||||
@@ -255,10 +256,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存对象</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>float对象</returns>
|
||||
float ToSingle(byte[] buffer, int offset);
|
||||
float ToSingle(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
float[] ToSingle(byte[] buffer, int offset, int len);
|
||||
float[] ToSingle(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中的部分字节数组转化为string结果,使用指定的编码,指定起始的字节索引,字节长度信息。<br />
|
||||
@@ -267,7 +268,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <param name="length">byte数组长度</param>
|
||||
/// <returns>string对象</returns>
|
||||
string ToString(byte[] buffer, int offset, int length);
|
||||
string ToString(ReadOnlySpan<byte> buffer, int offset, int length);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取ushort结果,需要指定起始的字节索引,按照字节为单位,一个ushort占用两个字节<br />
|
||||
@@ -275,10 +276,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存数据</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>ushort对象</returns>
|
||||
ushort ToUInt16(byte[] buffer, int offset);
|
||||
ushort ToUInt16(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
ushort[] ToUInt16(byte[] buffer, int offset, int len);
|
||||
ushort[] ToUInt16(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取uint结果,需要指定起始的字节索引,按照字节为单位,一个uint占用四个字节<br />
|
||||
@@ -286,10 +287,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存数据</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>uint对象</returns>
|
||||
uint ToUInt32(byte[] buffer, int offset);
|
||||
uint ToUInt32(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
uint[] ToUInt32(byte[] buffer, int offset, int len);
|
||||
uint[] ToUInt32(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取ulong结果,需要指定起始的字节索引,按照字节为单位,一个ulong占用八个字节<b />
|
||||
@@ -297,10 +298,10 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer">缓存数据</param>
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <returns>ulong对象</returns>
|
||||
ulong ToUInt64(byte[] buffer, int offset);
|
||||
ulong ToUInt64(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <inheritdoc/>
|
||||
ulong[] ToUInt64(byte[] buffer, int offset, int len);
|
||||
ulong[] ToUInt64(ReadOnlySpan<byte> buffer, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 转换为指定端模式的<see cref="decimal"/>数据。
|
||||
@@ -308,7 +309,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
decimal ToDecimal(byte[] buffer, int offset);
|
||||
decimal ToDecimal(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <summary>
|
||||
/// 转换为指定端模式的Char数据。
|
||||
@@ -316,7 +317,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
char ToChar(byte[] buffer, int offset);
|
||||
char ToChar(ReadOnlySpan<byte> buffer, int offset);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中提取decimal结果,需要指定起始的字节索引,按照字节为单位,一个decimal占用16个字节<b />
|
||||
@@ -325,7 +326,7 @@ public interface IThingsGatewayBitConverter
|
||||
/// <param name="offset">索引位置</param>
|
||||
/// <param name="length">length</param>
|
||||
/// <returns>decimal对象</returns>
|
||||
decimal[] ToDecimal(byte[] buffer, int offset, int length);
|
||||
decimal[] ToDecimal(ReadOnlySpan<byte> buffer, int offset, int length);
|
||||
IThingsGatewayBitConverter GetTransByAddress(string? registerAddress);
|
||||
|
||||
#endregion ToValue
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -26,9 +26,9 @@ public static class ThingsGatewayBitConverterExtension
|
||||
/// <summary>
|
||||
/// 根据数据类型获取字节数组
|
||||
/// </summary>
|
||||
public static byte[] GetBytesFormData(this IThingsGatewayBitConverter byteConverter, JToken value, DataTypeEnum dataType, int arrayLength = 1)
|
||||
public static ReadOnlyMemory<byte> GetBytesFormData(this IThingsGatewayBitConverter byteConverter, JToken value, DataTypeEnum dataType, bool array)
|
||||
{
|
||||
if (arrayLength > 1)
|
||||
if (array)
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
@@ -62,16 +62,21 @@ public static class ThingsGatewayBitConverterExtension
|
||||
case DataTypeEnum.Double:
|
||||
return byteConverter.GetBytes(value.ToObject<Double[]>());
|
||||
|
||||
case DataTypeEnum.Decimal:
|
||||
return byteConverter.GetBytes(value.ToObject<Decimal[]>());
|
||||
|
||||
case DataTypeEnum.String:
|
||||
default:
|
||||
List<byte> bytes = new();
|
||||
List<ReadOnlyMemory<byte>> bytes = new();
|
||||
|
||||
String[] strings = value.ToObject<String[]>();
|
||||
for (int i = 0; i < arrayLength; i++)
|
||||
for (int i = 0; i < strings.Length; i++)
|
||||
{
|
||||
var data = byteConverter.GetBytes(strings[i]);
|
||||
bytes.AddRange(data.ArrayExpandToLength(byteConverter.StringLength ?? data.Length));
|
||||
bytes.Add(data.ArrayExpandToLength(byteConverter.StringLength ?? data.Length));
|
||||
}
|
||||
return bytes.ToArray();
|
||||
return bytes.CombineMemoryBlocks();
|
||||
default:
|
||||
throw new(string.Format(ThingsGateway.Foundation.AppResource.DataTypeNotSupported, dataType));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -108,9 +113,14 @@ public static class ThingsGatewayBitConverterExtension
|
||||
case DataTypeEnum.Double:
|
||||
return byteConverter.GetBytes(value.ToObject<Double>());
|
||||
|
||||
case DataTypeEnum.Decimal:
|
||||
return byteConverter.GetBytes(value.ToObject<Decimal>());
|
||||
|
||||
case DataTypeEnum.String:
|
||||
default:
|
||||
return byteConverter.GetBytes(value.ToObject<String>());
|
||||
|
||||
default:
|
||||
throw new(string.Format(ThingsGateway.Foundation.AppResource.DataTypeNotSupported, dataType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +132,7 @@ public static class ThingsGatewayBitConverterExtension
|
||||
this IThingsGatewayBitConverter byteConverter,
|
||||
IDevice device,
|
||||
string address,
|
||||
byte[] buffer,
|
||||
ReadOnlySpan<byte> buffer,
|
||||
int index,
|
||||
DataTypeEnum dataType,
|
||||
int arrayLength,
|
||||
@@ -370,7 +380,29 @@ public static class ThingsGatewayBitConverterExtension
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.Decimal:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = byteConverter.ToDecimal(buffer, index, arrayLength);
|
||||
if (oldValue is decimal[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = byteConverter.ToDecimal(buffer, index);
|
||||
if (oldValue is decimal oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
case DataTypeEnum.String:
|
||||
default:
|
||||
if (arrayLength > 1)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -36,9 +36,11 @@ public static class CRC16Utils
|
||||
{
|
||||
int length = bytes.Length;
|
||||
int num = 0xFFFF;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
num = (num >> ((!crc16) ? 8 : 0)) ^ bytes[i];
|
||||
num = (num >> (crc16 ? 0 : 8)) ^ bytes[i];
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
int num2 = num & 1;
|
||||
@@ -50,14 +52,22 @@ public static class CRC16Utils
|
||||
}
|
||||
}
|
||||
|
||||
return (!crc16) ?
|
||||
[
|
||||
(byte)(num >> 8),
|
||||
(byte)((uint)num & 0xFFu)
|
||||
] :
|
||||
[
|
||||
(byte)((uint)num & 0xFFu),
|
||||
if (crc16)
|
||||
{
|
||||
return new byte[]
|
||||
{
|
||||
(byte)(num & 0xFFu),
|
||||
(byte)(num >> 8)
|
||||
];
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new byte[]
|
||||
{
|
||||
(byte)(num >> 8),
|
||||
(byte)(num & 0xFFu)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -22,8 +22,9 @@ public static class DataTransUtil
|
||||
/// </summary>
|
||||
/// <param name="InBytes">字节数组</param>
|
||||
/// <param name="segment">分割符</param>
|
||||
/// <param name="newLineCount">指定在何处换行,设为0则不换行</param>
|
||||
/// <returns>返回的字符串</returns>
|
||||
public static string ByteToHexString(byte[] InBytes, char segment = default) => ByteToHexString(InBytes, segment, 0);
|
||||
public static string ByteToHexString(ReadOnlySpan<byte> InBytes, char segment = default, int newLineCount = 0) => ByteToHexString(InBytes, 0, InBytes.Length, segment, newLineCount);
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为十六进制表示的字符串
|
||||
@@ -34,60 +35,30 @@ public static class DataTransUtil
|
||||
/// <param name="segment">用于分隔每个字节的字符</param>
|
||||
/// <param name="newLineCount">指定在何处换行,设为0则不换行</param>
|
||||
/// <returns>转换后的十六进制字符串</returns>
|
||||
public static string ByteToHexString(byte[] inBytes, int offset, int length, char segment = default, int newLineCount = 0)
|
||||
public static string ByteToHexString(ReadOnlySpan<byte> inBytes, int offset, int length, char segment = default, int newLineCount = 0)
|
||||
{
|
||||
if (inBytes == null || inBytes.Length == 0)
|
||||
// 合法性检查:避免越界和空输入
|
||||
if (inBytes.IsEmpty || offset < 0 || length <= 0 || offset + length > inBytes.Length)
|
||||
return string.Empty;
|
||||
// 每个字节占 2 个字符,如果有分隔符 +1,再加换行(估算上限)
|
||||
int estimatedSize = length * (segment != default ? 3 : 2) + (length / Math.Max(newLineCount, int.MaxValue));
|
||||
StringBuilder sb = new StringBuilder(estimatedSize);
|
||||
|
||||
StringBuilder stringBuilder = new();
|
||||
var len = length + offset;
|
||||
for (int i = offset; i < len; i++)
|
||||
int end = offset + length;
|
||||
for (int i = offset; i < end; i++)
|
||||
{
|
||||
// 将字节转换为两位十六进制数并追加到字符串构建器中
|
||||
stringBuilder.Append(inBytes[i].ToString("X2"));
|
||||
sb.Append(inBytes[i].ToString("X2")); // 转大写16进制
|
||||
|
||||
// 如果设置了分隔符并且不是最后一个字节,则添加分隔符
|
||||
if (segment != char.MinValue && i < len - 1)
|
||||
stringBuilder.Append(segment);
|
||||
if (segment != default && i < end - 1)
|
||||
sb.Append(segment);
|
||||
|
||||
// 如果设置了换行数且当前位置满足换行条件,则换行
|
||||
if (newLineCount > 0 && (i + 1) % newLineCount == 0)
|
||||
stringBuilder.AppendLine();
|
||||
if (newLineCount > 0 && (i - offset + 1) % newLineCount == 0)
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为十六进制表示的字符串
|
||||
/// </summary>
|
||||
/// <param name="inBytes">输入的字节数组</param>
|
||||
/// <param name="segment">用于分隔每个字节的字符</param>
|
||||
/// <param name="newLineCount">指定在何处换行,设为0则不换行</param>
|
||||
/// <returns>转换后的十六进制字符串</returns>
|
||||
public static string ByteToHexString(ReadOnlySpan<byte> inBytes, char segment = default, int newLineCount = 0)
|
||||
{
|
||||
if (inBytes.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
StringBuilder stringBuilder = new();
|
||||
var len = inBytes.Length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
// 将字节转换为两位十六进制数并追加到字符串构建器中
|
||||
stringBuilder.Append(inBytes[i].ToString("X2"));
|
||||
|
||||
// 如果设置了分隔符并且不是最后一个字节,则添加分隔符
|
||||
if (segment != char.MinValue && i < len - 1)
|
||||
stringBuilder.Append(segment);
|
||||
|
||||
// 如果设置了换行数且当前位置满足换行条件,则换行
|
||||
if (newLineCount > 0 && (i + 1) % newLineCount == 0)
|
||||
stringBuilder.AppendLine();
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Bcd值
|
||||
@@ -286,33 +257,31 @@ public static class DataTransUtil
|
||||
/// <returns>转换后的字节数组</returns>
|
||||
public static byte[] GetBytesFromBCD(string value, BcdFormatEnum format)
|
||||
{
|
||||
// 如果输入字符串为空,则返回空字节数组
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
// 计算输出字节数组的长度
|
||||
int length = value.Length % 2 == 1 ? (value.Length / 2) + 1 : value.Length / 2;
|
||||
int length = (value.Length + 1) / 2;
|
||||
byte[] bytesFromBcd = new byte[length];
|
||||
|
||||
// 遍历输入的Bcd值字符串进行转换
|
||||
for (int index = 0; index < length; ++index)
|
||||
{
|
||||
// 将每两个字符转换为一个字节
|
||||
bytesFromBcd[index] = (byte)(bytesFromBcd[index] | ((uint)GetBcdCodeFromChar(value[2 * index], format) << 4));
|
||||
byte highNibble = (byte)(GetBcdCodeFromChar(value[2 * index], format) << 4);
|
||||
byte lowNibble = 0;
|
||||
|
||||
// 如果还有下一个字符,则将其转换为字节的低四位
|
||||
if ((2 * index) + 1 < value.Length)
|
||||
{
|
||||
bytesFromBcd[index] = (byte)(bytesFromBcd[index] | (uint)GetBcdCodeFromChar(value[(2 * index) + 1], format));
|
||||
lowNibble = GetBcdCodeFromChar(value[(2 * index) + 1], format);
|
||||
}
|
||||
|
||||
bytesFromBcd[index] = (byte)(highNibble | lowNibble);
|
||||
}
|
||||
|
||||
// 返回转换后的字节数组
|
||||
return bytesFromBcd;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 返回bit代表的数据
|
||||
/// </summary>
|
||||
@@ -368,26 +337,35 @@ public static class DataTransUtil
|
||||
/// </summary>
|
||||
/// <param name="hex">输入的十六进制字符串</param>
|
||||
/// <returns>转换后的字节数组</returns>
|
||||
public static byte[] HexStringToBytes(string hex)
|
||||
public static Memory<byte> HexStringToBytes(string hex)
|
||||
{
|
||||
// 创建内存流用于存储转换后的字节数据
|
||||
using var bytes = new MemoryStream();
|
||||
if (string.IsNullOrEmpty(hex))
|
||||
return Memory<byte>.Empty;
|
||||
|
||||
// 遍历十六进制字符串进行转换
|
||||
for (int index = 0; index < hex.Length; ++index)
|
||||
int len = hex.Length / 2;
|
||||
var result = new byte[len];
|
||||
int byteIndex = 0;
|
||||
|
||||
for (int i = 0; i < hex.Length - 1; i += 2)
|
||||
{
|
||||
// 检查当前字符和下一个字符是否都在合法的十六进制范围内
|
||||
if (index + 1 < hex.Length && GetHexCharIndex(hex[index]) <= 0x0F && GetHexCharIndex(hex[index + 1]) <= 0x0F)
|
||||
int hi = GetHexCharIndex(hex[i]);
|
||||
int lo = GetHexCharIndex(hex[i + 1]);
|
||||
|
||||
if (hi >= 0 && lo >= 0 && hi < 0x10 && lo < 0x10)
|
||||
{
|
||||
// 计算两个十六进制字符对应的字节值并写入内存流
|
||||
bytes.WriteByte((byte)(GetHexCharIndex(hex[index]) * 16 + GetHexCharIndex(hex[index + 1])));
|
||||
++index; // 跳过下一个字符,因为已经处理过了
|
||||
result[byteIndex++] = (byte)((hi << 4) | lo);
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes.ToArray();
|
||||
return new Memory<byte>(result, 0, byteIndex);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 拼接任意个泛型数组为一个总的泛型数组对象。
|
||||
/// </summary>
|
||||
@@ -396,21 +374,75 @@ public static class DataTransUtil
|
||||
/// <returns>拼接之后的最终的结果对象</returns>
|
||||
public static T[] SpliceArray<T>(params T[][] arrays)
|
||||
{
|
||||
// 初始化一个动态数组用于存储拼接后的结果
|
||||
List<T> resultList = new List<T>();
|
||||
if (arrays == null || arrays.Length == 0)
|
||||
return Array.Empty<T>();
|
||||
|
||||
// 遍历输入的数组
|
||||
foreach (T[] array in arrays)
|
||||
// 预先计算所有数组的总长度,避免多次扩容
|
||||
int totalLength = 0;
|
||||
foreach (var array in arrays)
|
||||
{
|
||||
// 如果数组不为空且长度不为0,则将其元素添加到结果数组中
|
||||
if (array != null && array.Length != 0)
|
||||
{
|
||||
resultList.AddRange(array);
|
||||
}
|
||||
if (array != null)
|
||||
totalLength += array.Length;
|
||||
}
|
||||
|
||||
// 将动态数组转换为静态数组并返回
|
||||
return resultList.ToArray();
|
||||
if (totalLength == 0)
|
||||
return Array.Empty<T>();
|
||||
|
||||
// 分配目标数组
|
||||
T[] result = new T[totalLength];
|
||||
int offset = 0;
|
||||
|
||||
// 拷贝所有数组到目标数组中
|
||||
foreach (var array in arrays)
|
||||
{
|
||||
if (array == null || array.Length == 0)
|
||||
continue;
|
||||
|
||||
Array.Copy(array, 0, result, offset, array.Length);
|
||||
offset += array.Length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 拼接任意个泛型数组为一个总的泛型数组对象。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">数组的类型信息</typeparam>
|
||||
/// <param name="arrays">任意个长度的数组</param>
|
||||
/// <returns>拼接之后的最终的结果对象</returns>
|
||||
public static Memory<T> SpliceArray<T>(params Memory<T>[] arrays)
|
||||
{
|
||||
if (arrays == null || arrays.Length == 0)
|
||||
return Array.Empty<T>();
|
||||
|
||||
// 预先计算所有数组的总长度,避免多次扩容
|
||||
int totalLength = 0;
|
||||
foreach (var array in arrays)
|
||||
{
|
||||
if (!array.IsEmpty)
|
||||
totalLength += array.Length;
|
||||
}
|
||||
|
||||
if (totalLength == 0)
|
||||
return Array.Empty<T>();
|
||||
|
||||
// 分配目标数组
|
||||
Memory<T> result = new T[totalLength];
|
||||
int offset = 0;
|
||||
|
||||
// 拷贝所有数组到目标数组中
|
||||
foreach (var array in arrays)
|
||||
{
|
||||
if (array.IsEmpty)
|
||||
continue;
|
||||
|
||||
array.CopyTo(result.Slice(offset, array.Length));
|
||||
offset += array.Length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
@@ -65,31 +65,31 @@ public static class JTokenUtil
|
||||
var array = (JArray)token;
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.Integer))
|
||||
return array.Select(x => x.Value<long>()).ToList();
|
||||
return array.Select(x => x.Value<long>()).ToArray();
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.Float))
|
||||
return array.Select(x => x.Value<double>()).ToList();
|
||||
return array.Select(x => x.Value<double>()).ToArray();
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.String))
|
||||
return array.Select(x => x.Value<string>()).ToList();
|
||||
return array.Select(x => x.Value<string>()).ToArray();
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.Boolean))
|
||||
return array.Select(x => x.Value<bool>()).ToList();
|
||||
return array.Select(x => x.Value<bool>()).ToArray();
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.Date))
|
||||
return array.Select(x => x.Value<DateTime>()).ToList();
|
||||
return array.Select(x => x.Value<DateTime>()).ToArray();
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.TimeSpan))
|
||||
return array.Select(x => x.Value<TimeSpan>()).ToList();
|
||||
return array.Select(x => x.Value<TimeSpan>()).ToArray();
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.Guid))
|
||||
return array.Select(x => x.Value<Guid>()).ToList();
|
||||
return array.Select(x => x.Value<Guid>()).ToArray();
|
||||
|
||||
if (array.All(x => x.Type == JTokenType.Uri))
|
||||
return array.Select(x => x.Value<Uri>()).ToList();
|
||||
return array.Select(x => x.Value<Uri>()).ToArray();
|
||||
|
||||
// 否则递归
|
||||
return array.Select(x => GetObjectFromJToken(x)).ToList();
|
||||
return array.Select(x => GetObjectFromJToken(x)).ToArray();
|
||||
|
||||
case JTokenType.Integer:
|
||||
return token.ToObject<long>();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
|
@@ -52,7 +52,7 @@ public class AsyncReadWriteLock
|
||||
{
|
||||
var resetEvent = _readerLock;
|
||||
_readerLock = new(false);
|
||||
resetEvent.SafeDispose();
|
||||
resetEvent.SetAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -104,6 +104,8 @@ public abstract class BusinessBase : DriverBase
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
LogMessage?.LogInformation("Get business tasks");
|
||||
|
||||
var setDeviceStatusTask = new ScheduledSyncTask(3000, SetDeviceStatus, null, LogMessage, cancellationToken);
|
||||
|
||||
var executeTask = ScheduledTaskHelper.GetTask(CurrentDevice.IntervalTime, ProtectedExecuteAsync, null, LogMessage, cancellationToken);
|
||||
|
@@ -98,7 +98,7 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage?.LogInformation($"Add {typeof(DeviceBasicData).Name} data to file cache, count {data.Count}");
|
||||
LogMessage?.LogInformation($"Add {typeof(AlarmVariable).Name} data to file cache, count {data.Count}");
|
||||
foreach (var item in data)
|
||||
{
|
||||
item.Id = CommonUtils.GetSingleId();
|
||||
|
@@ -26,7 +26,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected abstract BusinessPropertyWithCacheIntervalScript _businessPropertyWithCacheIntervalScript { get; }
|
||||
|
||||
public virtual List<string> Match(string input)
|
||||
public virtual string[] Match(string input)
|
||||
{
|
||||
// 生成缓存键,以确保缓存的唯一性
|
||||
var cacheKey = $"{nameof(BusinessBaseWithCacheIntervalScript)}-{CultureInfo.CurrentUICulture.Name}-Match-{input}";
|
||||
@@ -51,7 +51,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
}
|
||||
|
||||
// 返回匹配结果列表
|
||||
return strings;
|
||||
return strings.ToArray();
|
||||
});
|
||||
|
||||
// 返回匹配结果列表
|
||||
@@ -64,7 +64,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<AlarmVariable>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
|
||||
if (topics.Count > 0)
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
@@ -77,7 +77,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
string topic = _businessPropertyWithCacheIntervalScript.AlarmTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
for (int i = 0; i < topics.Length; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
@@ -126,7 +126,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<DeviceBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
|
||||
if (topics.Count > 0)
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
@@ -140,7 +140,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
string topic = _businessPropertyWithCacheIntervalScript.DeviceTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
for (int i = 0; i < topics.Length; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
@@ -191,7 +191,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<VariableBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Count > 0)
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
@@ -205,7 +205,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
string topic = _businessPropertyWithCacheIntervalScript.VariableTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
for (int i = 0; i < topics.Length; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
@@ -268,7 +268,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
}
|
||||
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Count > 0)
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
@@ -282,7 +282,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
string topic = _businessPropertyWithCacheIntervalScript.VariableTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
for (int i = 0; i < topics.Length; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
@@ -25,6 +25,6 @@ public class CacheDBItem<T> : IPrimaryIdEntity
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
|
||||
[SugarColumn(IsJson = true)]
|
||||
[SugarColumn(IsJson = true, ColumnDataType = "TEXT")]
|
||||
public T Value { get; set; }
|
||||
}
|
||||
|
@@ -207,6 +207,8 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
||||
{
|
||||
var tasks = new List<IScheduledTask>();
|
||||
|
||||
LogMessage?.LogInformation("Get collect tasks");
|
||||
|
||||
var setDeviceStatusTask = new ScheduledSyncTask(10000, SetDeviceStatus, null, LogMessage, cancellationToken);
|
||||
tasks.Add(setDeviceStatusTask);
|
||||
|
||||
@@ -398,7 +400,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
||||
{
|
||||
// 读取成功时记录日志并增加成功计数器
|
||||
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content?.ToHexString(' ')));
|
||||
LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content.Span.ToHexString(' ')));
|
||||
CurrentDevice.SetDeviceStatus(TimerX.Now, null);
|
||||
}
|
||||
else
|
||||
@@ -477,10 +479,11 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
||||
/// <summary>
|
||||
/// 采集驱动读取,读取成功后直接赋值变量
|
||||
/// </summary>
|
||||
protected virtual ValueTask<OperResult<byte[]>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
protected virtual ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult(new OperResult<byte[]>(new NotImplementedException()));
|
||||
return ValueTask.FromResult(new OperResult<ReadOnlyMemory<byte>>(new NotImplementedException()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量写入变量值,需返回变量名称/结果
|
||||
/// </summary>
|
||||
|
@@ -141,23 +141,32 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
/// <summary>
|
||||
/// 采集驱动读取,读取成功后直接赋值变量,失败不做处理,注意非通用设备需重写
|
||||
/// </summary>
|
||||
protected override async ValueTask<OperResult<byte[]>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
protected override async ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return new(new OperationCanceledException());
|
||||
|
||||
// 从协议读取数据
|
||||
var read = await FoundationDevice.ReadAsync(variableSourceRead.AddressObject, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// 如果读取成功且有有效内容,则解析结构化内容
|
||||
if (read.IsSuccess)
|
||||
try
|
||||
{
|
||||
var prase = variableSourceRead.VariableRuntimes.PraseStructContent(FoundationDevice, read.Content, false);
|
||||
return new OperResult<byte[]>(prase);
|
||||
}
|
||||
|
||||
// 返回读取结果
|
||||
return read;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return new(new OperationCanceledException());
|
||||
|
||||
// 从协议读取数据
|
||||
var read = await FoundationDevice.ReadAsync(variableSourceRead.AddressObject, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// 如果读取成功且有有效内容,则解析结构化内容
|
||||
if (read.IsSuccess)
|
||||
{
|
||||
var prase = variableSourceRead.VariableRuntimes.PraseStructContent(FoundationDevice, read.Content.Span, false);
|
||||
return new OperResult<ReadOnlyMemory<byte>>(prase);
|
||||
}
|
||||
|
||||
// 返回读取结果
|
||||
return read;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 捕获异常并返回失败结果
|
||||
return new OperResult<ReadOnlyMemory<byte>>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -183,7 +192,7 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
LogMessage?.Debug(string.Format("{0} - Writing [{1} - {2} - {3}]", DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType));
|
||||
|
||||
// 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果
|
||||
var result = await FoundationDevice.WriteAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false);
|
||||
var result = await FoundationDevice.WriteJTokenAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
|
@@ -289,14 +289,14 @@ public abstract class DriverBase : DisposableObject, IDriver
|
||||
}
|
||||
}
|
||||
|
||||
protected internal TaskSchedulerLoop TaskSchedulerLoop;
|
||||
protected internal TaskSchedulerLoop TaskSchedulerLoop { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取任务
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消操作的令牌。</param>
|
||||
/// <returns>表示异步操作结果的枚举。</returns>
|
||||
internal virtual TaskSchedulerLoop GetTasks(CancellationToken cancellationToken)
|
||||
internal virtual void GetTasks(CancellationToken cancellationToken)
|
||||
{
|
||||
TaskSchedulerLoop = new(ProtectedGetTasks(cancellationToken));
|
||||
|
||||
@@ -309,7 +309,6 @@ public abstract class DriverBase : DisposableObject, IDriver
|
||||
// GlobalData.GatewayMonitorHostedService.Logger.LogInformation($"set min threads count {wt}, device tasks count {count}");
|
||||
//}
|
||||
|
||||
return TaskSchedulerLoop;
|
||||
}
|
||||
|
||||
protected abstract List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken);
|
||||
@@ -345,7 +344,15 @@ public abstract class DriverBase : DisposableObject, IDriver
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
TaskSchedulerLoop?.Stop();
|
||||
base.Dispose(disposing);
|
||||
if (TaskSchedulerLoop != null)
|
||||
{
|
||||
lock (TaskSchedulerLoop)
|
||||
{
|
||||
TaskSchedulerLoop.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
TextLogger?.Dispose();
|
||||
_logger?.TryDispose();
|
||||
IdVariableRuntimes?.Clear();
|
||||
@@ -359,7 +366,6 @@ public abstract class DriverBase : DisposableObject, IDriver
|
||||
pluginPropertyEditorItems?.Clear();
|
||||
pluginPropertyEditorItems = null;
|
||||
DeviceThreadManage = null;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
#endregion 插件生命周期
|
||||
|
||||
|
@@ -79,10 +79,10 @@ public static class DynamicModelExtension
|
||||
return null; // 未找到对应的业务设备Id,返回null
|
||||
}
|
||||
|
||||
public static IEnumerable<IGrouping<object[], T>> GroupByKeys<T>(this IEnumerable<T> values, IEnumerable<string> keys)
|
||||
public static IEnumerable<IGrouping<object[], T>> GroupByKeys<T>(this IEnumerable<T> values, params string[] keys)
|
||||
{
|
||||
// 获取动态对象集合中指定键的属性信息
|
||||
var properties = GetProperties(values, keys.ToArray());
|
||||
var properties = GetProperties(values, keys);
|
||||
|
||||
// 使用对象数组作为键进行分组
|
||||
return values.GroupBy(v => properties.Select(property => property.GetValue(v)).ToArray(), new ArrayEqualityComparer());
|
||||
|
@@ -68,6 +68,12 @@ public class DeviceBasicData
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark5 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="BaseDataEntity.CreateOrgId"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public long CreateOrgId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,7 +83,6 @@ public class VariableBasicData
|
||||
{
|
||||
/// <inheritdoc cref="PrimaryIdEntity.Id"/>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.Name"/>
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -177,4 +182,10 @@ public class VariableBasicData
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark5 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="BaseDataEntity.CreateOrgId"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public long CreateOrgId { get; set; }
|
||||
|
||||
}
|
||||
|
@@ -320,16 +320,18 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
|
||||
driver.IsInitSuccess = false;
|
||||
LogMessage?.LogWarning(ex, string.Format(AppResource.InitFail, CurrentChannel.PluginName, driver?.DeviceName));
|
||||
}
|
||||
|
||||
if (CancellationTokenSources.TryGetValue(driver.DeviceId, out var oldCts))
|
||||
if (driver?.DeviceId > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
await oldCts.CancelAsync().ConfigureAwait(false);
|
||||
oldCts.SafeDispose();
|
||||
}
|
||||
catch
|
||||
if (CancellationTokenSources.TryGetValue(driver.DeviceId, out var oldCts))
|
||||
{
|
||||
try
|
||||
{
|
||||
await oldCts.CancelAsync().ConfigureAwait(false);
|
||||
oldCts.SafeDispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,12 +493,16 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
|
||||
if (!driver.IsStarted)
|
||||
await driver.StartAsync(token).ConfigureAwait(false);
|
||||
|
||||
var driverTask = driver.GetTasks(token); // 执行驱动的异步执行操作
|
||||
driver.GetTasks(token); // 执行驱动的异步执行操作
|
||||
|
||||
DriverTasks.TryAdd(driver.DeviceId, driverTask);
|
||||
DriverTasks.TryAdd(driver.DeviceId, driver.TaskSchedulerLoop);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
driverTask.Start();
|
||||
lock (driver.TaskSchedulerLoop)
|
||||
{
|
||||
if (!driver.DisposedValue && !token.IsCancellationRequested)
|
||||
driver.TaskSchedulerLoop.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user