diff --git a/src/Admin/README.md b/src/Admin/README.md index dd1f01fdc..55e51fb36 100644 --- a/src/Admin/README.md +++ b/src/Admin/README.md @@ -1,5 +1,5 @@ -

ThingsBlazor

+

ThingsBlazor

权限管理框架

diff --git a/src/Admin/ThingsGateway.Common/Options/WebsiteOptions.cs b/src/Admin/ThingsGateway.Common/Options/WebsiteOptions.cs index f710ad0c5..6580c1694 100644 --- a/src/Admin/ThingsGateway.Common/Options/WebsiteOptions.cs +++ b/src/Admin/ThingsGateway.Common/Options/WebsiteOptions.cs @@ -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; + /// /// 是否显示关于页面 /// diff --git a/src/Admin/ThingsGateway.DB/Services/SugarAopService/SugarAopService.cs b/src/Admin/ThingsGateway.DB/Services/SugarAopService/SugarAopService.cs index e773a6e2a..b60dd8b5d 100644 --- a/src/Admin/ThingsGateway.DB/Services/SugarAopService/SugarAopService.cs +++ b/src/Admin/ThingsGateway.DB/Services/SugarAopService/SugarAopService.cs @@ -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)); diff --git a/src/Admin/ThingsGateway.Furion/ThingsGateway.Furion.csproj b/src/Admin/ThingsGateway.Furion/ThingsGateway.Furion.csproj index f18235fd8..92227468e 100644 --- a/src/Admin/ThingsGateway.Furion/ThingsGateway.Furion.csproj +++ b/src/Admin/ThingsGateway.Furion/ThingsGateway.Furion.csproj @@ -31,6 +31,7 @@ + @@ -43,6 +44,7 @@ + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 545564233..4b1848922 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,11 +1,11 @@ - 10.9.70 - 10.9.70 - 10.9.70 - 2.9.28 - 10.9.25 + 10.9.91 + 10.9.91 + 10.9.91 + 2.9.29 + 10.9.29 8.0.18 9.0.7 zh-Hans;en-US diff --git a/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponent.razor.cs b/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponent.razor.cs index f9055f663..67c638384 100644 --- a/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponent.razor.cs +++ b/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponent.razor.cs @@ -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; diff --git a/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponentBase.cs b/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponentBase.cs index 5986cfb18..2bdf6f72b 100644 --- a/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponentBase.cs +++ b/src/Foundation/ThingsGateway.Foundation.Razor/DebugPages/DeviceComponentBase.cs @@ -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"]}"); diff --git a/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs b/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs index c71126ee0..768a74c73 100644 --- a/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs +++ b/src/Foundation/ThingsGateway.Foundation.Variable/VariableObject.cs @@ -83,7 +83,7 @@ public abstract class VariableObject /// GetBytes /// /// - public virtual byte[] GetBytes(Expression> accessor) + public virtual ReadOnlyMemory GetBytes(Expression> 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); } /// @@ -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) diff --git a/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs b/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs index 3e6d25fac..3efc1a448 100644 --- a/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs +++ b/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptions.cs b/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptions.cs index 3a420c341..ea978a8ee 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptionsBase.cs b/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptionsBase.cs index ce22deab9..4cecb3a41 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptionsBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/ChannelOptionsBase.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPMessage.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPMessage.cs index 35198e48a..687c9b9fe 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPMessage.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPMessage.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -30,8 +30,8 @@ public abstract class DDPMessage : MessageBase, IResultMessage public override bool CheckHead(ref TByteBlock byteBlock) { - var code = byteBlock.ReadByte(); - Type = byteBlock.ReadByte(); + var code = ReaderExtension.ReadValue(ref byteBlock); + Type = ReaderExtension.ReadValue(ref byteBlock); if (code != 0x7B) { @@ -44,15 +44,15 @@ public abstract class DDPMessage : MessageBase, IResultMessage } } - public abstract int GetBodyLength(ref TByteBlock byteBlock) where TByteBlock : IByteBlock; - public abstract byte[] GetContent(ref TByteBlock byteBlock) where TByteBlock : IByteBlock; + public abstract int GetBodyLength(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader; + public abstract byte[] GetContent(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader; } public class DDPTcpMessage : DDPMessage { public override int GetBodyLength(ref TByteBlock byteBlock) { - return byteBlock.ReadUInt16(EndianType.Big) - 4; + return ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) - 4; } public override byte[] GetContent(ref TByteBlock byteBlock) diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPSend.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPSend.cs index 86dec4df3..e9031e80a 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPSend.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPSend.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -10,6 +10,8 @@ using System.Text; +using ThingsGateway.NewLife.Extension; + namespace ThingsGateway.Foundation; /// @@ -30,40 +32,37 @@ public class DDPSend : ISendMessage Id = id; Command = command; } - public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlock + public void Build(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; } } diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs index 8879e4907..bceca25f8 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -42,17 +42,17 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel DataHandlingAdapter.SendAsyncCallBack = DefaultSendAsync; return base.OnTcpConnected(e); } - protected Task DefaultSendAsync(ReadOnlyMemory memory) + protected Task DefaultSendAsync(ReadOnlyMemory memory, CancellationToken cancellationToken) { - return DDPAdapter.SendInputAsync(new DDPSend(memory, Id, true)); + return DDPAdapter.SendInputAsync(new DDPSend(memory, Id, true), cancellationToken); } - protected Task DDPSendAsync(ReadOnlyMemory memory) + protected Task DDPSendAsync(ReadOnlyMemory 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 DDPAdapter = new(); private WaitLock _waitLock = new(nameof(DDPTcpSessionClientChannel)); - protected override async ValueTask OnTcpReceiving(ByteBlock byteBlock) + + protected override async ValueTask 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.Empty, id, true, 0x81)).ConfigureAwait(false); + await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory.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.Empty, Id, true, 0x82)).ConfigureAwait(false); + await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory.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); diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs index cbbfa199a..de60b15b7 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -49,11 +49,12 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe DataHandlingAdapter.SendCallBackAsync = DefaultSendAsync; } - protected Task DefaultSendAsync(EndPoint endPoint, ReadOnlyMemory memory) + + protected Task DefaultSendAsync(EndPoint endPoint, ReadOnlyMemory 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 memory) + protected Task DDPSendAsync(EndPoint endPoint, ReadOnlyMemory memory, CancellationToken token) { //获取endpoint - return base.ProtectedDefaultSendAsync(endPoint, memory); + return base.ProtectedDefaultSendAsync(endPoint, memory, token); } private ConcurrentDictionary 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.Empty, id, false, 0x81)).ConfigureAwait(false); + await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory.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.Empty, id, false, 0x82)).ConfigureAwait(false); + await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory.Empty, id, false, 0x82), ClosedToken).ConfigureAwait(false); Logger?.Info(string.Format(AppResource.DtuDisconnecting, id)); await Task.Delay(100).ConfigureAwait(false); IdDict.TryRemove(endPoint, out _); diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DtuSeviceType.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DtuSeviceType.cs index 9ea4aba0c..d30ef9c4c 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DtuSeviceType.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DtuSeviceType.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs index 996855d64..75d50956a 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Extension/ChannelOptionsExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/IChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/IChannel.cs index 7cfa212a8..5c1543939 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/IChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/IChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -37,10 +37,7 @@ public interface IChannel : ISetupConfigObject, IDisposable, IClosableClient, IC /// public ConcurrentList Collects { get; } - /// - /// MaxSign - /// - int MaxSign { get; set; } + /// /// 通道启动成功后 @@ -66,6 +63,11 @@ public interface IChannel : ISetupConfigObject, IDisposable, IClosableClient, IC /// 主动请求时的等待池 /// public ConcurrentDictionary> ChannelReceivedWaitDict { get; } + + void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue); + + + } /// diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/IChannelOptions.cs b/src/Foundation/ThingsGateway.Foundation/Channel/IChannelOptions.cs index 329155efe..e0209c982 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/IChannelOptions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/IChannelOptions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/IClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/IClientChannel.cs index 10e163310..304dd272f 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/IClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/IClientChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/IDtuUdpSessionChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/IDtuUdpSessionChannel.cs index 996c3c9ad..d183170d6 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/IDtuUdpSessionChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/IDtuUdpSessionChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/ITcpServiceChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/ITcpServiceChannel.cs index 75b292050..0e00165b5 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/ITcpServiceChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/ITcpServiceChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs index 9af79995c..33b309d6d 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -12,6 +12,8 @@ using System.Collections.Concurrent; using ThingsGateway.NewLife; +using TouchSocket.SerialPorts; + namespace ThingsGateway.Foundation; /// @@ -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; - /// - 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(minSign, maxSign); + pool?.CancelAll(); + pool?.SafeDispose(); + } /// public ChannelReceivedEventHandler ChannelReceived { get; set; } = new(); @@ -60,7 +64,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel /// /// 等待池 /// - public WaitHandlePool WaitHandlePool { get; } = new(); + public WaitHandlePool WaitHandlePool { get; internal set; } = new(); /// 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 /// 异步发送数据,保护方法。 /// /// 待发送的字节数据内存。 + /// cancellationToken /// 异步任务。 - protected Task ProtectedDefaultSendAsync(ReadOnlyMemory memory) + protected Task ProtectedDefaultSendAsync(ReadOnlyMemory 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 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> 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(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 memory) + public Task SendAsync(ReadOnlyMemory 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() { diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs index 6d38bd69b..b2554d580 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/DtuPlugin.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -31,13 +31,13 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin { _heartbeat = value; if (!heartbeatHex) - HeartbeatByte = new ArraySegment(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); + HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); else - HeartbeatByte = new ArraySegment(_heartbeat?.HexStringToBytes() ?? Array.Empty()); + HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty()); } } private string _heartbeat; - private ArraySegment HeartbeatByte = new(); + private Memory HeartbeatByte = new(); private bool heartbeatHex; @@ -51,9 +51,9 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin { heartbeatHex = value; if (!heartbeatHex) - HeartbeatByte = new ArraySegment(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); + HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); else - HeartbeatByte = new ArraySegment(_heartbeat?.HexStringToBytes() ?? Array.Empty()); + HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty()); } } public bool DtuIdHex { get; set; } @@ -61,7 +61,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin /// 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"); diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs index f1c96f211..79de2a901 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/HeartbeatAndReceivePlugin.cs @@ -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(Encoding.UTF8.GetBytes(_dtuId ?? string.Empty)); + DtuIdByte = (Encoding.UTF8.GetBytes(_dtuId ?? string.Empty)); else - DtuIdByte = new ArraySegment(_dtuId?.HexStringToBytes() ?? Array.Empty()); + DtuIdByte = (_dtuId?.HexStringToBytes() ?? Array.Empty()); } } private string _dtuId; - private ArraySegment DtuIdByte; + private Memory DtuIdByte; /// /// 心跳字符串 @@ -48,13 +48,13 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi { _heartbeat = value; if (!heartbeatHex) - HeartbeatByte = new ArraySegment(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); + HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); else - HeartbeatByte = new ArraySegment(_heartbeat?.HexStringToBytes() ?? Array.Empty()); + HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty()); } } private string _heartbeat; - private ArraySegment HeartbeatByte; + private Memory HeartbeatByte; @@ -73,12 +73,12 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi heartbeatHex = value; if (!heartbeatHex) { - HeartbeatByte = new ArraySegment(Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); + HeartbeatByte = (Encoding.UTF8.GetBytes(_heartbeat ?? string.Empty)); } else { - HeartbeatByte = new ArraySegment(_heartbeat?.HexStringToBytes() ?? Array.Empty()); + HeartbeatByte = (_heartbeat?.HexStringToBytes() ?? Array.Empty()); } } @@ -95,12 +95,12 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi dtuIdHex = value; if (!dtuIdHex) { - DtuIdByte = new ArraySegment(Encoding.UTF8.GetBytes(_dtuId ?? string.Empty)); + DtuIdByte = (Encoding.UTF8.GetBytes(_dtuId ?? string.Empty)); } else { - DtuIdByte = new ArraySegment(_dtuId?.HexStringToBytes() ?? Array.Empty()); + DtuIdByte = (_dtuId?.HexStringToBytes() ?? Array.Empty()); } } @@ -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; diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/IDtu.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/IDtu.cs index 54a1399d0..dbf5cc3fe 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/IDtu.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/IDtu.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/PluginUtil.cs b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/PluginUtil.cs index f8325b213..3b869e868 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/PluginUtil.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/Plugin/PluginUtil.cs @@ -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); }); }; diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs index a234bc9b7..b1fd058e1 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/SerialPortChannel.cs @@ -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; - /// - 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(minSign, maxSign); + pool?.CancelAll(); + pool?.SafeDispose(); + } /// public ChannelReceivedEventHandler ChannelReceived { get; set; } = new(); @@ -62,7 +64,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel /// /// 等待池 /// - public WaitHandlePool WaitHandlePool { get; } = new(); + public WaitHandlePool WaitHandlePool { get; internal set; } = new(); /// 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 } /// - 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; } /// 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 } /// - protected override void Dispose(bool disposing) + protected override void SafetyDispose(bool disposing) { WaitHandlePool.SafeDispose(); - base.Dispose(disposing); + base.SafetyDispose(disposing); } } diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs index 00fd6e396..c4e0985fd 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/TcpClientChannel.cs @@ -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(minSign, maxSign); + pool?.CancelAll(); + pool?.SafeDispose(); + } /// public ChannelReceivedEventHandler ChannelReceived { get; } = new(); @@ -57,7 +62,7 @@ public class TcpClientChannel : TcpClient, IClientChannel /// /// 等待池 /// - public WaitHandlePool WaitHandlePool { get; } = new(); + public WaitHandlePool WaitHandlePool { get; internal set; } = new(); public virtual WaitLock GetLock(string key) => WaitLock; /// @@ -181,9 +186,9 @@ public class TcpClientChannel : TcpClient, IClientChannel } /// - protected override void Dispose(bool disposing) + protected override void SafetyDispose(bool disposing) { WaitHandlePool.SafeDispose(); - base.Dispose(disposing); + base.SafetyDispose(disposing); } } diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/TcpServiceChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/TcpServiceChannel.cs index 7be512e6c..e37b624ab 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/TcpServiceChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/TcpServiceChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -23,10 +23,10 @@ public abstract class TcpServiceChannelBase : TcpService, ITcp /// public ConcurrentList Collects { get; } = new(); - /// - /// 停止时是否发送ShutDown - /// - public bool ShutDownEnable { get; set; } = true; + ///// + ///// 停止时是否发送ShutDown + ///// + //public bool ShutDownEnable { get; set; } = true; /// public override async Task ClearAsync() @@ -35,8 +35,8 @@ public abstract class TcpServiceChannelBase : TcpService, 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 : TcpService, 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 : TcpService, ITcp } finally { + var cts = m_transport; + m_transport = new(); + cts?.SafeCancel(); + cts?.SafeDispose(); _connectLock.Release(); } } @@ -110,6 +114,10 @@ public abstract class TcpServiceChannelBase : TcpService, ITcp } finally { + var cts = m_transport; + m_transport = null; + cts?.SafeCancel(); + cts?.SafeDispose(); _connectLock.Release(); } } @@ -121,6 +129,14 @@ public abstract class TcpServiceChannelBase : TcpService, 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); + } /// protected override Task OnTcpClosed(TClient socketClient, ClosedEventArgs e) { @@ -208,10 +224,16 @@ public class TcpServiceChannel : TcpServiceChannelBase, 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; + } /// protected override async Task OnTcpClosing(TClient socketClient, ClosingEventArgs e) { @@ -264,6 +286,7 @@ public class TcpServiceChannel : TcpServiceChannelBase, IChann IEnumerable ITcpServiceChannel.Clients => base.Clients; + protected override void ClientInitialized(TClient client) { client.ChannelOptions = ChannelOptions; diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs index 8c92a29b6..d2ce525be 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/TcpSessionClientChannel.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -22,11 +22,15 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel /// 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(minSign, maxSign); + pool?.CancelAll(); + pool?.SafeDispose(); + } /// public ChannelReceivedEventHandler ChannelReceived { get; set; } = new(); @@ -92,10 +96,10 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel } /// - protected override void Dispose(bool disposing) + protected override void SafetyDispose(bool disposing) { WaitHandlePool.SafeDispose(); - base.Dispose(disposing); + base.SafetyDispose(disposing); } /// diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs index f0ec95178..ca51dc6a9 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/UdpSessionChannel.cs @@ -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(minSign, maxSign); + pool?.CancelAll(); + pool?.SafeDispose(); + } /// 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; /// 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 } /// - protected override void Dispose(bool disposing) + protected override void SafetyDispose(bool disposing) { + m_transport?.SafeDispose(); WaitHandlePool.SafeDispose(); - base.Dispose(disposing); + base.SafetyDispose(disposing); } } diff --git a/src/Foundation/ThingsGateway.Foundation/Converter/EncodingConverter.cs b/src/Foundation/ThingsGateway.Foundation/Converter/EncodingConverter.cs index 9b49910ef..90b79cf28 100644 --- a/src/Foundation/ThingsGateway.Foundation/Converter/EncodingConverter.cs +++ b/src/Foundation/ThingsGateway.Foundation/Converter/EncodingConverter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Converter/JsonToClassConverter.cs b/src/Foundation/ThingsGateway.Foundation/Converter/JsonToClassConverter.cs index 4cc9988e6..10cf5ce28 100644 --- a/src/Foundation/ThingsGateway.Foundation/Converter/JsonToClassConverter.cs +++ b/src/Foundation/ThingsGateway.Foundation/Converter/JsonToClassConverter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Converter/StringConverter.cs b/src/Foundation/ThingsGateway.Foundation/Converter/StringConverter.cs index b1f96bc90..2a92fe81f 100644 --- a/src/Foundation/ThingsGateway.Foundation/Converter/StringConverter.cs +++ b/src/Foundation/ThingsGateway.Foundation/Converter/StringConverter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Converter/ThingsGatewayStringConverter.cs b/src/Foundation/ThingsGateway.Foundation/Converter/ThingsGatewayStringConverter.cs index 2aacababd..f03140055 100644 --- a/src/Foundation/ThingsGateway.Foundation/Converter/ThingsGatewayStringConverter.cs +++ b/src/Foundation/ThingsGateway.Foundation/Converter/ThingsGatewayStringConverter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceSingleStreamDataHandleAdapter.cs b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceSingleStreamDataHandleAdapter.cs index 8fa5ce96b..99d705ff7 100644 --- a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceSingleStreamDataHandleAdapter.cs +++ b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceSingleStreamDataHandleAdapter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -27,9 +27,6 @@ public class DeviceSingleStreamDataHandleAdapter : TcpCustomDataHandli /// public override bool CanSendRequestInfo => true; - /// - public override bool CanSplicingSend => false; - /// /// 报文输出时采用字符串还是HexString /// @@ -171,23 +168,24 @@ public class DeviceSingleStreamDataHandleAdapter : TcpCustomDataHandli } /// - protected override async Task PreviewSendAsync(ReadOnlyMemory memory) + protected override async Task PreviewSendAsync(ReadOnlyMemory 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); } /// - 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 : TcpCustomDataHandli { SetRequest(sendMessage, ref byteBlock); } - await GoSendAsync(byteBlock.Memory).ConfigureAwait(false); + await GoSendAsync(byteBlock.Memory, cancellationToken).ConfigureAwait(false); } finally { diff --git a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceUdpDataHandleAdapter.cs b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceUdpDataHandleAdapter.cs index 55a525967..3687d9fd4 100644 --- a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceUdpDataHandleAdapter.cs +++ b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/DeviceUdpDataHandleAdapter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -21,9 +21,6 @@ public class DeviceUdpDataHandleAdapter : UdpDataHandlingAdapter where /// public override bool CanSendRequestInfo => true; - /// - public override bool CanSplicingSend => false; - /// /// 报文输出时采用字符串还是HexString /// @@ -61,7 +58,7 @@ public class DeviceUdpDataHandleAdapter : UdpDataHandlingAdapter where } /// - 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 : UdpDataHandlingAdapter where } /// - protected override async Task PreviewSendAsync(EndPoint endPoint, ReadOnlyMemory memory) + protected override async Task PreviewSendAsync(EndPoint endPoint, ReadOnlyMemory 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); } /// - 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 : UdpDataHandlingAdapter where { SetRequest(sendMessage, ref byteBlock); } - await GoSendAsync(endPoint, byteBlock.Memory).ConfigureAwait(false); + await GoSendAsync(endPoint, byteBlock.Memory, cancellationToken).ConfigureAwait(false); } finally { diff --git a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/IByteBlockWriterBuilder.cs b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/IByteBlockWriterBuilder.cs new file mode 100644 index 000000000..cd97971a6 --- /dev/null +++ b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/IByteBlockWriterBuilder.cs @@ -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 interface IByteBlockWriterBuilder +{ + /// + /// 构建数据时,指示内存池的申请长度。 + /// + /// 建议:该值可以尽可能的设置大一些,这样可以避免内存池扩容。 + /// + /// + int MaxLength { get; } + + /// + /// 构建对象到 + /// + /// 要构建的字节块对象引用。 + void Build(ref TWriter writer) where TWriter : IByteBlockWriter +#if AllowsRefStruct +,allows ref struct +#endif + ; +} + + + +/// +/// 指示应当如何构建 +/// +public interface IRequestInfoByteBlockWriterBuilder : IRequestInfo, IByteBlockWriterBuilder +{ + +} \ No newline at end of file diff --git a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/IResultMessage.cs b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/IResultMessage.cs index f1b0d66a6..8567845bf 100644 --- a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/IResultMessage.cs +++ b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/IResultMessage.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -23,7 +23,7 @@ public interface IResultMessage : IOperResult, IRequestInfo /// /// 解析的字节信息 /// - byte[] Content { get; set; } + ReadOnlyMemory Content { get; set; } /// /// 消息头的指令长度,不固定时返回0 @@ -42,14 +42,14 @@ public interface IResultMessage : IOperResult, IRequestInfo /// 然后返回 /// /// 是否成功有效 - FilterResult CheckBody(ref TByteBlock byteBlock) where TByteBlock : IByteBlock; + FilterResult CheckBody(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader; /// /// 检查头子节的合法性,并赋值
/// 如果返回false,意味着放弃本次解析的所有数据,包括已经解析完成的Header ///
/// 是否成功的结果 - bool CheckHead(ref TByteBlock byteBlock) where TByteBlock : IByteBlock; + bool CheckHead(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader; /// /// 发送前的信息处理,例如存储某些特征信息:站号/功能码等等用于验证后续的返回信息是否合法 diff --git a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/ISendMessage.cs b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/ISendMessage.cs index c6d2cc9cd..71c26e8c8 100644 --- a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/ISendMessage.cs +++ b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/ISendMessage.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -13,6 +13,6 @@ namespace ThingsGateway.Foundation; /// /// 发送消息 /// -public interface ISendMessage : IRequestInfo, IWaitHandle, IRequestInfoBuilder +public interface ISendMessage : IRequestInfo, IWaitHandle, IRequestInfoByteBlockWriterBuilder { } diff --git a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/MessageBase.cs b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/MessageBase.cs index fb8b44642..6797a2e80 100644 --- a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/MessageBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/MessageBase.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -11,7 +11,7 @@ namespace ThingsGateway.Foundation; /// -public class MessageBase : OperResultClass, IResultMessage, IWaitHandle +public class MessageBase : OperResultClass>, IResultMessage, IWaitHandle { #region 构造 @@ -42,13 +42,13 @@ public class MessageBase : OperResultClass, IResultMessage, IWaitHandle public virtual int Sign { get; set; } = -1; /// - public virtual FilterResult CheckBody(ref TByteBlock byteBlock) where TByteBlock : IByteBlock + public virtual FilterResult CheckBody(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader { return FilterResult.Success; } /// - public virtual bool CheckHead(ref TByteBlock byteBlock) where TByteBlock : IByteBlock + public virtual bool CheckHead(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader { return true; } diff --git a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/TcpCustomDataHandlingAdapter.cs b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/TcpCustomDataHandlingAdapter.cs index 251cc0978..2fe2edc83 100644 --- a/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/TcpCustomDataHandlingAdapter.cs +++ b/src/Foundation/ThingsGateway.Foundation/DataHandleAdapter/TcpCustomDataHandlingAdapter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 // 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 // CSDN博客:https://blog.csdn.net/qq_40374647 @@ -13,14 +13,14 @@ namespace ThingsGateway.Foundation; /// -/// 用户自定义数据处理适配器,使用该适配器时,接收方收到的数据中,将为null, +/// 用户自定义数据处理适配器,使用该适配器时,接收方收到的数据中,将为, /// 同时将实现为TRequest,发送数据直接发送。 /// public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataHandlingAdapter where TRequest : IRequestInfo { - private ValueByteBlock m_tempByteBlock; - private readonly Type m_requestType; + private ValueByteBlock m_tempByteBlock; + private TRequest m_tempRequest; /// /// 初始化自定义数据处理适配器。 @@ -33,16 +33,9 @@ public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataH this.m_requestType = typeof(TRequest); } - private TRequest m_tempRequest; - /// public override bool CanSendRequestInfo => false; - /// - /// 默认不支持拼接发送 - /// - public override bool CanSplicingSend => false; - /// /// 指示需要解析当前包的剩余长度。如果不能得知,请赋值。 /// @@ -52,10 +45,10 @@ public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataH /// 尝试解析请求数据块。 /// /// 字节块类型,必须实现IByteBlock接口。 - /// 待解析的字节块。 + /// 待解析的字节块。 /// 解析出的请求对象。 /// 解析是否成功。 - public bool TryParseRequest(ref TByteBlock byteBlock, out TRequest request) where TByteBlock : IByteBlock + public bool TryParseRequest(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 : 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 : 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 : 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 : SingleStreamDataH // 如果字节块中还有剩余数据,则回退指针。 if (block.CanReadLength > 0) { - byteBlock.Position -= block.CanReadLength; + reader.Position -= block.CanReadLength; } return true; } @@ -119,7 +112,7 @@ public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataH // 对于需要继续解析的情况,也回退指针。 if (block.CanReadLength > 0) { - byteBlock.Position -= block.CanReadLength; + reader.Position -= block.CanReadLength; } return false; } @@ -138,13 +131,23 @@ public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataH /// 当数据部分异常时,请移动到指定位置,然后返回 /// 当完全满足解析条件时,请返回最后将移至指定位置。 /// - /// 字节块 + /// 字节块 /// 是否为上次遗留对象,当该参数为时,request也将是上次实例化的对象。 /// 对象。 /// 缓存容量。当需要首次缓存时,指示申请的ByteBlock的容量。合理的值可避免ByteBlock扩容带来的性能消耗。 /// - protected abstract FilterResult Filter(ref TByteBlock byteBlock, bool beCached, ref TRequest request, ref int tempCapacity) - where TByteBlock : IByteBlock; + protected abstract FilterResult Filter(ref TByteBlock reader, bool beCached, ref TRequest request, ref int tempCapacity) + where TByteBlock : IByteBlockReader; + + /// + /// 判断请求对象是否应该被缓存。 + /// + /// 请求对象。 + /// 返回布尔值,指示请求对象是否应该被缓存。 + protected virtual bool IsBeCached(in TRequest request) + { + return this.m_requestType.IsValueType ? request.GetHashCode() != default(TRequest).GetHashCode() : request != null; + } /// /// 成功执行接收以后。 @@ -155,7 +158,7 @@ public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataH } /// - /// 即将执行。 + /// 即将执行。 /// /// /// 返回值标识是否继续执行 @@ -166,7 +169,7 @@ public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataH /// /// - 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 : 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 : 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(); } - /// - /// 判断请求对象是否应该被缓存。 - /// - /// 请求对象。 - /// 返回布尔值,指示请求对象是否应该被缓存。 - protected virtual bool IsBeCached(in TRequest request) - { - // 如果请求对象类型是值类型,则判断其哈希码是否与默认值不同; - // 如果是引用类型,则判断对象本身是否为null。 - return this.m_requestType.IsValueType ? request.GetHashCode() != default(TRequest).GetHashCode() : request != null; - } - /// /// 处理单个字节块,提取请求对象。 /// /// 字节块类型,需要实现IByteBlock接口。 - /// 字节块,将被解析以提取请求对象。 + /// 字节块,将被解析以提取请求对象。 /// 输出参数,提取出的请求对象。 /// 返回过滤结果,指示处理的状态。 - protected FilterResult Single(ref TByteBlock byteBlock, out TRequest request) where TByteBlock : IByteBlock + protected FilterResult Single(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 : 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 : SingleStreamDataH } // 将字节块指针移到末尾。 - byteBlock.SeekToEnd(); + reader.Advance((int)reader.BytesRemaining); } // 更新缓存时间。 if (this.UpdateCacheTimeWhenRev) @@ -264,17 +257,17 @@ public abstract class TcpCustomDataHandlingAdapter : SingleStreamDataH } } - private async Task SingleAsync(TByteBlock byteBlock, bool temp) where TByteBlock : IByteBlock + private async Task SingleAsync(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 : 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) { diff --git a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs index 6577b8e59..5ab26681e 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs @@ -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; /// @@ -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 } /// - public virtual ValueTask> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default) + public virtual ValueTask>> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default) { var channelResult = GetChannel(this is IDtu dtu ? dtu.DtuId : null); - if (!channelResult.IsSuccess) return EasyValueTask.FromResult(new OperResult(channelResult)); + if (!channelResult.IsSuccess) return EasyValueTask.FromResult(new OperResult>(channelResult)); return SendThenReturnAsync(sendMessage, channelResult.Content, cancellationToken); } - /// - protected virtual async ValueTask 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); - } + /// - public virtual async ValueTask> SendThenReturnAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken cancellationToken = default) + public virtual async ValueTask>> SendThenReturnAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken cancellationToken = default) { try { - var result = await SendThenReturnMessageBaseAsync(sendMessage, channel, cancellationToken).ConfigureAwait(false); - return new OperResult(result) { Content = result.Content }; + var result = await SendThenReturnMessageAsync(sendMessage, channel, cancellationToken).ConfigureAwait(false); + return new OperResult>(result) { Content = result.Content }; } catch (Exception ex) { @@ -517,7 +517,15 @@ public abstract class DeviceBase : DisposableObject, IDevice } /// - protected virtual ValueTask SendThenReturnMessageBaseAsync(ISendMessage command, IClientChannel clientChannel = default, CancellationToken cancellationToken = default) + protected virtual async ValueTask 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); + } + + /// + protected virtual ValueTask 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 动态类型读写 /// - public virtual async ValueTask> ReadAsync(string address, int length, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public virtual async ValueTask> 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(string.Format(AppResource.DataTypeNotSupported, dataType)), }; } /// - public virtual async ValueTask WriteAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public virtual async ValueTask 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(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Boolean => await WriteAsync(address, jArray.ToObject(), cancellationToken).ConfigureAwait(false), - DataTypeEnum.Byte => await WriteAsync(address, jArray.ToObject(), dataType, cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int16 => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt16 => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int32 => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt32 => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Int64 => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.UInt64 => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Single => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), - DataTypeEnum.Double => await WriteAsync(address, jArray.ToObject(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.String => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Boolean => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken).ConfigureAwait(false), + DataTypeEnum.Byte => await WriteAsync(address, jArray.ToObject().AsMemory(), dataType, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int16 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt16 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int32 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt32 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Int64 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.UInt64 => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Single => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Double => await WriteAsync(address, jArray.ToObject().AsMemory(), cancellationToken: cancellationToken).ConfigureAwait(false), + DataTypeEnum.Decimal => await WriteAsync(address, jArray.ToObject().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(), bitConverter, cancellationToken).ConfigureAwait(false), DataTypeEnum.Single => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), DataTypeEnum.Double => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), + DataTypeEnum.Decimal => await WriteAsync(address, value.ToObject(), bitConverter, cancellationToken).ConfigureAwait(false), _ => new OperResult(string.Format(AppResource.DataTypeNotSupported, dataType)), }; } @@ -685,16 +696,16 @@ public abstract class DeviceBase : DisposableObject, IDevice #region 读取 /// - public abstract ValueTask> ReadAsync(string address, int length, CancellationToken cancellationToken = default); + public abstract ValueTask>> ReadAsync(string address, int length, CancellationToken cancellationToken = default); /// - public virtual async ValueTask> ReadBooleanAsync(string address, int length, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual async ValueTask> 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))); } /// @@ -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)); } /// @@ -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)); } /// @@ -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)); } /// @@ -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)); } /// @@ -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)); } /// @@ -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)); } /// @@ -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)); } /// @@ -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)); + } + /// + public virtual async ValueTask> 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)); } - /// public virtual async ValueTask> 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 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 写入 /// - public abstract ValueTask WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default); + public abstract ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default); /// - public abstract ValueTask WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default); + public abstract ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default); /// public virtual ValueTask WriteAsync(string address, bool value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) @@ -802,7 +819,7 @@ public abstract class DeviceBase : DisposableObject, IDevice public virtual ValueTask 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); } /// @@ -860,7 +877,12 @@ public abstract class DeviceBase : DisposableObject, IDevice bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address); return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Double, cancellationToken); } - + /// + public virtual ValueTask WriteAsync(string address, decimal value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + { + bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address); + return WriteAsync(address, bitConverter.GetBytes(value), DataTypeEnum.Decimal, cancellationToken); + } /// public virtual ValueTask WriteAsync(string address, string value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) { @@ -874,73 +896,81 @@ public abstract class DeviceBase : DisposableObject, IDevice #region 写入数组 /// - public virtual ValueTask WriteAsync(string address, short[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual ValueTask WriteAsync(string address, ushort[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual ValueTask WriteAsync(string address, int[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual ValueTask WriteAsync(string address, uint[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual ValueTask WriteAsync(string address, long[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual ValueTask WriteAsync(string address, ulong[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual ValueTask WriteAsync(string address, float[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual ValueTask WriteAsync(string address, double[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory 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); } /// - public virtual async ValueTask WriteAsync(string address, string[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public virtual ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + { + bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address); + return WriteAsync(address, bitConverter.GetBytes(value.Span), DataTypeEnum.Decimal, cancellationToken); + } + + /// + public virtual async ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) { bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address); if (bitConverter.StringLength == null) return new OperResult(AppResource.StringAddressError); - List bytes = new(); - foreach (var a in value) + List> 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> ReadAsync(object state, CancellationToken cancellationToken = default); + public abstract ValueTask>> ReadAsync(object state, CancellationToken cancellationToken = default); } diff --git a/src/Foundation/ThingsGateway.Foundation/Device/DeviceExtension.cs b/src/Foundation/ThingsGateway.Foundation/Device/DeviceExtension.cs index f64c78c22..8cd9c664d 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/DeviceExtension.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/DeviceExtension.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -99,7 +99,7 @@ public static partial class DeviceExtension /// 返回的字节数组 /// 任意一个失败时抛出异常 /// 解析结果 - public static OperResult PraseStructContent(this IEnumerable variables, IDevice device, byte[] buffer, bool exWhenAny) where T : IVariable + public static OperResult PraseStructContent(this IEnumerable variables, IDevice device, ReadOnlySpan buffer, bool exWhenAny) where T : IVariable { var time = DateTime.Now; var result = OperResult.Success; diff --git a/src/Foundation/ThingsGateway.Foundation/Device/DtuServiceDeviceBase.cs b/src/Foundation/ThingsGateway.Foundation/Device/DtuServiceDeviceBase.cs index 4a8f4fdcc..3f4fbe1b5 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/DtuServiceDeviceBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/DtuServiceDeviceBase.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Device/IDevice.cs b/src/Foundation/ThingsGateway.Foundation/Device/IDevice.cs index bccf55242..001d2de50 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/IDevice.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/IDevice.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -121,7 +121,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 数据类型 /// 取消令箭 /// - ValueTask> ReadAsync(string address, int length, DataTypeEnum dataType, CancellationToken cancellationToken = default); + ValueTask> ReadArrayAsync(string address, int length, DataTypeEnum dataType, CancellationToken cancellationToken = default); /// /// 根据数据类型,写入类型值 @@ -131,7 +131,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 数据类型 /// 取消令箭 /// - ValueTask WriteAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default); + ValueTask WriteJTokenAsync(string address, JToken value, DataTypeEnum dataType, CancellationToken cancellationToken = default); #endregion 动态类型读写 @@ -144,7 +144,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 读取寄存器数量,对于不同PLC,对应的字节数量可能不一样 /// 取消令箭 /// - ValueTask> ReadAsync(string address, int length, CancellationToken cancellationToken = default); + ValueTask>> ReadAsync(string address, int length, CancellationToken cancellationToken = default); /// /// 读取布尔量数组 @@ -253,12 +253,12 @@ public interface IDevice : IDisposable, IDisposableObject /// /// 写入原始的byte数组数据到指定的地址,返回结果 /// - ValueTask WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default); /// /// 写入bool数组数据,返回结果 /// - ValueTask WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default); /// /// 写入bool数据,返回结果 @@ -327,7 +327,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, string[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入Double数组 @@ -337,7 +337,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, double[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入Single数组 @@ -347,7 +347,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, float[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入Int32数组 @@ -357,7 +357,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, int[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入Int64数组 @@ -367,7 +367,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, long[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入Int16数组 @@ -377,7 +377,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, short[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入UInt32数组 @@ -387,7 +387,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, uint[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入UInt64数组 @@ -397,7 +397,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, ulong[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); /// /// 写入UInt16数组 @@ -407,7 +407,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 转换规则 /// 取消令箭 /// 写入结果 - ValueTask WriteAsync(string address, ushort[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); + ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default); #endregion 写入数组 @@ -445,7 +445,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 取消令箭 /// 通道 /// 返回消息体 - ValueTask> SendThenReturnAsync(ISendMessage command, IClientChannel channel = default, CancellationToken cancellationToken = default); + ValueTask>> SendThenReturnAsync(ISendMessage command, IClientChannel channel = default, CancellationToken cancellationToken = default); /// /// 发送并等待返回,会经过适配器,可传入socketId,如果为空,则默认通道必须为类型 @@ -453,7 +453,7 @@ public interface IDevice : IDisposable, IDisposableObject /// 发送字节数组 /// 取消令箭 /// 返回消息体 - ValueTask> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default); + ValueTask>> SendThenReturnAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default); /// /// 支持通道多设备 @@ -467,6 +467,6 @@ public interface IDevice : IDisposable, IDisposableObject /// 通道 /// 单独设备日志 void InitChannel(IChannel channel, ILog? deviceLog = null); - ValueTask> ReadAsync(object state, CancellationToken cancellationToken = default); + ValueTask>> ReadAsync(object state, CancellationToken cancellationToken = default); Task ConnectAsync(CancellationToken token); } diff --git a/src/Foundation/ThingsGateway.Foundation/Enums/BcdFormatEnum.cs b/src/Foundation/ThingsGateway.Foundation/Enums/BcdFormatEnum.cs index 8c5f2e09f..d52cf9a10 100644 --- a/src/Foundation/ThingsGateway.Foundation/Enums/BcdFormatEnum.cs +++ b/src/Foundation/ThingsGateway.Foundation/Enums/BcdFormatEnum.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Enums/ChannelEnum.cs b/src/Foundation/ThingsGateway.Foundation/Enums/ChannelEnum.cs index 0105a2a5f..23d0f2b00 100644 --- a/src/Foundation/ThingsGateway.Foundation/Enums/ChannelEnum.cs +++ b/src/Foundation/ThingsGateway.Foundation/Enums/ChannelEnum.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Enums/DataFormatEnum.cs b/src/Foundation/ThingsGateway.Foundation/Enums/DataFormatEnum.cs index 3ee443968..8ae754739 100644 --- a/src/Foundation/ThingsGateway.Foundation/Enums/DataFormatEnum.cs +++ b/src/Foundation/ThingsGateway.Foundation/Enums/DataFormatEnum.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Enums/DataTypeEnum.cs b/src/Foundation/ThingsGateway.Foundation/Enums/DataTypeEnum.cs index e3f2f8354..3de03a8d6 100644 --- a/src/Foundation/ThingsGateway.Foundation/Enums/DataTypeEnum.cs +++ b/src/Foundation/ThingsGateway.Foundation/Enums/DataTypeEnum.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -50,4 +50,7 @@ public enum DataTypeEnum /// Double, + + /// + Decimal, } diff --git a/src/Foundation/ThingsGateway.Foundation/Enums/ErrorTypeEnum.cs b/src/Foundation/ThingsGateway.Foundation/Enums/ErrorTypeEnum.cs index 644b7e1f5..9ea66549f 100644 --- a/src/Foundation/ThingsGateway.Foundation/Enums/ErrorTypeEnum.cs +++ b/src/Foundation/ThingsGateway.Foundation/Enums/ErrorTypeEnum.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/BoolExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/BoolExtensions.cs index 820644b77..3ab04b801 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/BoolExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/BoolExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -16,23 +16,32 @@ namespace ThingsGateway.Foundation; public static class BoolExtensions { /// - /// 将bool数组转换到byte数组 + /// 将布尔数组转换为压缩的字节数组(每 8 位布尔值压缩为 1 个字节,低位在前)。 /// - public static byte[] BoolArrayToByte(this bool[] array) + /// 布尔数组 + /// 压缩后的只读字节内存 + public static byte[] BoolArrayToByte(this ReadOnlySpan array) { + if (array.IsEmpty) + return Array.Empty(); + 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; } + + /// + /// 将布尔数组转换为压缩的字节数组(每 8 位布尔值压缩为 1 个字节,低位在前)。 + /// + public static byte[] BoolArrayToByte(this Span array) + => ((ReadOnlySpan)array).BoolArrayToByte(); } diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/ByteBlockExtension.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/ByteBlockExtension.cs new file mode 100644 index 000000000..7f4dbcdaa --- /dev/null +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/ByteBlockExtension.cs @@ -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; + + + +/// +/// 提供字节块扩展方法的静态类。 +/// +public static class ByteBlockExtension +{ + public static void WriteBackAddValue(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(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(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(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(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; + } + + + /// + /// 将值类型的字节块转换为普通的字节块。 + /// + /// 要转换的值类型字节块。 + /// 一个新的字节块对象。 + 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 + + /// + /// 将指定的字节块转换为【新】字节数组。 + /// + /// 实现接口的字节块类型。 + /// 字节块对象。 + /// 起始偏移量。 + /// 要转换为数组的长度。 + /// 包含指定长度的【新】字节数组。 + public static byte[] ToArray(this TByteBlock byteBlock, int offset, int length) where TByteBlock : IByteBlockCore + { + return byteBlock.Span.Slice(offset, length).ToArray(); + } + + /// + /// 将指定的字节块转换为【新】字节数组,从指定偏移量开始,直到字节块的末尾。 + /// + /// 实现接口的字节块类型。 + /// 字节块对象。 + /// 起始偏移量。 + /// 从指定偏移量到字节块末尾的【新】字节数组。 + public static byte[] ToArray(this TByteBlock byteBlock, int offset) where TByteBlock : IByteBlockCore + { + return ToArray(byteBlock, offset, byteBlock.Length - offset); + } + + /// + /// 将指定的字节块转换为【新】字节数组,从索引0开始,直到字节块的末尾。 + /// + /// 实现接口的字节块类型。 + /// 字节块对象。 + /// 整个字节块的【新】字节数组。 + public static byte[] ToArray(this TByteBlock byteBlock) where TByteBlock : IByteBlockCore + { + return ToArray(byteBlock, 0, byteBlock.Length); + } + + /// + /// 将指定的字节块从当前位置转换为【新】字节数组,直到字节块的末尾。 + /// + /// 实现接口的字节块类型。 + /// 字节块对象。 + /// 从当前位置到字节块末尾的【新】字节数组。 + public static byte[] ToArrayTake(this TByteBlock byteBlock) where TByteBlock : IByteBlockReader + { + return ToArray(byteBlock, byteBlock.Position, byteBlock.CanReadLength); + } + + /// + /// 将指定的字节块从当前位置转换为【新】字节数组,指定长度。 + /// + /// 实现接口的字节块类型。 + /// 字节块对象。 + /// 要转换为数组的长度。 + /// 从当前位置开始,指定长度的【新】字节数组。 + public static byte[] ToArrayTake(this TByteBlock byteBlock, int length) where TByteBlock : IByteBlockReader + { + return ToArray(byteBlock, byteBlock.Position, length); + } + + #endregion ToArray + + #region AsSegment + + /// + /// 将字节块【作为】数组段。 + /// + /// 【作为】的意思是,导出的数据内存实际上依旧是生命周期内的,不能脱离生命周期使用。 + /// + /// + /// 实现接口的字节块类型。 + /// 要转换的字节块实例。 + /// 数组段的起始偏移量。 + /// 数组段的长度。 + /// 一个包含指定偏移量和长度的数组段。 + public static ArraySegment AsSegment(this TByteBlock byteBlock, int offset, int length) where TByteBlock : IByteBlockReader + { + return byteBlock.Memory.Slice(offset, length).GetArray(); + } + + /// + /// 将字节块【作为】数组段,从指定偏移量开始,长度为可读长度。 + /// + /// 【作为】的意思是,导出的数据内存实际上依旧是生命周期内的,不能脱离生命周期使用。 + /// + /// + /// 实现接口的字节块类型。 + /// 要转换的字节块实例。 + /// 数组段的起始偏移量。 + /// 一个从指定偏移量开始,长度为可读长度的数组段。 + public static ArraySegment AsSegment(this TByteBlock byteBlock, int offset) where TByteBlock : IByteBlockReader + { + return AsSegment(byteBlock, offset, byteBlock.Length - offset); + } + + /// + /// 将字节块【作为】数组段,从头开始,长度为指定长度。 + /// + /// 【作为】的意思是,导出的数据内存实际上依旧是生命周期内的,不能脱离生命周期使用。 + /// + /// + /// 实现接口的字节块类型。 + /// 要转换的字节块实例。 + /// 一个从头开始,长度为字节块长度的数组段。 + public static ArraySegment AsSegment(this TByteBlock byteBlock) where TByteBlock : IByteBlockReader + { + return AsSegment(byteBlock, 0, byteBlock.Length); + } + + /// + /// 将字节块【作为】数组段,从当前位置开始,指定长度。 + /// + /// 【作为】的意思是,导出的数据内存实际上依旧是生命周期内的,不能脱离生命周期使用。 + /// + /// + /// 实现接口的字节块类型。 + /// 要转换的字节块实例。 + /// 数组段的长度。 + /// 一个从当前位置开始,指定长度的数组段。 + public static ArraySegment AsSegmentTake(this TByteBlock byteBlock, int length) where TByteBlock : IByteBlockReader + { + return AsSegment(byteBlock, byteBlock.Position, length); + } + + /// + /// 将字节块【作为】数组段,从当前位置开始,长度为可读长度。 + /// + /// 【作为】的意思是,导出的数据内存实际上依旧是生命周期内的,不能脱离生命周期使用。 + /// + /// + /// 实现接口的字节块类型。 + /// 要转换的字节块实例。 + /// 一个从当前位置开始,长度为可读长度的数组段。 + public static ArraySegment AsSegmentTake(this TByteBlock byteBlock) where TByteBlock : IByteBlockReader + { + return AsSegment(byteBlock, byteBlock.Position, byteBlock.CanReadLength); + } + /// + public static string ToString(this TByteBlock byteBlock, int offset, int length) where TByteBlock : IByteBlockReader + { + return byteBlock.Span.Slice(offset, length).ToString(Encoding.UTF8); + } + + /// + public static string ToString(this TByteBlock byteBlock, int offset) where TByteBlock : IByteBlockReader + { + return byteBlock.Span.Slice(offset, byteBlock.Length - offset).ToString(Encoding.UTF8); + + } + #endregion AsSegment + + +} \ No newline at end of file diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/ByteExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/ByteExtensions.cs index 77fea55ca..dc4a6c74c 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/ByteExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/ByteExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -10,8 +10,6 @@ using System.Text; -using ThingsGateway.Foundation.Extension.Generic; - namespace ThingsGateway.Foundation; /// @@ -21,6 +19,8 @@ public static class ByteExtensions { return DataTransUtil.SpliceArray(array, values); } + + /// /// 获取byte数据类型的第offset位,是否为True
///
@@ -38,6 +38,24 @@ public static class ByteExtensions return (value & mask) == mask; } + public static byte[] BoolToByte(this ReadOnlySpan 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 value) + { + bool[] bytes = new bool[value.Length]; + for (int i = 0; i < value.Length; i++) + { + bytes[i] = value[i] > 0 ? true : false; + } + return bytes; + } /// /// 数组内容分别相加某个数字 /// @@ -46,28 +64,27 @@ public static class ByteExtensions /// 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)bytes, value); } - /// /// 数组内容分别相加某个数字 /// /// /// /// - public static ReadOnlySpan BytesAdd(this ReadOnlySpan bytes, int value) + public static byte[] BytesAdd(this Span bytes, int value) { + return BytesAdd((ReadOnlySpan)bytes, value); + } + /// + /// 数组内容分别相加某个数字 + /// + /// + /// + /// + public static byte[] BytesAdd(this ReadOnlySpan bytes, int value) + { + if (bytes.IsEmpty) return Array.Empty(); byte[] result = new byte[bytes.Length]; for (int index = 0; index < bytes.Length; index++) { @@ -82,104 +99,143 @@ public static class ByteExtensions ///
/// 输入的字节信息 /// 反转后的数据 - public static byte[] BytesReverseByWord(this byte[] inBytes) + /// + /// 将字节数组按“字(2字节)”为单位反转高低位,奇数长度自动补齐 0。 + /// + public static ReadOnlySpan BytesReverseByWord(this ReadOnlySpan inBytes) { - if (inBytes.Length == 0) + int len = inBytes.Length; + if (len == 0) + return ReadOnlySpan.Empty; + + // 如果是奇数,自动补齐 0 + int evenLen = (len % 2 == 0) ? len : len + 1; + if (evenLen == len) return inBytes; + + Span 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; + } + + /// + /// 将byte数组按照双字节进行反转,如果为单数的情况,则自动补齐
+ ///
+ /// 输入的字节信息 + /// 反转后的数据 + /// + /// 将字节数组按“字(2字节)”为单位反转高低位,奇数长度自动补齐 0。 + /// + public static Memory BytesReverseByWord(this Memory inBytes) + { + int len = inBytes.Length; + if (len == 0) + return Memory.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; + } + + /// + /// 从字节数组中提取位数组,length 代表位数 + /// + /// 原始的字节数组 + /// 想要转换的位数,如果超出字节数组长度 * 8,则自动缩小为数组最大长度 + /// 转换后的布尔数组 + public static ReadOnlySpan ByteToBoolArray(this ReadOnlySpan 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 CombineMemoryBlocks(this List> blocks) + { + if (blocks == null || blocks.Count == 0) + return ReadOnlyMemory.Empty; + + // 计算总长度 + int totalLength = 0; + foreach (var block in blocks) + { + totalLength += block.Length; + } + + if (totalLength == 0) + return ReadOnlyMemory.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; + } + + /// + /// 获取异或校验,返回ASCII十六进制字符串的字节数组
+ ///
+ public static byte[] GetAsciiXOR(this ReadOnlySpan data) + { + if (data.IsEmpty) return Array.Empty(); - } - // 创建新数组进行补齐 - byte[] lengthEven = inBytes.CopyArray().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; } - /// - /// 从字节数组中提取位数组,length 代表位数 - /// - /// 原始的字节数组 - /// 想要转换的位数,如果超出字节数组长度 * 8,则自动缩小为数组最大长度 - /// 转换后的布尔数组 - 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; - } - /// - /// 从字节数组中提取位数组,length 代表位数 - /// - /// 原始的字节数组 - /// 想要转换的位数,如果超出字节数组长度 * 8,则自动缩小为数组最大长度 - /// 转换后的布尔数组 - public static bool[] ByteToBoolArray(this Span 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 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")); - } /// /// 获取Byte数组的第 boolIndex 偏移的bool值,这个偏移值可以为 10,就是第 1 个字节的 第3位
@@ -187,7 +243,7 @@ public static class ByteExtensions /// 字节数组信息 /// 指定字节的位偏移 /// bool值 - public static bool GetBoolByIndex(this byte[] bytes, int boolIndex) + public static bool GetBoolByIndex(this ReadOnlySpan bytes, int boolIndex) { return bytes[boolIndex / 8].BoolOnByteIndex(boolIndex % 8); } @@ -195,42 +251,34 @@ public static class ByteExtensions /// /// 字节数组默认转16进制字符 /// - /// - /// /// - public static string ToHexString(this ArraySegment buffer, char splite = ' ') + public static string ToHexString(this ArraySegment buffer, char splite = ' ', int newLineCount = 0) { - return DataTransUtil.ByteToHexString(buffer, splite); - } - - /// - /// 字节数组默认转16进制字符 - /// - /// - /// - /// - public static string ToHexString(this ReadOnlySpan buffer, char splite = ' ') - { - return DataTransUtil.ByteToHexString(buffer, splite); - } - - /// - /// 字节数组默认转16进制字符 - /// - /// - /// - /// - public static string ToHexString(this byte[] buffer, char splite = default) - { - return DataTransUtil.ByteToHexString(buffer, splite); + return DataTransUtil.ByteToHexString(buffer, splite, newLineCount); } /// /// 字节数组默认转16进制字符 /// /// - 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); + } + /// + /// 字节数组默认转16进制字符 + /// + /// + public static string ToHexString(this Span buffer, char splite = ' ', int newLineCount = 0) + { + return DataTransUtil.ByteToHexString(buffer, splite, newLineCount); + } + /// + /// 字节数组默认转16进制字符 + /// + /// + public static string ToHexString(this ReadOnlySpan buffer, char splite = ' ', int newLineCount = 0) + { + return DataTransUtil.ByteToHexString(buffer, splite, newLineCount); } } diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/DataTypeExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/DataTypeExtensions.cs index 6523b74a3..546b490bf 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/DataTypeExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/DataTypeExtensions.cs @@ -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), }; } diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/DateTimeExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/DateTimeExtensions.cs index 67e1df5e6..aa950d74e 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/DateTimeExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/DateTimeExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/ExceptionExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/ExceptionExtensions.cs index 9391cb6ae..1fa51c199 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/ExceptionExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/ExceptionExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/ExpandoObjectExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/ExpandoObjectExtensions.cs index 4d08a6f8e..7ebdf9c0a 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/ExpandoObjectExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/ExpandoObjectExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/GenericExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/GenericExtensions.cs index a01340c8e..2a887170d 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/GenericExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/GenericExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -32,6 +32,66 @@ public static class GenericExtensions return data; } + /// + /// 将一个数组进行扩充到指定长度,或是缩短到指定长度
+ ///
+ public static Memory ArrayExpandToLength(this Memory data, int length) + { + if (data.IsEmpty) + { + return Memory.Empty; + } + + if (data.Length == length) + { + return data; + } + + var result = new T[length]; + data.Slice(0, Math.Min(data.Length, length)).CopyTo(result); + return result; + } + + /// + /// 将一个数组进行扩充到指定长度,或是缩短到指定长度
+ ///
+ public static ReadOnlyMemory ArrayExpandToLength(this ReadOnlyMemory data, int length) + { + if (data.IsEmpty) + { + return ReadOnlyMemory.Empty; + } + + if (data.Length == length) + { + return data; + } + + var result = new T[length]; + data.Slice(0, Math.Min(data.Length, length)).CopyTo(result); + return result; + } + + /// + /// 将一个数组进行扩充到指定长度,或是缩短到指定长度
+ ///
+ public static ReadOnlySpan ArrayExpandToLength(this ReadOnlySpan data, int length) + { + if (data.IsEmpty) + { + return ReadOnlySpan.Empty; + } + + if (data.Length == length) + { + return data; + } + + var result = new T[length]; + data.Slice(0, Math.Min(data.Length, length)).CopyTo(result); + return result; + } + /// /// 将一个数组进行扩充到偶数长度
///
@@ -44,11 +104,41 @@ public static class GenericExtensions return data.Length % 2 == 1 ? data.ArrayExpandToLength(data.Length + 1) : data; } + /// + /// 将一个数组进行扩充到偶数长度
+ ///
+ public static ReadOnlyMemory ArrayExpandToLengthEven(this ReadOnlyMemory data) + { + if (data.IsEmpty) + { + return Array.Empty(); + } + + return data.Length % 2 == 1 ? data.ArrayExpandToLength(data.Length + 1) : data; + } /// - /// + /// 将一个数组进行扩充到偶数长度
///
- public static T[] ArrayRemoveBegin(T[] value, int length) => ArrayRemoveDouble(value, length, 0); + public static Memory ArrayExpandToLengthEven(this Memory data) + { + if (data.IsEmpty) + { + return Array.Empty(); + } + + return data.Length % 2 == 1 ? data.ArrayExpandToLength(data.Length + 1) : data; + } + + + public static T[] ArrayRemoveBegin(this T[] value, int length) => ArrayRemoveDouble(value, length, 0); + public static T[] ArrayRemoveLast(this T[] value, int length) => ArrayRemoveDouble(value, 0, length); + + public static ReadOnlySpan ArrayRemoveBegin(ReadOnlySpan value, int length) => ArrayRemoveDouble(value, length, 0); + public static ReadOnlySpan ArrayRemoveLast(ReadOnlySpan value, int length) => ArrayRemoveDouble(value, 0, length); + + public static ReadOnlyMemory ArrayRemoveBegin(ReadOnlyMemory value, int length) => ArrayRemoveDouble(value, length, 0); + public static ReadOnlyMemory ArrayRemoveLast(ReadOnlyMemory value, int length) => ArrayRemoveDouble(value, 0, length); /// /// 从数组中移除指定数量的元素,并返回新的数组 @@ -59,9 +149,21 @@ public static class GenericExtensions /// 从右侧移除的元素个数 /// 移除元素后的新数组 public static T[] ArrayRemoveDouble(T[] value, int leftLength, int rightLength) + { + return ArrayRemoveDouble((ReadOnlySpan)value, leftLength, rightLength).ToArray(); + } + /// + /// 从数组中移除指定数量的元素,并返回新的数组 + /// + /// 数组元素类型 + /// 要移除元素的数组 + /// 从左侧移除的元素个数 + /// 从右侧移除的元素个数 + /// 移除元素后的新数组 + public static ReadOnlySpan ArrayRemoveDouble(ReadOnlySpan value, int leftLength, int rightLength) { // 如果输入数组为空或者剩余长度不足以移除左右两侧指定的元素,则返回空数组 - if (value == null || value.Length <= leftLength + rightLength) + if (value.IsEmpty || value.Length <= leftLength + rightLength) { return Array.Empty(); } @@ -69,13 +171,28 @@ public static class GenericExtensions // 计算新数组的长度 int newLength = value.Length - leftLength - rightLength; - // 创建新数组 - T[] result = new T[newLength]; + return value.Slice(leftLength, newLength); + } + /// + /// 从数组中移除指定数量的元素,并返回新的数组 + /// + /// 数组元素类型 + /// 要移除元素的数组 + /// 从左侧移除的元素个数 + /// 从右侧移除的元素个数 + /// 移除元素后的新数组 + public static ReadOnlyMemory ArrayRemoveDouble(ReadOnlyMemory value, int leftLength, int rightLength) + { + // 如果输入数组为空或者剩余长度不足以移除左右两侧指定的元素,则返回空数组 + if (value.IsEmpty || value.Length <= leftLength + rightLength) + { + return Array.Empty(); + } - // 将剩余的元素复制到新数组中 - Array.Copy(value, leftLength, result, 0, newLength); + // 计算新数组的长度 + int newLength = value.Length - leftLength - rightLength; - return result; + return value.Slice(leftLength, newLength); } /// @@ -120,7 +237,13 @@ public static class GenericExtensions yield return chunk; } } - + public static IEnumerable> ChunkBetter(this ReadOnlyMemory span, int groupSize) + { + for (int i = 0; i < span.Length; i += groupSize) + { + yield return span.Slice(i, Math.Min(groupSize, span.Length - i)); + } + } /// 拷贝当前的实例数组,是基于引用层的浅拷贝,如果类型为值类型,那就是深度拷贝,如果类型为引用类型,就是浅拷贝 public static T[] CopyArray(this T[] value) @@ -153,37 +276,7 @@ public static class GenericExtensions return arrayFromOneArray; } - /// - /// 将一个数组的前后移除指定位数,返回新的一个数组
- ///
- public static T[] RemoveArray(this T[] value, int leftLength, int rightLength) - { - if (value == null || value.Length == 0) - { - return Array.Empty(); - } - int newLength = value.Length - leftLength - rightLength; - if (newLength <= 0) - { - return Array.Empty(); - } - - T[] result = new T[newLength]; - Array.Copy(value, leftLength, result, 0, newLength); - - return result; - } - - /// - /// 将一个数组的前面指定位数移除,返回新的一个数组
- ///
- public static T[] RemoveBegin(this T[] value, int length) => value.RemoveArray(length, 0); - - /// - /// 将一个数组的后面指定位数移除,返回新的一个数组
- ///
- public static T[] RemoveLast(this T[] value, int length) => value.RemoveArray(0, length); /// /// 选择数组中的最后几个元素组成新的数组 @@ -192,25 +285,7 @@ public static class GenericExtensions /// 输入数组 /// 选择的元素个数 /// 由最后几个元素组成的新数组 - public static T[] SelectLast(this T[] value, int length) - { - // 如果输入数组为空,则返回空数组 - if (value == null || value.Length == 0) - { - return Array.Empty(); - } - - // 计算实际需要复制的元素个数,取输入数组长度和指定长度的较小值 - 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(this T[] value, int length) => ArrayRemoveBegin(value, value.Length - length); /// /// 从数组中获取指定索引开始的中间一段长度的子数组 @@ -220,23 +295,5 @@ public static class GenericExtensions /// 起始索引 /// 选择的元素个数 /// 中间指定长度的子数组 - public static T[] SelectMiddle(this T[] value, int index, int length) - { - // 如果输入数组为空,则返回空数组 - if (value == null || value.Length == 0) - { - return Array.Empty(); - } - - // 计算实际需要复制的元素个数,取输入数组剩余元素和指定长度的较小值 - 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(this T[] value, int index, int length) => ArrayRemoveDouble(value, index, value.Length - index - length); } diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/LoggerExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/LoggerExtensions.cs index cafcac813..d0ea070f9 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/LoggerExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/LoggerExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/PackHelpers.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/PackHelpers.cs new file mode 100644 index 000000000..f3e2ea398 --- /dev/null +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/PackHelpers.cs @@ -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 GetSourceRead(this IEnumerable deviceVariables, IDevice device, string defaultIntervalTime) where T : IVariableSource, new() + { + var byteConverter = device.ThingsGatewayBitConverter; + var result = new List(); + //需要先剔除额外信息,比如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; + } +} \ No newline at end of file diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs index 5479d96c8..e8f9e4267 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -184,7 +184,7 @@ public static class StringExtensions /// /// /// - public static byte[] HexStringToBytes(this string str) => DataTransUtil.HexStringToBytes(str); + public static Memory 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[] { ',' }; diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs index 14997cc02..4ae1c01cc 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/GlobalUsings.cs b/src/Foundation/ThingsGateway.Foundation/GlobalUsings.cs index 4f6ef9363..b1b329ca1 100644 --- a/src/Foundation/ThingsGateway.Foundation/GlobalUsings.cs +++ b/src/Foundation/ThingsGateway.Foundation/GlobalUsings.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Locales/AppResource.cs b/src/Foundation/ThingsGateway.Foundation/Locales/AppResource.cs index e373a024c..5848b645b 100644 --- a/src/Foundation/ThingsGateway.Foundation/Locales/AppResource.cs +++ b/src/Foundation/ThingsGateway.Foundation/Locales/AppResource.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Localization/JsonLocalizer.cs b/src/Foundation/ThingsGateway.Foundation/Localization/JsonLocalizer.cs index d7c40ceb6..9676c945b 100644 --- a/src/Foundation/ThingsGateway.Foundation/Localization/JsonLocalizer.cs +++ b/src/Foundation/ThingsGateway.Foundation/Localization/JsonLocalizer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Logger/TextFileLogger.cs b/src/Foundation/ThingsGateway.Foundation/Logger/TextFileLogger.cs index aede8a9c9..92e8924a3 100644 --- a/src/Foundation/ThingsGateway.Foundation/Logger/TextFileLogger.cs +++ b/src/Foundation/ThingsGateway.Foundation/Logger/TextFileLogger.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs b/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs index 7b7e76794..bb75a8ad1 100644 --- a/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs +++ b/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/OperResult/IOperResult.cs b/src/Foundation/ThingsGateway.Foundation/OperResult/IOperResult.cs index 8464f4905..ab5a29fce 100644 --- a/src/Foundation/ThingsGateway.Foundation/OperResult/IOperResult.cs +++ b/src/Foundation/ThingsGateway.Foundation/OperResult/IOperResult.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResult.cs b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResult.cs index 0f2428deb..361f0ca66 100644 --- a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResult.cs +++ b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResult.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultClass.cs b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultClass.cs index ecff2d9d2..1d4de916a 100644 --- a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultClass.cs +++ b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultClass.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultExtension.cs b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultExtension.cs index 69fb13df3..bdf21aad8 100644 --- a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultExtension.cs +++ b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultExtension.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -18,7 +18,7 @@ public static class OperResultExtension /// /// 转换对应类型 /// - public static OperResult GetResultFromBytes(this OperResult result, Func translator) + public static OperResult GetResultFromBytes(this OperResult> result, Func, TResult> translator) { try { @@ -26,7 +26,35 @@ public static class OperResultExtension } catch (Exception ex) { - return new OperResult(string.Format(AppResource.TransBytesError, result.Content?.ToHexString(' '), result.Content?.Length), ex); + return new OperResult(string.Format(AppResource.TransBytesError, result.Content.Span.ToHexString(' '), result.Content.Span.Length), ex); + } + } + /// + /// 转换对应类型 + /// + public static OperResult GetResultFromBytes(this OperResult> result, Func, TResult> translator) + { + try + { + return result.IsSuccess ? new() { Content = translator(result.Content) } : new OperResult(result); + } + catch (Exception ex) + { + return new OperResult(string.Format(AppResource.TransBytesError, result.Content.Span.ToHexString(' '), result.Content.Span.Length), ex); + } + } + /// + /// 转换对应类型 + /// + public static OperResult GetResultFromBytes(this OperResult result, Func translator) + { + try + { + return result.IsSuccess ? new() { Content = translator(result.Content) } : new OperResult(result); + } + catch (Exception ex) + { + return new OperResult(string.Format(AppResource.TransBytesError, result.Content.ToHexString(' '), result.Content.Length), ex); } } @@ -41,6 +69,16 @@ public static class OperResultExtension return new OperResult(result); } + /// + /// 操作成功则继续,并返回对应结果值 + /// + public static OperResult OperResultFrom(this OperResult result, Func func) + { + if (result.IsSuccess) + return new OperResult() { Content = func(result.Content) }; + else + return new OperResult(result); + } /// /// 操作成功则继续,并返回对应结果值 /// @@ -51,7 +89,6 @@ public static class OperResultExtension else return new OperResult(result); } - /// /// 操作成功则继续 /// diff --git a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultUtil.cs b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultUtil.cs index 24d5219d8..6f0753be3 100644 --- a/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultUtil.cs +++ b/src/Foundation/ThingsGateway.Foundation/OperResult/OperResultUtil.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/OperResult/ReturnErrorException.cs b/src/Foundation/ThingsGateway.Foundation/OperResult/ReturnErrorException.cs index 0fa1cb2f2..c60103388 100644 --- a/src/Foundation/ThingsGateway.Foundation/OperResult/ReturnErrorException.cs +++ b/src/Foundation/ThingsGateway.Foundation/OperResult/ReturnErrorException.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj b/src/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj index 9c11384c4..8475c511d 100644 --- a/src/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj +++ b/src/Foundation/ThingsGateway.Foundation/ThingsGateway.Foundation.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/Foundation/ThingsGateway.Foundation/Trans/ConverterConfig.cs b/src/Foundation/ThingsGateway.Foundation/Trans/ConverterConfig.cs index 822d6e771..4cef80a8d 100644 --- a/src/Foundation/ThingsGateway.Foundation/Trans/ConverterConfig.cs +++ b/src/Foundation/ThingsGateway.Foundation/Trans/ConverterConfig.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Trans/IThingsGatewayBitConverter.cs b/src/Foundation/ThingsGateway.Foundation/Trans/IThingsGatewayBitConverter.cs index a3cec614e..8c3f27a19 100644 --- a/src/Foundation/ThingsGateway.Foundation/Trans/IThingsGatewayBitConverter.cs +++ b/src/Foundation/ThingsGateway.Foundation/Trans/IThingsGatewayBitConverter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -95,7 +95,7 @@ public interface IThingsGatewayBitConverter /// /// 等待转化的数组 /// buffer数据 - byte[] GetBytes(bool[] values); + byte[] GetBytes(ReadOnlySpan values); /// /// short变量转化缓存数据,一个short数据可以转为2个字节的byte数组
@@ -105,29 +105,30 @@ public interface IThingsGatewayBitConverter byte[] GetBytes(short value); /// - byte[] GetBytes(short[] value); + byte[] GetBytes(ReadOnlySpan value); /// - byte[] GetBytes(ushort[] value); + byte[] GetBytes(ReadOnlySpan value); /// - byte[] GetBytes(int[] value); + byte[] GetBytes(ReadOnlySpan value); /// - byte[] GetBytes(uint[] value); + byte[] GetBytes(ReadOnlySpan value); /// - byte[] GetBytes(long[] value); + byte[] GetBytes(ReadOnlySpan value); /// - byte[] GetBytes(ulong[] value); + byte[] GetBytes(ReadOnlySpan value); /// - byte[] GetBytes(float[] value); + byte[] GetBytes(ReadOnlySpan value); /// - byte[] GetBytes(double[] value); - + byte[] GetBytes(ReadOnlySpan value); + /// + byte[] GetBytes(ReadOnlySpan value); /// /// ushort变量转化缓存数据,一个ushort数据可以转为2个字节的Byte数组
///
@@ -182,7 +183,7 @@ public interface IThingsGatewayBitConverter ///
/// 等待转化的数据 /// buffer数据 - byte[] GetBytes(string value); + Memory GetBytes(string value); #endregion GetBytes @@ -194,16 +195,16 @@ public interface IThingsGatewayBitConverter /// 等待提取的缓存数据 /// 位的索引,注意:是从0开始的位索引,10则表示 buffer[1] 的第二位。 /// 是否需要按字反转 - bool ToBoolean(byte[] buffer, int offset, bool isReverse); + bool ToBoolean(ReadOnlySpan buffer, int offset, bool isReverse); /// - bool[] ToBoolean(byte[] buffer, int offset, int len, bool isReverse); + bool[] ToBoolean(ReadOnlySpan buffer, int offset, int len, bool isReverse); /// - byte ToByte(byte[] buffer, int offset); + byte ToByte(ReadOnlySpan buffer, int offset); /// - byte[] ToByte(byte[] buffer, int offset, int length); + byte[] ToByte(ReadOnlySpan buffer, int offset, int length); /// /// 从缓存中提取double结果,需要指定起始的字节索引,按照字节为单位,一个double占用八个字节
@@ -211,10 +212,10 @@ public interface IThingsGatewayBitConverter /// 缓存对象 /// 索引位置 /// double对象 - double ToDouble(byte[] buffer, int offset); + double ToDouble(ReadOnlySpan buffer, int offset); /// - double[] ToDouble(byte[] buffer, int offset, int len); + double[] ToDouble(ReadOnlySpan buffer, int offset, int len); /// /// 从缓存中提取short结果,需要指定起始的字节索引,按照字节为单位,一个short占用两个字节
@@ -222,10 +223,10 @@ public interface IThingsGatewayBitConverter /// 缓存数据 /// 索引位置 /// short对象 - short ToInt16(byte[] buffer, int offset); + short ToInt16(ReadOnlySpan buffer, int offset); /// - short[] ToInt16(byte[] buffer, int offset, int len); + short[] ToInt16(ReadOnlySpan buffer, int offset, int len); /// /// 从缓存中提取int结果,需要指定起始的字节索引,按照字节为单位,一个int占用四个字节
@@ -233,10 +234,10 @@ public interface IThingsGatewayBitConverter /// 缓存数据 /// 索引位置 /// int对象 - int ToInt32(byte[] buffer, int offset); + int ToInt32(ReadOnlySpan buffer, int offset); /// - int[] ToInt32(byte[] buffer, int offset, int len); + int[] ToInt32(ReadOnlySpan buffer, int offset, int len); /// /// 从缓存中提取long结果,需要指定起始的字节索引,按照字节为单位,一个long占用八个字节
@@ -244,10 +245,10 @@ public interface IThingsGatewayBitConverter /// 缓存数据 /// 索引位置 /// long对象 - long ToInt64(byte[] buffer, int offset); + long ToInt64(ReadOnlySpan buffer, int offset); /// - long[] ToInt64(byte[] buffer, int offset, int len); + long[] ToInt64(ReadOnlySpan buffer, int offset, int len); /// /// 从缓存中提取float结果,需要指定起始的字节索引,按照字节为单位,一个float占用四个字节 @@ -255,10 +256,10 @@ public interface IThingsGatewayBitConverter /// 缓存对象 /// 索引位置 /// float对象 - float ToSingle(byte[] buffer, int offset); + float ToSingle(ReadOnlySpan buffer, int offset); /// - float[] ToSingle(byte[] buffer, int offset, int len); + float[] ToSingle(ReadOnlySpan buffer, int offset, int len); /// /// 从缓存中的部分字节数组转化为string结果,使用指定的编码,指定起始的字节索引,字节长度信息。
@@ -267,7 +268,7 @@ public interface IThingsGatewayBitConverter /// 索引位置 /// byte数组长度 /// string对象 - string ToString(byte[] buffer, int offset, int length); + string ToString(ReadOnlySpan buffer, int offset, int length); /// /// 从缓存中提取ushort结果,需要指定起始的字节索引,按照字节为单位,一个ushort占用两个字节
@@ -275,10 +276,10 @@ public interface IThingsGatewayBitConverter /// 缓存数据 /// 索引位置 /// ushort对象 - ushort ToUInt16(byte[] buffer, int offset); + ushort ToUInt16(ReadOnlySpan buffer, int offset); /// - ushort[] ToUInt16(byte[] buffer, int offset, int len); + ushort[] ToUInt16(ReadOnlySpan buffer, int offset, int len); /// /// 从缓存中提取uint结果,需要指定起始的字节索引,按照字节为单位,一个uint占用四个字节
@@ -286,10 +287,10 @@ public interface IThingsGatewayBitConverter /// 缓存数据 /// 索引位置 /// uint对象 - uint ToUInt32(byte[] buffer, int offset); + uint ToUInt32(ReadOnlySpan buffer, int offset); /// - uint[] ToUInt32(byte[] buffer, int offset, int len); + uint[] ToUInt32(ReadOnlySpan buffer, int offset, int len); /// /// 从缓存中提取ulong结果,需要指定起始的字节索引,按照字节为单位,一个ulong占用八个字节 @@ -297,10 +298,10 @@ public interface IThingsGatewayBitConverter /// 缓存数据 /// 索引位置 /// ulong对象 - ulong ToUInt64(byte[] buffer, int offset); + ulong ToUInt64(ReadOnlySpan buffer, int offset); /// - ulong[] ToUInt64(byte[] buffer, int offset, int len); + ulong[] ToUInt64(ReadOnlySpan buffer, int offset, int len); /// /// 转换为指定端模式的数据。 @@ -308,7 +309,7 @@ public interface IThingsGatewayBitConverter /// /// /// - decimal ToDecimal(byte[] buffer, int offset); + decimal ToDecimal(ReadOnlySpan buffer, int offset); /// /// 转换为指定端模式的Char数据。 @@ -316,7 +317,7 @@ public interface IThingsGatewayBitConverter /// /// /// - char ToChar(byte[] buffer, int offset); + char ToChar(ReadOnlySpan buffer, int offset); /// /// 从缓存中提取decimal结果,需要指定起始的字节索引,按照字节为单位,一个decimal占用16个字节 @@ -325,7 +326,7 @@ public interface IThingsGatewayBitConverter /// 索引位置 /// length /// decimal对象 - decimal[] ToDecimal(byte[] buffer, int offset, int length); + decimal[] ToDecimal(ReadOnlySpan buffer, int offset, int length); IThingsGatewayBitConverter GetTransByAddress(string? registerAddress); #endregion ToValue diff --git a/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverter.cs b/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverter.cs index 2c09d3f3d..1e01c0f6c 100644 --- a/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverter.cs +++ b/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverter.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -16,6 +16,8 @@ using System.Text; using ThingsGateway.Foundation.Extension.Generic; using ThingsGateway.Foundation.Extension.String; +using TouchSocket.Resources; + namespace ThingsGateway.Foundation; /// @@ -225,22 +227,22 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter #region GetBytes /// - public virtual byte[] GetBytes(string value) + public virtual Memory GetBytes(string value) { if (string.IsNullOrEmpty(value)) { - return Array.Empty(); + return Memory.Empty; } if (StringLength != null) { if (BcdFormat != null) { - byte[] bytes = DataTransUtil.GetBytesFromBCD(value, BcdFormat.Value); + var bytes = DataTransUtil.GetBytesFromBCD(value, BcdFormat.Value).AsMemory(); return IsStringReverseByteWord ? bytes.BytesReverseByWord().ArrayExpandToLength(StringLength.Value) : bytes.ArrayExpandToLength(StringLength.Value); } else { - byte[] bytes = EncodingValue.GetBytes(value); + var bytes = Encoding.GetBytes(value).AsMemory(); return IsStringReverseByteWord ? bytes.BytesReverseByWord().ArrayExpandToLength(StringLength.Value) : bytes.ArrayExpandToLength(StringLength.Value); } } @@ -248,323 +250,204 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter { if (BcdFormat != null) { - byte[] bytes = DataTransUtil.GetBytesFromBCD(value, BcdFormat.Value); + var bytes = DataTransUtil.GetBytesFromBCD(value, BcdFormat.Value).AsMemory(); return IsStringReverseByteWord ? bytes.BytesReverseByWord() : bytes; } else { - byte[] bytes = EncodingValue.GetBytes(value); + var bytes = Encoding.GetBytes(value).AsMemory(); return IsStringReverseByteWord ? bytes.BytesReverseByWord() : bytes; } } } + + /// - public virtual byte[] GetBytes(short[] value) + public virtual byte[] GetBytes(decimal value) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 2); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(value); } /// - public virtual byte[] GetBytes(ushort[] value) + public virtual byte[] GetBytes(char value) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 2); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(value); } /// - public virtual byte[] GetBytes(int[] value) + public virtual byte[] GetBytes(bool value) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 4); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(value); } /// - public virtual byte[] GetBytes(uint[] value) + public virtual byte[] GetBytes(short value) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 4); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(value); } /// - public virtual byte[] GetBytes(long[] value) + public virtual byte[] GetBytes(ushort value) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 4); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(value); } /// - public virtual byte[] GetBytes(ulong[] value) + public virtual byte[] GetBytes(ReadOnlySpan values) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 4); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(values); } /// - public virtual byte[] GetBytes(float[] value) + public virtual byte[] GetBytes(ReadOnlySpan value) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 4); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(value); } /// - public virtual byte[] GetBytes(double[] value) + public virtual byte[] GetBytes(ReadOnlySpan value) { - using ValueByteBlock byteBlock = new ValueByteBlock(value.Length * 4); - for (int index = 0; index < value.Length; ++index) - { - byte[] bytes = GetBytes(value[index]); - byteBlock.Write(bytes); - } - return byteBlock.ToArray(); + return GetTBytes(value); } + /// + public virtual byte[] GetBytes(ReadOnlySpan value) + { + return GetTBytes(value); + } + + /// + public virtual byte[] GetBytes(ReadOnlySpan value) + { + return GetTBytes(value); + } + + /// + public virtual byte[] GetBytes(ReadOnlySpan value) + { + return GetTBytes(value); + } + + /// + public virtual byte[] GetBytes(ReadOnlySpan value) + { + return GetTBytes(value); + } + + /// + public virtual byte[] GetBytes(ReadOnlySpan value) + { + return GetTBytes(value); + } + + /// + public virtual byte[] GetBytes(ReadOnlySpan value) + { + return GetTBytes(value); + } + /// + public virtual byte[] GetBytes(ReadOnlySpan value) + { + return GetTBytes(value); + } #endregion GetBytes /// - public virtual string ToString(byte[] buffer, int offset, int len) + public virtual string ToString(ReadOnlySpan buffer, int offset, int length) { + buffer = buffer.Slice(offset, length); if (BcdFormat != null) { - return IsStringReverseByteWord ? DataTransUtil.GetBcdValue(new ReadOnlySpan(buffer, offset, len).ToArray().BytesReverseByWord(), BcdFormat.Value) : DataTransUtil.GetBcdValue(new ReadOnlySpan(buffer, offset, len), BcdFormat.Value); + return IsStringReverseByteWord ? DataTransUtil.GetBcdValue(buffer.BytesReverseByWord(), BcdFormat.Value) : DataTransUtil.GetBcdValue(buffer, BcdFormat.Value); } else { return IsStringReverseByteWord ? - EncodingValue.GetString(new ReadOnlySpan(buffer, offset, len).ToArray().BytesReverseByWord()).TrimEnd().Replace($"\0", "") : - EncodingValue.GetString(buffer, offset, len).TrimEnd().Replace($"\0", ""); + buffer.BytesReverseByWord().ToString(Encoding).TrimEnd().Replace($"\0", "") : + buffer.ToString(Encoding).TrimEnd().Replace($"\0", ""); } } /// - public virtual bool ToBoolean(byte[] buffer, int offset, bool isReverse) + public virtual bool ToBoolean(ReadOnlySpan buffer, int offset, bool isReverse) { - byte[] bytes; + ReadOnlySpan bytes; if (isReverse) bytes = buffer.BytesReverseByWord(); else - bytes = buffer.CopyArray(); + bytes = buffer; return bytes.GetBoolByIndex(offset); } /// - public virtual byte ToByte(byte[] buffer, int offset) + public virtual byte ToByte(ReadOnlySpan buffer, int offset) { return buffer[offset]; } /// - public virtual short ToInt16(byte[] buffer, int offset) + public virtual char ToChar(ReadOnlySpan buffer, int offset) { - return TouchSocketBitConverter.ToInt16(buffer, offset); + return To(buffer.Slice(offset)); } /// - public virtual int ToInt32(byte[] buffer, int offset) + public virtual decimal ToDecimal(ReadOnlySpan buffer, int offset) { - if (buffer.Length - offset < 4) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - unsafe - { - fixed (byte* p = &buffer[offset]) - { - if (DataFormat == DataFormatEnum.DCBA) - { - return Unsafe.Read(p); - } - else - { - ByteTransDataFormat4_Net6(ref buffer[offset]); - var v = Unsafe.Read(p); - ByteTransDataFormat4_Net6(ref buffer[offset]); - return v; - } - } - } + return To(buffer.Slice(offset)); } /// - public virtual long ToInt64(byte[] buffer, int offset) + public virtual short ToInt16(ReadOnlySpan buffer, int offset) { - if (buffer.Length - offset < 8) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - unsafe - { - fixed (byte* p = &buffer[offset]) - { - if (DataFormat == DataFormatEnum.DCBA) - { - return Unsafe.Read(p); - } - else - { - ByteTransDataFormat8_Net6(ref buffer[offset]); - var v = Unsafe.Read(p); - ByteTransDataFormat8_Net6(ref buffer[offset]); - return v; - } - } - } + return To(buffer.Slice(offset)); } /// - public virtual ushort ToUInt16(byte[] buffer, int offset) + public virtual int ToInt32(ReadOnlySpan buffer, int offset) { - return TouchSocketBitConverter.ToUInt16(buffer, offset); + return To(buffer.Slice(offset)); } /// - public virtual uint ToUInt32(byte[] buffer, int offset) + public virtual long ToInt64(ReadOnlySpan buffer, int offset) { - if (buffer.Length - offset < 4) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - unsafe - { - fixed (byte* p = &buffer[offset]) - { - if (DataFormat == DataFormatEnum.DCBA) - { - return Unsafe.Read(p); - } - else - { - ByteTransDataFormat4_Net6(ref buffer[offset]); - var v = Unsafe.Read(p); - ByteTransDataFormat4_Net6(ref buffer[offset]); - return v; - } - } - } + return To(buffer.Slice(offset)); } /// - public virtual ulong ToUInt64(byte[] buffer, int offset) + public virtual ushort ToUInt16(ReadOnlySpan buffer, int offset) { - if (buffer.Length - offset < 8) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - unsafe - { - fixed (byte* p = &buffer[offset]) - { - if (DataFormat == DataFormatEnum.DCBA) - { - return Unsafe.Read(p); - } - else - { - ByteTransDataFormat8_Net6(ref buffer[offset]); - var v = Unsafe.Read(p); - ByteTransDataFormat8_Net6(ref buffer[offset]); - return v; - } - } - } + return To(buffer.Slice(offset)); } /// - public virtual float ToSingle(byte[] buffer, int offset) + public virtual uint ToUInt32(ReadOnlySpan buffer, int offset) { - if (buffer.Length - offset < 4) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - unsafe - { - fixed (byte* p = &buffer[offset]) - { - if (DataFormat == DataFormatEnum.DCBA) - { - return Unsafe.Read(p); - } - else - { - ByteTransDataFormat4_Net6(ref buffer[offset]); - var v = Unsafe.Read(p); - ByteTransDataFormat4_Net6(ref buffer[offset]); - return v; - } - } - } + return To(buffer.Slice(offset)); } /// - public virtual double ToDouble(byte[] buffer, int offset) + public virtual ulong ToUInt64(ReadOnlySpan buffer, int offset) { - if (buffer.Length - offset < 8) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - unsafe - { - fixed (byte* p = &buffer[offset]) - { - if (DataFormat == DataFormatEnum.DCBA) - { - return Unsafe.Read(p); - } - else - { - ByteTransDataFormat8_Net6(ref buffer[offset]); - var v = Unsafe.Read(p); - ByteTransDataFormat8_Net6(ref buffer[offset]); - return v; - } - } - } + return To(buffer.Slice(offset)); } /// - public virtual bool[] ToBoolean(byte[] buffer, int offset, int len, bool isReverse = false) + public virtual float ToSingle(ReadOnlySpan buffer, int offset) + { + return To(buffer.Slice(offset)); + } + + /// + public virtual double ToDouble(ReadOnlySpan buffer, int offset) + { + return To(buffer.Slice(offset)); + } + + /// + public virtual bool[] ToBoolean(ReadOnlySpan buffer, int offset, int len, bool isReverse = false) { bool[] result = new bool[len]; for (int i = 0; i < result.Length; i++) @@ -575,15 +458,15 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual byte[] ToByte(byte[] buffer, int offset, int length) + public virtual byte[] ToByte(ReadOnlySpan buffer, int offset, int length) { byte[] bytes = new byte[length]; - Array.Copy(buffer, offset, bytes, 0, bytes.Length); + buffer.Slice(offset, length).CopyTo(bytes); return bytes; } /// - public virtual double[] ToDouble(byte[] buffer, int offset, int len) + public virtual double[] ToDouble(ReadOnlySpan buffer, int offset, int len) { double[] numArray = new double[len]; for (int index = 0; index < len; ++index) @@ -594,7 +477,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual short[] ToInt16(byte[] buffer, int offset, int len) + public virtual short[] ToInt16(ReadOnlySpan buffer, int offset, int len) { short[] numArray = new short[len]; for (int index = 0; index < len; ++index) @@ -605,7 +488,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual int[] ToInt32(byte[] buffer, int offset, int len) + public virtual int[] ToInt32(ReadOnlySpan buffer, int offset, int len) { int[] numArray = new int[len]; for (int index = 0; index < len; ++index) @@ -616,7 +499,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual long[] ToInt64(byte[] buffer, int offset, int len) + public virtual long[] ToInt64(ReadOnlySpan buffer, int offset, int len) { long[] numArray = new long[len]; for (int index = 0; index < len; ++index) @@ -627,7 +510,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual float[] ToSingle(byte[] buffer, int offset, int len) + public virtual float[] ToSingle(ReadOnlySpan buffer, int offset, int len) { float[] numArray = new float[len]; for (int index = 0; index < len; ++index) @@ -638,7 +521,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual ushort[] ToUInt16(byte[] buffer, int offset, int len) + public virtual ushort[] ToUInt16(ReadOnlySpan buffer, int offset, int len) { ushort[] numArray = new ushort[len]; for (int index = 0; index < len; ++index) @@ -649,7 +532,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual uint[] ToUInt32(byte[] buffer, int offset, int len) + public virtual uint[] ToUInt32(ReadOnlySpan buffer, int offset, int len) { uint[] numArray = new uint[len]; for (int index = 0; index < len; ++index) @@ -660,7 +543,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual ulong[] ToUInt64(byte[] buffer, int offset, int len) + public virtual ulong[] ToUInt64(ReadOnlySpan buffer, int offset, int len) { ulong[] numArray = new ulong[len]; for (int index = 0; index < len; ++index) @@ -671,7 +554,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } /// - public virtual decimal[] ToDecimal(byte[] buffer, int offset, int len) + public virtual decimal[] ToDecimal(ReadOnlySpan buffer, int offset, int len) { decimal[] numArray = new decimal[len]; for (int index = 0; index < len; ++index) @@ -681,248 +564,182 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter return numArray; } + + /// - public virtual byte[] GetBytes(decimal value) + public virtual byte[] GetTBytes(ReadOnlySpan value) where T : unmanaged { - var bytes = new byte[16]; - Unsafe.As(ref bytes[0]) = value; - if (DataFormat != DataFormatEnum.DCBA) + var size = Unsafe.SizeOf(); + using (ValueByteBlock byteBlock = new ValueByteBlock(value.Length * size)) { - ByteTransDataFormat16_Net6(ref bytes[0]); - } - return bytes; - } - - /// - public virtual byte[] GetBytes(char value) - { - return TouchSocketBitConverter.GetBytes(value); - } - - /// - public virtual byte[] GetBytes(bool value) - { - return TouchSocketBitConverter.GetBytes(value); - } - - /// - public virtual byte[] GetBytes(bool[] values) - { - return TouchSocketBitConverter.GetBytes(values); - } - - /// - public virtual byte[] GetBytes(short value) - { - return TouchSocketBitConverter.GetBytes(value); - } - - /// - public virtual byte[] GetBytes(ushort value) - { - return TouchSocketBitConverter.GetBytes(value); - } - - /// - public virtual byte[] GetBytes(int value) - { - var bytes = BitConverter.GetBytes(value); - - if (DataFormat != DataFormatEnum.DCBA) - bytes = ByteTransDataFormat4(bytes, 0); - - return bytes; - } - - /// - public virtual byte[] GetBytes(uint value) - { - var bytes = BitConverter.GetBytes(value); - - if (DataFormat != DataFormatEnum.DCBA) - bytes = ByteTransDataFormat4(bytes, 0); - - return bytes; - } - - /// - public virtual byte[] GetBytes(long value) - { - var bytes = BitConverter.GetBytes(value); - - if (DataFormat != DataFormatEnum.DCBA) - bytes = ByteTransDataFormat8(bytes, 0); - - return bytes; - } - - /// - public virtual byte[] GetBytes(ulong value) - { - var bytes = BitConverter.GetBytes(value); - - if (DataFormat != DataFormatEnum.DCBA) - bytes = ByteTransDataFormat8(bytes, 0); - - return bytes; - } - - /// - public virtual byte[] GetBytes(float value) - { - var bytes = BitConverter.GetBytes(value); - - if (DataFormat != DataFormatEnum.DCBA) - bytes = ByteTransDataFormat4(bytes, 0); - - return bytes; - } - - /// - public virtual byte[] GetBytes(double value) - { - var bytes = BitConverter.GetBytes(value); - - if (DataFormat != DataFormatEnum.DCBA) - bytes = ByteTransDataFormat8(bytes, 0); - - return bytes; - } - - /// - public virtual decimal ToDecimal(byte[] buffer, int offset) - { - if (buffer.Length - offset < 16) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - unsafe - { - fixed (byte* p = &buffer[offset]) + for (int index = 0; index < value.Length; ++index) { - if (DataFormat == DataFormatEnum.DCBA) + var bytes = GetTBytes(value[index]); + byteBlock.Write(bytes); + } + return byteBlock.ToArray(); + } + } + + + private byte[] GetTBytes(T value) where T : unmanaged + { + var size = Unsafe.SizeOf(); + var bytes = new byte[size]; + this.WriteBytes(bytes, value); + return bytes; + } + + + /// + /// 将指定值的字节表示形式写入到指定的字节跨度中。 + /// + /// 要写入的值的类型,必须是非托管类型。 + /// 要写入字节的目标跨度。 + /// 要写入的值。 + /// 写入的字节数。 + private unsafe int WriteBytes(Span span, T value) where T : unmanaged + { + var size = sizeof(T); + + if (span.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(span.Length), TouchSocketCoreResource.ValueLessThan.Format(nameof(span.Length), span.Length, size)); + } + + if (size == 2) + { + return TouchSocketBitConverter.WriteBytes(span, value); + } + + Unsafe.As(ref span[0]) = value; + + if (size == 1) + { + // 对于单字节类型,不需要转换 + return size; + } + + if (!TouchSocketBitConverter.IsSameOfSet()) + { + + if (size == 4) + { + this.ByteTransDataFormat4_Net6(ref span[0]); + } + else if (size == 8) + { + this.ByteTransDataFormat8_Net6(ref span[0]); + } + else if (size == 16) + { + this.ByteTransDataFormat16_Net6(ref span[0]); + } + else + { + throw new NotSupportedException(size.ToString()); + } + } + + return size; + } + + /// + /// 将字节跨度转换为指定类型 + /// + /// 要转换成的类型 + /// 要转换的字节跨度 + /// 转换后的值 + /// 当字节跨度长度不足以表示类型T时抛出 + /// 当类型T不支持时抛出 + public unsafe T To(ReadOnlySpan span) where T : unmanaged + { + var size = sizeof(T); + if (span.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(span.Length), TouchSocketCoreResource.ValueLessThan.Format(nameof(span.Length), span.Length, size)); + } + + if (size == 2) + { + return TouchSocketBitConverter.To(span); + } + + fixed (byte* p = &span[0]) + { + if (this.TouchSocketBitConverter.IsSameOfSet()) + { + return Unsafe.Read(p); + } + else + { + if (size == 4) { - return Unsafe.Read(p); + this.ByteTransDataFormat4_Net6(p); + var v = Unsafe.Read(p); + this.ByteTransDataFormat4_Net6(p); + return v; + } + else if (size == 8) + { + this.ByteTransDataFormat8_Net6(p); + var v = Unsafe.Read(p); + this.ByteTransDataFormat8_Net6(p); + return v; + } + else if (size == 16) + { + this.ByteTransDataFormat16_Net6(p); + var v = Unsafe.Read(p); + this.ByteTransDataFormat16_Net6(p); + return v; } else { - ByteTransDataFormat16_Net6(ref buffer[offset]); - var v = Unsafe.Read(p); - ByteTransDataFormat16_Net6(ref buffer[offset]); - return v; + throw new NotSupportedException(size.ToString()); } } } } /// - public virtual char ToChar(byte[] buffer, int offset) + public virtual byte[] GetBytes(int value) { - return TouchSocketBitConverter.ToChar(buffer, offset); + return GetTBytes(value); } - #region Tool - - /// 反转多字节的数据信息 - /// 数据字节 - /// 起始索引,默认值为0 - /// 实际字节信息 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] ByteTransDataFormat4(byte[] value, int offset) + /// + public virtual byte[] GetBytes(uint value) { - var numArray = new byte[4]; - switch (DataFormat) - { - case DataFormatEnum.ABCD: - numArray[0] = value[offset + 3]; - numArray[1] = value[offset + 2]; - numArray[2] = value[offset + 1]; - numArray[3] = value[offset]; - break; - - case DataFormatEnum.BADC: - numArray[0] = value[offset + 2]; - numArray[1] = value[offset + 3]; - numArray[2] = value[offset]; - numArray[3] = value[offset + 1]; - break; - - case DataFormatEnum.CDAB: - numArray[0] = value[offset + 1]; - numArray[1] = value[offset]; - numArray[2] = value[offset + 3]; - numArray[3] = value[offset + 2]; - break; - - case DataFormatEnum.DCBA: - numArray[0] = value[offset]; - numArray[1] = value[offset + 1]; - numArray[2] = value[offset + 2]; - numArray[3] = value[offset + 3]; - break; - } - return numArray; + return GetTBytes(value); } - /// 反转多字节的数据信息 - /// 数据字节 - /// 起始索引,默认值为0 - /// 实际字节信息 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[] ByteTransDataFormat8(byte[] value, int offset) + /// + public virtual byte[] GetBytes(long value) { - var numArray = new byte[8]; - switch (DataFormat) - { - case DataFormatEnum.ABCD: - numArray[0] = value[offset + 7]; - numArray[1] = value[offset + 6]; - numArray[2] = value[offset + 5]; - numArray[3] = value[offset + 4]; - numArray[4] = value[offset + 3]; - numArray[5] = value[offset + 2]; - numArray[6] = value[offset + 1]; - numArray[7] = value[offset]; - break; - - case DataFormatEnum.BADC: - numArray[0] = value[offset + 6]; - numArray[1] = value[offset + 7]; - numArray[2] = value[offset + 4]; - numArray[3] = value[offset + 5]; - numArray[4] = value[offset + 2]; - numArray[5] = value[offset + 3]; - numArray[6] = value[offset]; - numArray[7] = value[offset + 1]; - break; - - case DataFormatEnum.CDAB: - numArray[0] = value[offset + 1]; - numArray[1] = value[offset]; - numArray[2] = value[offset + 3]; - numArray[3] = value[offset + 2]; - numArray[4] = value[offset + 5]; - numArray[5] = value[offset + 4]; - numArray[6] = value[offset + 7]; - numArray[7] = value[offset + 6]; - break; - - case DataFormatEnum.DCBA: - numArray[0] = value[offset]; - numArray[1] = value[offset + 1]; - numArray[2] = value[offset + 2]; - numArray[3] = value[offset + 3]; - numArray[4] = value[offset + 4]; - numArray[5] = value[offset + 5]; - numArray[6] = value[offset + 6]; - numArray[7] = value[offset + 7]; - break; - } - return numArray; + return GetTBytes(value); } - #endregion Tool + /// + public virtual byte[] GetBytes(ulong value) + { + return GetTBytes(value); + } + + /// + public virtual byte[] GetBytes(float value) + { + return GetTBytes(value); + } + + /// + public virtual byte[] GetBytes(double value) + { + return GetTBytes(value); + } + + + #region Tool @@ -1050,5 +867,113 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void ByteTransDataFormat4_Net6(byte* p) + { + var a = Unsafe.ReadUnaligned(p); + var b = Unsafe.ReadUnaligned(p + 1); + var c = Unsafe.ReadUnaligned(p + 2); + var d = Unsafe.ReadUnaligned(p + 3); + + switch (this.DataFormat) + { + case DataFormatEnum.ABCD: + Unsafe.WriteUnaligned(p, d); + Unsafe.WriteUnaligned(p + 1, c); + Unsafe.WriteUnaligned(p + 2, b); + Unsafe.WriteUnaligned(p + 3, a); + break; + + case DataFormatEnum.BADC: + Unsafe.WriteUnaligned(p, c); + Unsafe.WriteUnaligned(p + 1, d); + Unsafe.WriteUnaligned(p + 2, a); + Unsafe.WriteUnaligned(p + 3, b); + break; + + case DataFormatEnum.CDAB: + Unsafe.WriteUnaligned(p, b); + Unsafe.WriteUnaligned(p + 1, a); + Unsafe.WriteUnaligned(p + 2, d); + Unsafe.WriteUnaligned(p + 3, c); + break; + + case DataFormatEnum.DCBA: + return; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void ByteTransDataFormat8_Net6(byte* p) + { + var a = Unsafe.ReadUnaligned(p); + var b = Unsafe.ReadUnaligned(p + 1); + var c = Unsafe.ReadUnaligned(p + 2); + var d = Unsafe.ReadUnaligned(p + 3); + var e = Unsafe.ReadUnaligned(p + 4); + var f = Unsafe.ReadUnaligned(p + 5); + var g = Unsafe.ReadUnaligned(p + 6); + var h = Unsafe.ReadUnaligned(p + 7); + + switch (this.DataFormat) + { + case DataFormatEnum.ABCD: + Unsafe.WriteUnaligned(p, h); + Unsafe.WriteUnaligned(p + 1, g); + Unsafe.WriteUnaligned(p + 2, f); + Unsafe.WriteUnaligned(p + 3, e); + Unsafe.WriteUnaligned(p + 4, d); + Unsafe.WriteUnaligned(p + 5, c); + Unsafe.WriteUnaligned(p + 6, b); + Unsafe.WriteUnaligned(p + 7, a); + break; + + case DataFormatEnum.BADC: + Unsafe.WriteUnaligned(p, g); + Unsafe.WriteUnaligned(p + 1, h); + Unsafe.WriteUnaligned(p + 2, e); + Unsafe.WriteUnaligned(p + 3, f); + Unsafe.WriteUnaligned(p + 4, c); + Unsafe.WriteUnaligned(p + 5, d); + Unsafe.WriteUnaligned(p + 6, a); + Unsafe.WriteUnaligned(p + 7, b); + break; + + case DataFormatEnum.CDAB: + Unsafe.WriteUnaligned(p, b); + Unsafe.WriteUnaligned(p + 1, a); + Unsafe.WriteUnaligned(p + 2, d); + Unsafe.WriteUnaligned(p + 3, c); + Unsafe.WriteUnaligned(p + 4, f); + Unsafe.WriteUnaligned(p + 5, e); + Unsafe.WriteUnaligned(p + 6, h); + Unsafe.WriteUnaligned(p + 7, g); + break; + + case DataFormatEnum.DCBA: + break; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void ByteTransDataFormat16_Net6(byte* p) + { + switch (this.DataFormat) + { + case DataFormatEnum.ABCD: + var span = new Span(p, 16); + span.Reverse(); + break; + + case DataFormatEnum.DCBA: + return; + + default: + case DataFormatEnum.CDAB: + case DataFormatEnum.BADC: + throw new NotSupportedException(); + } + } + #endregion Tool } diff --git a/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverterExtension.cs b/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverterExtension.cs index 77f566beb..ca2680df8 100644 --- a/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverterExtension.cs +++ b/src/Foundation/ThingsGateway.Foundation/Trans/ThingsGatewayBitConverterExtension.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -26,9 +26,9 @@ public static class ThingsGatewayBitConverterExtension /// /// 根据数据类型获取字节数组 /// - public static byte[] GetBytesFormData(this IThingsGatewayBitConverter byteConverter, JToken value, DataTypeEnum dataType, int arrayLength = 1) + public static ReadOnlyMemory 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()); + case DataTypeEnum.Decimal: + return byteConverter.GetBytes(value.ToObject()); + case DataTypeEnum.String: - default: - List bytes = new(); + List> bytes = new(); + String[] strings = value.ToObject(); - 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()); + case DataTypeEnum.Decimal: + return byteConverter.GetBytes(value.ToObject()); + case DataTypeEnum.String: - default: return byteConverter.GetBytes(value.ToObject()); + + 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 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) diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs b/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs index 48d32be61..d1a12f634 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs @@ -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) + }; + } } + } diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs b/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs index 4313944c6..911eacf52 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 @@ -22,8 +22,9 @@ public static class DataTransUtil /// /// 字节数组 /// 分割符 + /// 指定在何处换行,设为0则不换行 /// 返回的字符串 - public static string ByteToHexString(byte[] InBytes, char segment = default) => ByteToHexString(InBytes, segment, 0); + public static string ByteToHexString(ReadOnlySpan InBytes, char segment = default, int newLineCount = 0) => ByteToHexString(InBytes, 0, InBytes.Length, segment, newLineCount); /// /// 将字节数组转换为十六进制表示的字符串 @@ -34,60 +35,30 @@ public static class DataTransUtil /// 用于分隔每个字节的字符 /// 指定在何处换行,设为0则不换行 /// 转换后的十六进制字符串 - public static string ByteToHexString(byte[] inBytes, int offset, int length, char segment = default, int newLineCount = 0) + public static string ByteToHexString(ReadOnlySpan 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(); } - /// - /// 将字节数组转换为十六进制表示的字符串 - /// - /// 输入的字节数组 - /// 用于分隔每个字节的字符 - /// 指定在何处换行,设为0则不换行 - /// 转换后的十六进制字符串 - public static string ByteToHexString(ReadOnlySpan 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(); - } /// /// 获取Bcd值 @@ -286,33 +257,31 @@ public static class DataTransUtil /// 转换后的字节数组 public static byte[] GetBytesFromBCD(string value, BcdFormatEnum format) { - // 如果输入字符串为空,则返回空字节数组 if (string.IsNullOrEmpty(value)) { return Array.Empty(); } - // 计算输出字节数组的长度 - 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; } + /// /// 返回bit代表的数据 /// @@ -368,26 +337,35 @@ public static class DataTransUtil /// /// 输入的十六进制字符串 /// 转换后的字节数组 - public static byte[] HexStringToBytes(string hex) + public static Memory HexStringToBytes(string hex) { - // 创建内存流用于存储转换后的字节数据 - using var bytes = new MemoryStream(); + if (string.IsNullOrEmpty(hex)) + return Memory.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(result, 0, byteIndex); } + /// /// 拼接任意个泛型数组为一个总的泛型数组对象。 /// @@ -396,21 +374,75 @@ public static class DataTransUtil /// 拼接之后的最终的结果对象 public static T[] SpliceArray(params T[][] arrays) { - // 初始化一个动态数组用于存储拼接后的结果 - List resultList = new List(); + if (arrays == null || arrays.Length == 0) + return Array.Empty(); - // 遍历输入的数组 - 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[] 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; + } + + + /// + /// 拼接任意个泛型数组为一个总的泛型数组对象。 + /// + /// 数组的类型信息 + /// 任意个长度的数组 + /// 拼接之后的最终的结果对象 + public static Memory SpliceArray(params Memory[] arrays) + { + if (arrays == null || arrays.Length == 0) + return Array.Empty(); + + // 预先计算所有数组的总长度,避免多次扩容 + int totalLength = 0; + foreach (var array in arrays) + { + if (!array.IsEmpty) + totalLength += array.Length; + } + + if (totalLength == 0) + return Array.Empty(); + + // 分配目标数组 + Memory 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; } /// diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/JTokenUtil.cs b/src/Foundation/ThingsGateway.Foundation/Utils/JTokenUtil.cs index a97649dc6..67d595cec 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/JTokenUtil.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/JTokenUtil.cs @@ -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()).ToList(); + return array.Select(x => x.Value()).ToArray(); if (array.All(x => x.Type == JTokenType.Float)) - return array.Select(x => x.Value()).ToList(); + return array.Select(x => x.Value()).ToArray(); if (array.All(x => x.Type == JTokenType.String)) - return array.Select(x => x.Value()).ToList(); + return array.Select(x => x.Value()).ToArray(); if (array.All(x => x.Type == JTokenType.Boolean)) - return array.Select(x => x.Value()).ToList(); + return array.Select(x => x.Value()).ToArray(); if (array.All(x => x.Type == JTokenType.Date)) - return array.Select(x => x.Value()).ToList(); + return array.Select(x => x.Value()).ToArray(); if (array.All(x => x.Type == JTokenType.TimeSpan)) - return array.Select(x => x.Value()).ToList(); + return array.Select(x => x.Value()).ToArray(); if (array.All(x => x.Type == JTokenType.Guid)) - return array.Select(x => x.Value()).ToList(); + return array.Select(x => x.Value()).ToArray(); if (array.All(x => x.Type == JTokenType.Uri)) - return array.Select(x => x.Value()).ToList(); + return array.Select(x => x.Value()).ToArray(); // 否则递归 - return array.Select(x => GetObjectFromJToken(x)).ToList(); + return array.Select(x => GetObjectFromJToken(x)).ToArray(); case JTokenType.Integer: return token.ToObject(); diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/LocalizerUtil.cs b/src/Foundation/ThingsGateway.Foundation/Utils/LocalizerUtil.cs index 21da201c8..6a78807ea 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/LocalizerUtil.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/LocalizerUtil.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Variable/IVariable.cs b/src/Foundation/ThingsGateway.Foundation/Variable/IVariable.cs index 7dba3eecf..20e73197a 100644 --- a/src/Foundation/ThingsGateway.Foundation/Variable/IVariable.cs +++ b/src/Foundation/ThingsGateway.Foundation/Variable/IVariable.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Variable/IVariableSource.cs b/src/Foundation/ThingsGateway.Foundation/Variable/IVariableSource.cs index 330bb3797..650f9bbf5 100644 --- a/src/Foundation/ThingsGateway.Foundation/Variable/IVariableSource.cs +++ b/src/Foundation/ThingsGateway.Foundation/Variable/IVariableSource.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Variable/VariableClass.cs b/src/Foundation/ThingsGateway.Foundation/Variable/VariableClass.cs index b7fc3e5b4..e8b29b376 100644 --- a/src/Foundation/ThingsGateway.Foundation/Variable/VariableClass.cs +++ b/src/Foundation/ThingsGateway.Foundation/Variable/VariableClass.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Foundation/ThingsGateway.Foundation/Variable/VariableSourceClass.cs b/src/Foundation/ThingsGateway.Foundation/Variable/VariableSourceClass.cs index e2c8d36ef..6c67e9a0a 100644 --- a/src/Foundation/ThingsGateway.Foundation/Variable/VariableSourceClass.cs +++ b/src/Foundation/ThingsGateway.Foundation/Variable/VariableSourceClass.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs b/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs index 198f38ea8..cd9e86848 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Common/AsyncReadWriteLock.cs @@ -52,7 +52,7 @@ public class AsyncReadWriteLock { var resetEvent = _readerLock; _readerLock = new(false); - resetEvent.SafeDispose(); + resetEvent.SetAll(); } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/BusinessBase.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/BusinessBase.cs index 9c45272f1..0ee8f6602 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/BusinessBase.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/BusinessBase.cs @@ -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); diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs index 474315df3..3353df5a6 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCache.cs @@ -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(); diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCacheIntervalScript.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCacheIntervalScript.cs index 9426ecee3..cac3ee936 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCacheIntervalScript.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Business/Cache/BusinessBaseWithCacheIntervalScript.cs @@ -26,7 +26,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase protected abstract BusinessPropertyWithCacheIntervalScript _businessPropertyWithCacheIntervalScript { get; } - public virtual List 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(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(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(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()); } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBItem.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBItem.cs index ddc8eba26..a353c572e 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBItem.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBItem.cs @@ -25,6 +25,6 @@ public class CacheDBItem : IPrimaryIdEntity [SugarColumn(IsPrimaryKey = true)] public long Id { get; set; } - [SugarColumn(IsJson = true)] + [SugarColumn(IsJson = true, ColumnDataType = "TEXT")] public T Value { get; set; } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs index 1f89536a7..f9a58ba43 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectBase.cs @@ -207,6 +207,8 @@ public abstract class CollectBase : DriverBase, IRpcDriver { var tasks = new List(); + 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 /// /// 采集驱动读取,读取成功后直接赋值变量 /// - protected virtual ValueTask> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken) + protected virtual ValueTask>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken) { - return ValueTask.FromResult(new OperResult(new NotImplementedException())); + return ValueTask.FromResult(new OperResult>(new NotImplementedException())); } + /// /// 批量写入变量值,需返回变量名称/结果 /// diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs index bf7b2f0cf..583fe3162 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/Collect/CollectFoundationBase.cs @@ -141,23 +141,32 @@ public abstract class CollectFoundationBase : CollectBase /// /// 采集驱动读取,读取成功后直接赋值变量,失败不做处理,注意非通用设备需重写 /// - protected override async ValueTask> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken) + protected override async ValueTask>> 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(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>(prase); + } + + // 返回读取结果 + return read; + } + catch (Exception ex) + { + // 捕获异常并返回失败结果 + return new OperResult>(ex); + } } /// @@ -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) { diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/DriverBase.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/DriverBase.cs index 6adb868ad..e1fd652ca 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/DriverBase.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/DriverBase.cs @@ -289,14 +289,14 @@ public abstract class DriverBase : DisposableObject, IDriver } } - protected internal TaskSchedulerLoop TaskSchedulerLoop; + protected internal TaskSchedulerLoop TaskSchedulerLoop { get; protected set; } /// /// 获取任务 /// /// 取消操作的令牌。 /// 表示异步操作结果的枚举。 - 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 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 插件生命周期 diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/DynamicModelExtension.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/DynamicModelExtension.cs index 507115d4a..075cc6821 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/DynamicModelExtension.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/DynamicModelExtension.cs @@ -79,10 +79,10 @@ public static class DynamicModelExtension return null; // 未找到对应的业务设备Id,返回null } - public static IEnumerable> GroupByKeys(this IEnumerable values, IEnumerable keys) + public static IEnumerable> GroupByKeys(this IEnumerable 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()); diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Model/DataModel.cs b/src/Gateway/ThingsGateway.Gateway.Application/Model/DataModel.cs index 5d2af9b4b..ee639f649 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Model/DataModel.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Model/DataModel.cs @@ -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; } + + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] + [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)] + public long CreateOrgId { get; set; } + } /// @@ -77,7 +83,6 @@ public class VariableBasicData { /// public long Id { get; set; } - /// 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; } + + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] + [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)] + public long CreateOrgId { get; set; } + } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/GatewayMonitor/DeviceManage/DeviceThreadManage.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/GatewayMonitor/DeviceManage/DeviceThreadManage.cs index c49d65e39..84c33fb7e 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/GatewayMonitor/DeviceManage/DeviceThreadManage.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/GatewayMonitor/DeviceManage/DeviceThreadManage.cs @@ -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) diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/GatewayRedundantSerivce.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/GatewayRedundantSerivce.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/GatewayRedundantSerivce.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/GatewayRedundantSerivce.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/IRedundancyHostedService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/IRedundancyHostedService.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/IRedundancyHostedService.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/IRedundancyHostedService.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/IRedundancyService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/IRedundancyService.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/IRedundancyService.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/IRedundancyService.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Model/DeviceDataWithValue.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/Model/DeviceDataWithValue.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Model/DeviceDataWithValue.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/Model/DeviceDataWithValue.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Model/VariableDataWithValue.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/Model/VariableDataWithValue.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Model/VariableDataWithValue.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/Model/VariableDataWithValue.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyHostedService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyHostedService.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyHostedService.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyHostedService.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyOptions.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyOptions.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyOptions.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyOptions.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyService.cs similarity index 100% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyService.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyService.cs diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyTask.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyTask.cs similarity index 97% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyTask.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyTask.cs index e878df7ad..0d899d1d4 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/RedundancyTask.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundancyTask.cs @@ -96,7 +96,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable { // 将 GlobalData.CollectDevices 和 GlobalData.Variables 同步到从站 await item.GetDmtpRpcActor().InvokeAsync( - nameof(ReverseCallbackServer.UpData), null, waitInvoke, deviceDataWithValues.AdaptListDeviceDataWithValue()).ConfigureAwait(false); + nameof(RedundantRpcServer.UpData), null, waitInvoke, deviceDataWithValues.AdaptListDeviceDataWithValue()).ConfigureAwait(false); } LogMessage?.LogTrace($"{item.GetIPPort()} Update StandbyStation data success"); } @@ -336,7 +336,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable .ConfigureContainer(a => { a.AddLogger(LogMessage); - a.AddRpcStore(store => store.RegisterServer(new ReverseCallbackServer(this))); + a.AddRpcStore(store => store.RegisterServer(new RedundantRpcServer(this))); }) .ConfigurePlugins(a => { @@ -365,7 +365,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable .ConfigureContainer(a => { a.AddLogger(LogMessage); - a.AddRpcStore(store => store.RegisterServer(new ReverseCallbackServer(this))); + a.AddRpcStore(store => store.RegisterServer(new RedundantRpcServer(this))); }) .ConfigurePlugins(a => { @@ -470,7 +470,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable if (variableBatch.Count >= maxBatchSize) { // 发送一批 - await client.GetDmtpRpcActor().InvokeAsync(nameof(ReverseCallbackServer.SyncData), null, invokeOption, channelBatch.ToList(), deviceBatch.ToList(), variableBatch).ConfigureAwait(false); + await client.GetDmtpRpcActor().InvokeAsync(nameof(RedundantRpcServer.SyncData), null, invokeOption, channelBatch.ToList(), deviceBatch.ToList(), variableBatch).ConfigureAwait(false); variableBatch.Clear(); channelBatch.Remove(channel); @@ -482,7 +482,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable // 发送最后剩余的一批 if (variableBatch.Count > 0) { - await client.GetDmtpRpcActor().InvokeAsync(nameof(ReverseCallbackServer.SyncData), null, invokeOption, channelBatch.ToList(), deviceBatch.ToList(), variableBatch).ConfigureAwait(false); + await client.GetDmtpRpcActor().InvokeAsync(nameof(RedundantRpcServer.SyncData), null, invokeOption, channelBatch.ToList(), deviceBatch.ToList(), variableBatch).ConfigureAwait(false); } LogMessage?.LogTrace($"ForcedSync data success"); @@ -566,7 +566,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable { return await _tcpDmtpClient.GetDmtpRpcActor() .InvokeTAsync>>>( - nameof(ReverseCallbackServer.Rpc), invokeOption, deviceDatas) + nameof(RedundantRpcServer.Rpc), invokeOption, deviceDatas) .ConfigureAwait(false); } private async Task InvokeRpcServerAsync( @@ -607,7 +607,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable try { var data = await client.GetDmtpRpcActor().InvokeTAsync>>>( - nameof(ReverseCallbackServer.Rpc), invokeOption, variableDatas) + nameof(RedundantRpcServer.Rpc), invokeOption, variableDatas) .ConfigureAwait(false); dataResult.AddRange(data); diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/ReverseCallbackServer.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundantRpcServer.cs similarity index 94% rename from src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/ReverseCallbackServer.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundantRpcServer.cs index c7e8f0ee0..519c75c6e 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Management/ReverseCallbackServer.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/Redundant/RedundantRpcServer.cs @@ -17,10 +17,10 @@ using TouchSocket.Sockets; namespace ThingsGateway.Management; -internal sealed partial class ReverseCallbackServer : SingletonRpcServer +internal sealed partial class RedundantRpcServer : SingletonRpcServer { RedundancyTask RedundancyTask; - public ReverseCallbackServer(RedundancyTask redundancyTask) + public RedundantRpcServer(RedundancyTask redundancyTask) { RedundancyTask = redundancyTask; } @@ -149,6 +149,6 @@ internal sealed partial class ReverseCallbackServer : SingletonRpcServer [DmtpRpc(MethodInvoke = true)] public Task>> Rpc(ICallContext callContext, Dictionary> deviceDatas, CancellationToken cancellationToken) { - return GlobalData.RpcService.InvokeDeviceMethodAsync("Management" + $"[{(callContext.Caller is ITcpSession tcpSession ? tcpSession.GetIPPort() : string.Empty)}]", deviceDatas, cancellationToken); + return GlobalData.RpcService.InvokeDeviceMethodAsync($"Redundant[{(callContext.Caller is ITcpSession tcpSession ? tcpSession.GetIPPort() : string.Empty)}]", deviceDatas, cancellationToken); } } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Enums/ThreadRunReturnTypeEnum.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementOptions.cs similarity index 59% rename from src/Gateway/ThingsGateway.Gateway.Application/Enums/ThreadRunReturnTypeEnum.cs rename to src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementOptions.cs index 260c95928..e5b9ed743 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Enums/ThreadRunReturnTypeEnum.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementOptions.cs @@ -8,25 +8,30 @@ // QQ群:605534569 //------------------------------------------------------------------------------ -namespace ThingsGateway.Gateway.Application; +using System.ComponentModel.DataAnnotations; -/// -/// 返回状态 -/// -public enum ThreadRunReturnTypeEnum +namespace ThingsGateway.Management; + +public class RemoteManagementOptions { - /// - /// 无 - /// - None, + public bool Enable { get; set; } + + public string Name { get; set; } + + public bool IsServer { get; set; } + + public string ServerUri { get; set; } /// - /// 继续 + /// 获取或设置用于验证的令牌。 /// - Continue, + [Required] + public string VerifyToken { get; set; } /// - /// 跳出 + /// 获取或设置心跳间隔。 /// - Break, + [MinValue(3000)] + public int HeartbeatInterval { get; set; } + } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementRpcServer.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementRpcServer.cs new file mode 100644 index 000000000..e2e012e10 --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementRpcServer.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://thingsgateway.cn/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +using BootstrapBlazor.Components; + +using ThingsGateway.Gateway.Application; + +using TouchSocket.Dmtp.Rpc; +using TouchSocket.Rpc; +using TouchSocket.Sockets; + +namespace ThingsGateway.Management; + +public partial class RemoteManagementRpcServer : SingletonRpcServer, + IBackendLogService +{ + [DmtpRpc(MethodInvoke = true)] + public Task DeleteBackendLogAsync() => App.GetService().DeleteBackendLogAsync(); + [DmtpRpc(MethodInvoke = true)] + public Task> StatisticsByDayAsync(int day) => App.GetService().StatisticsByDayAsync(day); + + [DmtpRpc(MethodInvoke = true)] + public Task> GetNewLog() => App.GetService().GetNewLog(); + + [DmtpRpc(MethodInvoke = true)] + public Task> PageAsync(QueryPageOptions option) => App.GetService().PageAsync(option); + + + [DmtpRpc(MethodInvoke = true)] + public Task>> Rpc(ICallContext callContext, Dictionary> deviceDatas, CancellationToken cancellationToken) + { + return GlobalData.RpcService.InvokeDeviceMethodAsync($"RemoteManagement[{(callContext.Caller is ITcpSession tcpSession ? tcpSession.GetIPPort() : string.Empty)}]", deviceDatas, cancellationToken); + } + + +} diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementTask.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementTask.cs new file mode 100644 index 000000000..5bf325d0e --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Management/RemoteManagement/RemoteManagementTask.cs @@ -0,0 +1,127 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://thingsgateway.cn/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +using TouchSocket.Core; +using TouchSocket.Dmtp; +using TouchSocket.Dmtp.Rpc; +using TouchSocket.Rpc; +using TouchSocket.Sockets; + +namespace ThingsGateway.Management; + +public partial class RemoteManagementTask +{ + private ILog LogMessage; + + public RemoteManagementTask(ILog log) + { + LogMessage = log; + _remoteManagementOptions = App.GetOptions(); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + if (_remoteManagementOptions.IsServer) + { + _tcpDmtpService ??= await GetTcpDmtpService().ConfigureAwait(false); + } + else + { + _tcpDmtpClient ??= await GetTcpDmtpClient().ConfigureAwait(false); + } + while (!cancellationToken.IsCancellationRequested) + { + try + { + await EnsureChannelOpenAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + LogMessage?.LogWarning(ex, "Start"); + } + finally + { + await Task.Delay(10000, cancellationToken).ConfigureAwait(false); + } + + } + } + + private TcpDmtpClient? _tcpDmtpClient; + private TcpDmtpService? _tcpDmtpService; + private RemoteManagementOptions _remoteManagementOptions; + + private async Task GetTcpDmtpClient() + { + var tcpDmtpClient = new TcpDmtpClient(); + var config = new TouchSocketConfig() + .SetRemoteIPHost(_remoteManagementOptions.ServerUri) + .SetAdapterOption(new AdapterOption() { MaxPackageSize = 1024 * 1024 * 1024 }) + .SetDmtpOption(new DmtpOption() { VerifyToken = _remoteManagementOptions.VerifyToken }) + .ConfigureContainer(a => + { + a.AddLogger(LogMessage); + a.AddRpcStore(store => store.RegisterServer(new RemoteManagementRpcServer())); + }) + .ConfigurePlugins(a => + { + a.UseDmtpRpc(); + a.UseDmtpHeartbeat()//使用Dmtp心跳 + .SetTick(TimeSpan.FromMilliseconds(_remoteManagementOptions.HeartbeatInterval)) + .SetMaxFailCount(3); + }); + + await tcpDmtpClient.SetupAsync(config).ConfigureAwait(false); + return tcpDmtpClient; + } + + private async Task GetTcpDmtpService() + { + var tcpDmtpService = new TcpDmtpService(); + var config = new TouchSocketConfig() + .SetListenIPHosts(_remoteManagementOptions.ServerUri) + .SetAdapterOption(new AdapterOption() { MaxPackageSize = 1024 * 1024 * 1024 }) + .SetDmtpOption(new DmtpOption() { VerifyToken = _remoteManagementOptions.VerifyToken }) + .ConfigureContainer(a => + { + a.AddLogger(LogMessage); + a.AddRpcStore(store => store.RegisterServer(new RemoteManagementRpcServer())); + }) + .ConfigurePlugins(a => + { + a.UseDmtpRpc(); + a.UseDmtpHeartbeat()//使用Dmtp心跳 + .SetTick(TimeSpan.FromMilliseconds(_remoteManagementOptions.HeartbeatInterval)) + .SetMaxFailCount(3); + }); + + await tcpDmtpService.SetupAsync(config).ConfigureAwait(false); + return tcpDmtpService; + } + + private async Task EnsureChannelOpenAsync(CancellationToken cancellationToken) + { + if (_remoteManagementOptions.IsServer) + { + if (_tcpDmtpService.ServerState != ServerState.Running) + { + if (_tcpDmtpService.ServerState != ServerState.Stopped) + await _tcpDmtpService.StopAsync(cancellationToken).ConfigureAwait(false); + + await _tcpDmtpService.StartAsync().ConfigureAwait(false); + } + } + else + { + if (!_tcpDmtpClient.Online) + await _tcpDmtpClient.ConnectAsync().ConfigureAwait(false); + } + } +} diff --git a/src/Gateway/ThingsGateway.Gateway.Application/ThingsGateway.Gateway.Application.csproj b/src/Gateway/ThingsGateway.Gateway.Application/ThingsGateway.Gateway.Application.csproj index ee3cf2586..fc6537eae 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/ThingsGateway.Gateway.Application.csproj +++ b/src/Gateway/ThingsGateway.Gateway.Application/ThingsGateway.Gateway.Application.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelEditComponent.razor b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelEditComponent.razor index cdaf4101f..e7bcf0d90 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelEditComponent.razor +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelEditComponent.razor @@ -92,9 +92,11 @@
- { - return InvokeAsync(StateHasChanged); + await InvokeAsync(StateHasChanged); + await Task.Delay(100); + await InvokeAsync(StateHasChanged); }) />
diff --git a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Message.cs b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Message.cs index f4ea83cf5..57b997b70 100644 --- a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Message.cs +++ b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Message.cs @@ -38,32 +38,32 @@ public class Dlt645_2007Message : MessageBase, IResultMessage public override int HeaderLength { get; set; } = 10; public Dlt645_2007Address? Request { get; set; } - public Dlt645_2007Response Response { get; set; } = new(); public override FilterResult CheckBody(ref TByteBlock byteBlock) { + var span = byteBlock.Span; var pos = byteBlock.Position - HeaderLength; var endIndex = HeaderLength + BodyLength + pos; - if (byteBlock[endIndex - 1] == 0x16) + if (span[endIndex - 1] == 0x16) { //检查校验码 int sumCheck = 0; for (int i = HeadCodeIndex; i < endIndex - 2; i++) - sumCheck += byteBlock[i]; - if ((byte)sumCheck != byteBlock[endIndex - 2]) + sumCheck += span[i]; + if ((byte)sumCheck != span[endIndex - 2]) { //校验错误 ErrorMessage = AppResource.SumError; OperCode = 999; return FilterResult.Success; } - Response.Station = byteBlock.Span.Slice(HeadCodeIndex + 1, 6).ToArray(); - - var controlCode = byteBlock[HeadCodeIndex + 8]; + var Station = byteBlock.Span.Slice(HeadCodeIndex + 1, 6); + byte? ErrorCode; + var controlCode = span[HeadCodeIndex + 8]; if ((controlCode & 0x40) == 0x40)//控制码bit6为1时,返回错误 { - Response.ErrorCode = (byte)(byteBlock[HeadCodeIndex + 10] - 0x33); - var error = Dlt645Helper.Get2007ErrorMessage(Response.ErrorCode.Value); + ErrorCode = (byte)(span[HeadCodeIndex + 10] - 0x33); + var error = Dlt645Helper.Get2007ErrorMessage(ErrorCode.Value); ErrorMessage = string.Format(AppResource.FunctionError, $"0x{controlCode:X2}", error); OperCode = 999; return FilterResult.Success; @@ -71,9 +71,9 @@ public class Dlt645_2007Message : MessageBase, IResultMessage if (Dlt645_2007Send != null) { - if (!Response.Station.SequenceEqual(Request.Station))//设备地址不符合时,返回错误 + if (!Station.SequenceEqual(Request.Station.Span))//设备地址不符合时,返回错误 { - if (!Request.Station.SequenceEqual(ReadStation))//读写通讯地址例外 + if (!Request.Station.Span.SequenceEqual(ReadStation))//读写通讯地址例外 { ErrorMessage = AppResource.StationNotSame; OperCode = 999; @@ -90,8 +90,8 @@ public class Dlt645_2007Message : MessageBase, IResultMessage if (Dlt645_2007Send.ControlCode == ControlCode.Read || Dlt645_2007Send.ControlCode == ControlCode.Write) { //数据标识不符合时,返回错误 - Response.DataId = byteBlock.Span.Slice(HeadCodeIndex + 10, 4).ToArray().BytesAdd(-0x33); - if (!Response.DataId.SequenceEqual(Request.DataId)) + var DataId = byteBlock.Span.Slice(HeadCodeIndex + 10, 4).BytesAdd(-0x33).AsSpan(); + if (!DataId.SequenceEqual(Request.DataId.Span)) { ErrorMessage = AppResource.DataIdNotSame; OperCode = 999; @@ -112,12 +112,12 @@ public class Dlt645_2007Message : MessageBase, IResultMessage public override bool CheckHead(ref TByteBlock byteBlock) { //因为设备可能带有FE前导符开头,这里找到0x68的位置 - + var span = byteBlock.Span; if (byteBlock != null) { for (int index = byteBlock.Position; index < byteBlock.Length; index++) { - if (byteBlock[index] == 0x68) + if (span[index] == 0x68) { HeadCodeIndex = index; break; @@ -128,7 +128,7 @@ public class Dlt645_2007Message : MessageBase, IResultMessage //帧起始符 地址域 帧起始符 控制码 数据域长度共10个字节 HeaderLength = HeadCodeIndex - byteBlock.Position + 10; if (byteBlock.Length > HeadCodeIndex + 9) - BodyLength = byteBlock[HeadCodeIndex + 9] + 2; + BodyLength = span[HeadCodeIndex + 9] + 2; return true; } diff --git a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Send.cs b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Send.cs index 457803051..a0b7eb1f1 100644 --- a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Send.cs +++ b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Core/Dlt645_2007Send.cs @@ -26,19 +26,24 @@ public class Dlt645_2007Request /// /// 数据标识 /// - public byte[] DataId { get; set; } = Array.Empty(); + public Memory DataId { get; set; } = Memory.Empty; + private string data; public string Data { get { - return DataId.Reverse().ToArray().ToHexString(); + return data; } set { + data = value; if (value != null) - DataId = value?.HexStringToBytes().Reverse().ToArray(); + { + DataId = value.HexStringToBytes(); + DataId.Span.Reverse(); + } else - DataId = null; + DataId = Memory.Empty; } } /// @@ -49,23 +54,26 @@ public class Dlt645_2007Request /// /// 站号信息 /// - public byte[] Station { get; set; } = Array.Empty(); + public Memory Station { get; set; } = Memory.Empty; + private string stationString; public string StationString { get { - return Station.Reverse().ToArray().ToHexString(); + return stationString; } set { + stationString = value; if (value != null) { if (value.Length < 12) value = value.PadLeft(12, '0'); - Station = value?.HexStringToBytes().Reverse().ToArray(); + Station = value.HexStringToBytes(); + Station.Span.Reverse(); } else - Station = null; + Station = Memory.Empty; } } #endregion Request @@ -81,23 +89,23 @@ public class Dlt645_2007Send : ISendMessage /// /// 密码、操作码 /// - private byte[] Codes = default; + private ReadOnlyMemory Codes = default; /// /// 写入值 /// - private string[] Datas = default; + private ReadOnlyMemory Datas = default; - private byte[] Fehead = default; + private ReadOnlyMemory Fehead = default; - public Dlt645_2007Send(Dlt645_2007Address dlt645_2007Address, ControlCode controlCode, byte[] fehead = default, byte[] codes = default, string[] datas = default) + public Dlt645_2007Send(Dlt645_2007Address dlt645_2007Address, ControlCode controlCode, ReadOnlyMemory fehead = default, ReadOnlyMemory codes = default, ReadOnlyMemory datas = default) { Dlt645_2007Address = dlt645_2007Address; ControlCode = controlCode; - Fehead = fehead ?? Array.Empty(); - Codes = codes ?? Array.Empty(); - Datas = datas ?? Array.Empty(); + Fehead = fehead; + Codes = codes; + Datas = datas; } public int MaxLength => 300; @@ -105,7 +113,7 @@ public class Dlt645_2007Send : ISendMessage public int Sign { get; set; } internal Dlt645_2007Address Dlt645_2007Address { get; } - public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlock + public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter { if (Dlt645_2007Address?.DataId.Length < 4) { @@ -113,39 +121,41 @@ public class Dlt645_2007Send : ISendMessage } if (Fehead.Length > 0) { - byteBlock.Write(Fehead);//帧起始符 + byteBlock.Write(Fehead.Span);//帧起始符 SendHeadCodeIndex = Fehead.Length; } - byteBlock.WriteByte(0x68);//帧起始符 - byteBlock.Write(Dlt645_2007Address.Station);//6个字节地址域 - byteBlock.WriteByte(0x68);//帧起始符 - byteBlock.WriteByte((byte)ControlCode);//控制码 + WriterExtension.WriteValue(ref byteBlock, (byte)0x68);//帧起始符 + byteBlock.Write(Dlt645_2007Address.Station.Span);//6个字节地址域 + WriterExtension.WriteValue(ref byteBlock, (byte)0x68);//帧起始符 + WriterExtension.WriteValue(ref byteBlock, (byte)ControlCode);//控制码 - byteBlock.WriteByte((byte)(Dlt645_2007Address.DataId.Length));//数据域长度 - byteBlock.Write(Dlt645_2007Address.DataId);//数据域标识DI3、DI2、DI1、DI0 + WriterExtension.WriteValue(ref byteBlock, (byte)(Dlt645_2007Address.DataId.Length));//数据域长度 + byteBlock.Write(Dlt645_2007Address.DataId.Span);//数据域标识DI3、DI2、DI1、DI0 - byteBlock.Write(Codes); + byteBlock.Write(Codes.Span); if (Datas.Length > 0) { - var dataInfos = Dlt645Helper.GetDataInfos(Dlt645_2007Address.DataId); + var dataInfos = Dlt645Helper.GetDataInfos(Dlt645_2007Address.DataId.Span); if (Datas.Length != dataInfos.Count) { throw new(AppResource.CountError); } + var datas = Datas.Span; for (int i = 0; i < Datas.Length; i++) { var dataInfo = dataInfos[i]; - byte[] data; + Span data; if (dataInfo.IsSigned)//可能为负数 { - var doubleValue = Convert.ToDouble(Datas[i]); + var doubleValue = Convert.ToDouble(datas[i]); if (dataInfo.Digtal != 0)//无小数点 { doubleValue *= Math.Pow(10.0, dataInfo.Digtal); } - data = doubleValue.ToString().HexStringToBytes().Reverse().ToArray(); + data = doubleValue.ToString().HexStringToBytes().Span; + data.Reverse(); if (doubleValue < 0) { data[0] = (byte)(data[0] & 0x80); @@ -155,15 +165,18 @@ public class Dlt645_2007Send : ISendMessage { if (dataInfo.Digtal < 0) { - data = Encoding.ASCII.GetBytes(Datas[i]).Reverse().ToArray(); + data = Encoding.ASCII.GetBytes(datas[i]).AsSpan(); + data.Reverse(); } else if (dataInfo.Digtal == 0)//无小数点 { - data = Datas[i].HexStringToBytes().Reverse().ToArray(); + data = datas[i].HexStringToBytes().Span; + data.Reverse(); } else { - data = (Convert.ToDouble(Datas[i]) * Math.Pow(10.0, dataInfo.Digtal)).ToString().HexStringToBytes().Reverse().ToArray(); + data = (Convert.ToDouble(datas[i]) * Math.Pow(10.0, dataInfo.Digtal)).ToString().HexStringToBytes().Span; + data.Reverse(); } } @@ -171,15 +184,16 @@ public class Dlt645_2007Send : ISendMessage } } - byteBlock[Fehead.Length + 9] = (byte)(byteBlock.Length - 10 - Fehead.Length);//数据域长度 + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 10 - Fehead.Length), Fehead.Length + 9);//数据域长度 for (int index = Fehead.Length + 10; index < byteBlock.Length; ++index) - byteBlock[index] += 0x33;//传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理 + ByteBlockExtension.WriteBackAddValue(ref byteBlock, (byte)0x33, index);//传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理 int num = 0; + var span = byteBlock.Span; for (int index = Fehead.Length; index < byteBlock.Length; ++index) - num += byteBlock[index]; - byteBlock.WriteByte((byte)num);//校验码,总加和 - byteBlock.WriteByte(0x16);//结束符 + num += span[index]; + WriterExtension.WriteValue(ref byteBlock, (byte)num);//校验码,总加和 + WriterExtension.WriteValue(ref byteBlock, (byte)0x16);//结束符 } } diff --git a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Dlt645_2007Master.cs b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Dlt645_2007Master.cs index 48d2d3dd5..160377445 100644 --- a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Dlt645_2007Master.cs +++ b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Dlt645_2007Master.cs @@ -8,7 +8,6 @@ // QQ群:605534569 //------------------------------------------------------------------------------ -using ThingsGateway.Foundation.Extension.Generic; using ThingsGateway.Foundation.Extension.String; using TouchSocket.Core; @@ -22,7 +21,6 @@ public class Dlt645_2007Master : DtuServiceDeviceBase { base.InitChannel(channel, deviceLog); RegisterByteLength = 2; - channel.MaxSign = ushort.MaxValue; } public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new Dlt645_2007BitConverter(EndianType.Big); @@ -62,7 +60,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase } /// - public ValueTask> Dlt645RequestAsync(Dlt645_2007Address dAddress, ControlCode controlCode, string feHead, byte[] codes = default, string[] datas = default, CancellationToken cancellationToken = default) + public ValueTask>> Dlt645RequestAsync(Dlt645_2007Address dAddress, ControlCode controlCode, string feHead, ReadOnlyMemory codes = default, ReadOnlyMemory datas = default, CancellationToken cancellationToken = default) { return SendThenReturnAsync(GetSendMessage(dAddress, controlCode, feHead, codes, datas), cancellationToken); } @@ -133,7 +131,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase } /// - public override ValueTask> ReadAsync(string address, int length, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) { try { @@ -142,10 +140,10 @@ public class Dlt645_2007Master : DtuServiceDeviceBase } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } - public override ValueTask> ReadAsync(object state, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(object state, CancellationToken cancellationToken = default) { if (state is Dlt645_2007Address dlt645_2007Address) { @@ -153,7 +151,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase } else { - return EasyValueTask.FromResult(new OperResult(new ArgumentException("State must be of type Dlt645_2007Address", nameof(state)))); + return EasyValueTask.FromResult(new OperResult>(new ArgumentException("State must be of type Dlt645_2007Address", nameof(state)))); } } /// @@ -171,8 +169,9 @@ public class Dlt645_2007Master : DtuServiceDeviceBase var result = await Dlt645RequestAsync(dAddress, ControlCode.ReadStation, FEHead, cancellationToken: cancellationToken).ConfigureAwait(false); if (result.IsSuccess) { - var buffer = result.Content.SelectMiddle(0, 6).BytesAdd(-0x33); - return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString()); + var buffer = result.Content.Slice(0, 6).Span.BytesAdd(-0x33).AsSpan(); + buffer.Reverse(); + return OperResult.CreateSuccessResult(buffer.ToHexString()); } else { @@ -191,16 +190,16 @@ public class Dlt645_2007Master : DtuServiceDeviceBase bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address); var result = await ReadAsync(address, GetLength(address, 0, 1), cancellationToken).ConfigureAwait(false); - return result.OperResultFrom(() => + return result.OperResultFrom>((a) => { - var data = bitConverter.ToString(result.Content, 0, result.Content.Length); + var data = bitConverter.ToString(a.Span, 0, a.Length); return [data]; } ); } /// - public override async ValueTask WriteAsync(string address, string[] value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) { try { @@ -212,7 +211,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase OperCode = OperCode.PadLeft(8, '0'); var codes = DataTransUtil.SpliceArray(Password.HexStringToBytes(), OperCode.HexStringToBytes()); - string[] strArray = value; + var strArray = value; var dAddress = Dlt645_2007Address.ParseFrom(address, Station); return await Dlt645RequestAsync(dAddress, ControlCode.Write, FEHead, codes, strArray, cancellationToken: cancellationToken).ConfigureAwait(false); } @@ -237,7 +236,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase } /// - public override async ValueTask WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default) { try { @@ -283,7 +282,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase public override ValueTask WriteAsync(string address, int value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), bitConverter, cancellationToken); /// - public override ValueTask WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => throw new NotSupportedException(); + public override ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default) => throw new NotSupportedException(); /// /// 修改波特率 @@ -310,7 +309,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase Dlt645_2007Address dAddress = new(); dAddress.SetStation(station ?? Station); - dAddress.DataId = [baudRateByte]; + dAddress.DataId = new byte[] { baudRateByte }; return await Dlt645RequestAsync(dAddress, ControlCode.ReadStation, FEHead, cancellationToken: cancellationToken).ConfigureAwait(false); } @@ -332,7 +331,8 @@ public class Dlt645_2007Master : DtuServiceDeviceBase { Dlt645_2007Address dAddress = new(); dAddress.Station = "AAAAAAAAAAAA".HexStringToBytes(); - dAddress.DataId = station.HexStringToBytes().Reverse().ToArray(); + var memory = station.HexStringToBytes(); memory.Span.Reverse(); + dAddress.DataId = memory; return await Dlt645RequestAsync(dAddress, ControlCode.WriteStation, FEHead, cancellationToken: cancellationToken).ConfigureAwait(false); } @@ -355,11 +355,16 @@ public class Dlt645_2007Master : DtuServiceDeviceBase { try { - string str = $"04000C{level + 1:D2}"; + var levelstr = $"04000C{level + 1:D2}".HexStringToBytes(); + var newPasswordstr = oldPassword.HexStringToBytes(); + var oldPasswordstr = newPassword.HexStringToBytes(); + levelstr.Span.Reverse(); + newPasswordstr.Span.Reverse(); + oldPasswordstr.Span.Reverse(); - var bytes = DataTransUtil.SpliceArray(str.HexStringToBytes().Reverse().ToArray() - , (oldPassword.HexStringToBytes().Reverse().ToArray()) - , (newPassword.HexStringToBytes().Reverse().ToArray())); + var bytes = DataTransUtil.SpliceArray(levelstr + , newPasswordstr + , oldPasswordstr); Dlt645_2007Address dAddress = new(); dAddress.SetStation(station ?? Station); @@ -374,7 +379,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase } } - private static Dlt645_2007Send GetSendMessage(Dlt645_2007Address dAddress, ControlCode read, string feHead, byte[] codes = default, string[] datas = default) + private static Dlt645_2007Send GetSendMessage(Dlt645_2007Address dAddress, ControlCode read, string feHead, ReadOnlyMemory codes = default, ReadOnlyMemory datas = default) { return new Dlt645_2007Send(dAddress, read, feHead.HexStringToBytes(), codes, datas); } diff --git a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Helper/Dlt645_2007BitConverter.cs b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Helper/Dlt645_2007BitConverter.cs index 9cd781075..ef67c3d0f 100644 --- a/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Helper/Dlt645_2007BitConverter.cs +++ b/src/Plugin/ThingsGateway.Foundation.Dlt645/Dlt645/Helper/Dlt645_2007BitConverter.cs @@ -10,6 +10,8 @@ using System.Text; +using ThingsGateway.NewLife.Extension; + using TouchSocket.Core; namespace ThingsGateway.Foundation.Dlt645; @@ -33,46 +35,48 @@ public class Dlt645_2007BitConverter : ThingsGatewayBitConverter /// 带数据项标识 /// /// - public override double ToDouble(byte[] buffer, int offset) + public override double ToDouble(ReadOnlySpan buffer, int offset) { return Convert.ToDouble(ToString(buffer, offset, buffer.Length)); } + /// - public override short ToInt16(byte[] buffer, int offset) + public override short ToInt16(ReadOnlySpan buffer, int offset) { return Convert.ToInt16(ToString(buffer, offset, buffer.Length)); } /// - public override int ToInt32(byte[] buffer, int offset) + public override int ToInt32(ReadOnlySpan buffer, int offset) { return Convert.ToInt32(ToString(buffer, offset, buffer.Length)); } /// - public override long ToInt64(byte[] buffer, int offset) + public override long ToInt64(ReadOnlySpan buffer, int offset) { return Convert.ToInt64(ToString(buffer, offset, buffer.Length)); } /// - public override float ToSingle(byte[] buffer, int offset) + public override float ToSingle(ReadOnlySpan buffer, int offset) { return Convert.ToSingle(ToString(buffer, offset, buffer.Length)); } /// - public override string ToString(byte[] buffer, int offset, int length) + public override string ToString(ReadOnlySpan buffer, int offset, int length) { - var data = new ReadOnlySpan(buffer, offset, buffer.Length - offset).BytesAdd(-0x33); + var data = buffer.Slice(offset, buffer.Length - offset).BytesAdd(-0x33).AsSpan(); var dataInfos = Dlt645Helper.GetDataInfos(data); StringBuilder stringBuilder = new(); int index = 0; foreach (var dataInfo in dataInfos) { //实际数据 - var content = data.Slice(4 + index, dataInfo.ByteLength).ToArray().Reverse().ToArray(); + var content = data.Slice(4 + index, dataInfo.ByteLength); + content.Reverse(); if (dataInfo.IsSigned)//可能为负数 { if (content[0] >= 0x80)//最高位是表示正负 @@ -101,7 +105,7 @@ public class Dlt645_2007BitConverter : ThingsGatewayBitConverter return stringBuilder.ToString(); - void ToString(StringBuilder stringBuilder, Dlt645DataInfo dataInfo, byte[] content) + void ToString(StringBuilder stringBuilder, Dlt645DataInfo dataInfo, Span content) { if (dataInfo.Digtal < 0) { @@ -119,19 +123,19 @@ public class Dlt645_2007BitConverter : ThingsGatewayBitConverter } /// - public override ushort ToUInt16(byte[] buffer, int offset) + public override ushort ToUInt16(ReadOnlySpan buffer, int offset) { return Convert.ToUInt16(ToString(buffer, offset, buffer.Length)); } /// - public override uint ToUInt32(byte[] buffer, int offset) + public override uint ToUInt32(ReadOnlySpan buffer, int offset) { return Convert.ToUInt32(ToString(buffer, offset, buffer.Length)); } /// - public override ulong ToUInt64(byte[] buffer, int offset) + public override ulong ToUInt64(ReadOnlySpan buffer, int offset) { return Convert.ToUInt64(ToString(buffer, offset, buffer.Length)); } diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuMessage.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuMessage.cs index 8d5aaab90..bf1564d3b 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuMessage.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuMessage.cs @@ -52,7 +52,7 @@ public class ModbusRtuMessage : MessageBase, IResultMessage else if (f == 5 || f == 6) { byteBlock.Position = HeaderLength - 1; - Response.StartAddress = byteBlock.ReadUInt16(EndianType.Big); + Response.StartAddress = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); OperCode = 0; Content = byteBlock.ToArrayTake(BodyLength - 4); Response.Data = Content; @@ -61,8 +61,8 @@ public class ModbusRtuMessage : MessageBase, IResultMessage else if (f == 15 || f == 16) { byteBlock.Position = HeaderLength - 1; - Response.StartAddress = byteBlock.ReadUInt16(EndianType.Big); - Response.Length = byteBlock.ReadUInt16(EndianType.Big); + Response.StartAddress = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); + Response.Length = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); OperCode = 0; Content = Array.Empty(); crcLen = 6; @@ -78,8 +78,8 @@ public class ModbusRtuMessage : MessageBase, IResultMessage var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen)); //Crc - var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2).ToArray(); - if (crc.SequenceEqual(checkCrc)) + var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2); + if (checkCrc.SequenceEqual(crc)) { //验证发送/返回站号与功能码 //站号验证 @@ -108,9 +108,9 @@ public class ModbusRtuMessage : MessageBase, IResultMessage public override bool CheckHead(ref TByteBlock byteBlock) { - Response.Station = byteBlock.ReadByte(); + Response.Station = ReaderExtension.ReadValue(ref byteBlock); bool error = false; - var code = byteBlock.ReadByte(); + var code = ReaderExtension.ReadValue(ref byteBlock); if ((code & 0x80) == 0) { Response.FunctionCode = code; @@ -124,7 +124,7 @@ public class ModbusRtuMessage : MessageBase, IResultMessage if (error) { - Response.ErrorCode = byteBlock.ReadByte(); + Response.ErrorCode = ReaderExtension.ReadValue(ref byteBlock); OperCode = 999; ErrorMessage = ModbusHelper.GetDescriptionByErrorCode(Response.ErrorCode.Value); ErrorType = ErrorTypeEnum.DeviceError; @@ -148,7 +148,7 @@ public class ModbusRtuMessage : MessageBase, IResultMessage } else if (f <= 4) { - Response.Length = byteBlock.ReadByte(); + Response.Length = ReaderExtension.ReadValue(ref byteBlock); BodyLength = Response.Length + 2; //数据区+crc return true; } diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuSend.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuSend.cs index 319b5217b..a8771f0cd 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuSend.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusRtuSend.cs @@ -27,7 +27,7 @@ public class ModbusRtuSend : ISendMessage public ModbusAddress ModbusAddress { get; } public int Sign { get; set; } - public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlock + public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter { var f = ModbusAddress.FunctionCode > 0x30 ? ModbusAddress.FunctionCode - 0x30 : ModbusAddress.FunctionCode; if (!Read) @@ -51,25 +51,25 @@ public class ModbusRtuSend : ISendMessage if (Read) { - byteBlock.WriteByte(ModbusAddress.Station); - byteBlock.WriteByte((byte)ModbusAddress.FunctionCode); - byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big); - byteBlock.WriteUInt16(ModbusAddress.Length, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.FunctionCode); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.Length, EndianType.Big); } else if (wf == 5 || wf == 6) { - byteBlock.WriteByte(ModbusAddress.Station); - byteBlock.WriteByte((byte)ModbusAddress.WriteFunctionCode); - byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big); byteBlock.Write(ModbusAddress.Data.Span); } else if (wf == 15 || wf == 16) { - byteBlock.WriteByte(ModbusAddress.Station); - byteBlock.WriteByte((byte)ModbusAddress.WriteFunctionCode); - byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big); - byteBlock.WriteUInt16((ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0), EndianType.Big); - byteBlock.WriteByte((byte)ModbusAddress.Data.Length); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0), EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Data.Length); byteBlock.Write(ModbusAddress.Data.Span); } else diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpMessage.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpMessage.cs index 0de83df19..39510c311 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpMessage.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpMessage.cs @@ -25,7 +25,7 @@ public class ModbusTcpMessage : MessageBase, IResultMessage var f = Response.FunctionCode > 0x30 ? Response.FunctionCode - 0x30 : Response.FunctionCode; if (error) { - Response.ErrorCode = byteBlock.ReadByte(); + Response.ErrorCode = ReaderExtension.ReadValue(ref byteBlock); OperCode = Response.ErrorCode; ErrorMessage = ModbusHelper.GetDescriptionByErrorCode(Response.ErrorCode.Value); ErrorType = ErrorTypeEnum.DeviceError; @@ -35,11 +35,11 @@ public class ModbusTcpMessage : MessageBase, IResultMessage Response.ErrorCode = null; if (f <= 4) { - Response.Length = byteBlock.ReadByte(); + Response.Length = ReaderExtension.ReadValue(ref byteBlock); } else { - Response.StartAddress = byteBlock.ReadUInt16(); + Response.StartAddress = ReaderExtension.ReadValue(ref byteBlock); } } if (Response.ErrorCode != null) @@ -57,7 +57,7 @@ public class ModbusTcpMessage : MessageBase, IResultMessage else if (f == 5 || f == 6) { byteBlock.Position = HeaderLength - 1; - Response.StartAddress = byteBlock.ReadUInt16(); + Response.StartAddress = ReaderExtension.ReadValue(ref byteBlock); OperCode = 0; Content = byteBlock.ToArrayTake(BodyLength - 2); Response.Data = Content; @@ -66,8 +66,8 @@ public class ModbusTcpMessage : MessageBase, IResultMessage else if (f == 15 || f == 16) { byteBlock.Position = HeaderLength - 1; - Response.StartAddress = byteBlock.ReadUInt16(EndianType.Big); - Response.Length = byteBlock.ReadUInt16(EndianType.Big); + Response.StartAddress = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); + Response.Length = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); OperCode = 0; Content = Array.Empty(); return FilterResult.Success; @@ -82,11 +82,11 @@ public class ModbusTcpMessage : MessageBase, IResultMessage bool error = false; public override bool CheckHead(ref TByteBlock byteBlock) { - Sign = byteBlock.ReadUInt16(EndianType.Big); + Sign = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); byteBlock.Position += 2; - BodyLength = byteBlock.ReadUInt16(EndianType.Big) - 2; - Response.Station = byteBlock.ReadByte(); - Response.FunctionCode = byteBlock.ReadByte(); + BodyLength = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) - 2; + Response.Station = ReaderExtension.ReadValue(ref byteBlock); + Response.FunctionCode = ReaderExtension.ReadValue(ref byteBlock); if ((Response.FunctionCode & 0x80) == 0) { error = false; diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpSend.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpSend.cs index 51c9cf190..e9f410c54 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpSend.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/Core/ModbusTcpSend.cs @@ -40,7 +40,7 @@ public class ModbusTcpSend : ISendMessage /// public ushort TransactionId { get; private set; } - public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlock + public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter { TransactionId = (ushort)Sign; var f = ModbusAddress.FunctionCode > 0x30 ? ModbusAddress.FunctionCode - 0x30 : ModbusAddress.FunctionCode; @@ -63,33 +63,33 @@ public class ModbusTcpSend : ISendMessage ModbusAddress.WriteFunctionCode += 0x30; } - byteBlock.WriteUInt16(TransactionId, EndianType.Big); - byteBlock.WriteUInt16(DeviceId, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)TransactionId, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)DeviceId, EndianType.Big); if (Read) { - byteBlock.WriteUInt16(6, EndianType.Big); - byteBlock.WriteByte(ModbusAddress.Station); - byteBlock.WriteByte((byte)ModbusAddress.FunctionCode); - byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big); - byteBlock.WriteUInt16(ModbusAddress.Length, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)6, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.FunctionCode); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.Length, EndianType.Big); } else if (wf == 5 || wf == 6) { - byteBlock.WriteUInt16(6, EndianType.Big); - byteBlock.WriteByte(ModbusAddress.Station); - byteBlock.WriteByte((byte)ModbusAddress.WriteFunctionCode); - byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)6, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big); byteBlock.Write(ModbusAddress.Data.Span); } else if (wf == 15 || wf == 16) { - byteBlock.WriteUInt16((ushort)(ModbusAddress.Data.Length + 7), EndianType.Big); - byteBlock.WriteByte(ModbusAddress.Station); - byteBlock.WriteByte((byte)ModbusAddress.WriteFunctionCode); - byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big); - byteBlock.WriteUInt16((ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0), EndianType.Big); - byteBlock.WriteByte((byte)ModbusAddress.Data.Length); + WriterExtension.WriteValue(ref byteBlock, (ushort)(ModbusAddress.Data.Length + 7), EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode); + WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0), EndianType.Big); + WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Data.Length); byteBlock.Write(ModbusAddress.Data.Span); } else diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/ModbusMaster.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/ModbusMaster.cs index 62326894a..103df2e2e 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Master/ModbusMaster.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Master/ModbusMaster.cs @@ -18,7 +18,6 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress base.InitChannel(channel, deviceLog); RegisterByteLength = 2; - channel.MaxSign = ushort.MaxValue; } protected override void SetChannel() @@ -102,7 +101,7 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress return PackHelper.LoadSourceRead(this, deviceVariables, maxPack, defaultIntervalTime, Station); } - public override ValueTask> ReadAsync(object state, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(object state, CancellationToken cancellationToken = default) { try { @@ -112,16 +111,16 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress } else { - return EasyValueTask.FromResult(new OperResult(new ArgumentException("State must be of type ModbusAddress", nameof(state)))); + return EasyValueTask.FromResult(new OperResult>(new ArgumentException("State must be of type ModbusAddress", nameof(state)))); } } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } - public ValueTask> ModbusReadAsync(ModbusAddress mAddress, CancellationToken cancellationToken = default) + public ValueTask>> ModbusReadAsync(ModbusAddress mAddress, CancellationToken cancellationToken = default) { try { @@ -130,11 +129,11 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } - public ValueTask> ModbusRequestAsync(ModbusAddress mAddress, bool read, CancellationToken cancellationToken = default) + public ValueTask>> ModbusRequestAsync(ModbusAddress mAddress, bool read, CancellationToken cancellationToken = default) { try { @@ -143,12 +142,12 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } /// - public override ValueTask> ReadAsync(string address, int length, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) { try { @@ -158,12 +157,12 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } /// - public override async ValueTask WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default) { try { @@ -179,12 +178,14 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress mAddress.Length = 1; //请求寄存器数量 var readData = await ModbusRequestAsync(mAddress, true, cancellationToken).ConfigureAwait(false); if (!readData.IsSuccess) return readData; + var v = value.Span[0]; + var writeValye = readData.Content.ToArray(); if (mAddress.BitIndex == 0) - readData.Content[1] = value[0]; + writeValye[1] = v; else - readData.Content[0] = value[0]; + writeValye[0] = v; - mAddress.Data = readData.Content; + mAddress.Data = writeValye; return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false); } else @@ -203,7 +204,7 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress return mAddress; } /// - public override async ValueTask WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default) { try { @@ -211,12 +212,13 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress if (value.Length > 1 && (mAddress.FunctionCode == 1 || mAddress.FunctionCode == 0x31)) { mAddress.WriteFunctionCode = 15; - mAddress.Data = value.BoolArrayToByte(); + mAddress.Data = value.Span.BoolArrayToByte(); return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false); } else if (mAddress.BitIndex == null) { - mAddress.Data = value[0] ? new byte[2] { 255, 0 } : [0, 0]; + var span = value.Span; + mAddress.Data = span[0] ? new byte[2] { 255, 0 } : [0, 0]; return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false); } else @@ -226,10 +228,11 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress mAddress.Length = 1; //请求寄存器数量 var readData = await ModbusRequestAsync(mAddress, true, cancellationToken).ConfigureAwait(false); if (!readData.IsSuccess) return readData; - var writeData = ThingsGatewayBitConverter.ToUInt16(readData.Content, 0); - for (int i = 0; i < value.Length; i++) + var writeData = ThingsGatewayBitConverter.ToUInt16(readData.Content.Span, 0); + var span = value.Span; + for (int i = 0; i < span.Length; i++) { - writeData = writeData.SetBit(mAddress.BitIndex.Value + i, value[i]); + writeData = writeData.SetBit(mAddress.BitIndex.Value + i, span[i]); } mAddress.Data = ThingsGatewayBitConverter.GetBytes(writeData); return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false); diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusRtuSlaveMessage.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusRtuSlaveMessage.cs index 5f4407c5e..d04d9f177 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusRtuSlaveMessage.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusRtuSlaveMessage.cs @@ -28,43 +28,43 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage /// public override bool CheckHead(ref TByteBlock byteBlock) { - Request.Station = byteBlock.ReadByte(); - Request.FunctionCode = byteBlock.ReadByte(); + Request.Station = ReaderExtension.ReadValue(ref byteBlock); + Request.FunctionCode = ReaderExtension.ReadValue(ref byteBlock); var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode; - Request.StartAddress = byteBlock.ReadUInt16(EndianType.Big); + Request.StartAddress = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); if (f == 3 || f == 4) { - Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2); + Request.Length = (ushort)(ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) * 2); BodyLength = 1; return true; } else if (f == 1 || f == 2) { - Request.Length = byteBlock.ReadUInt16(EndianType.Big); + Request.Length = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); BodyLength = 1; return true; } else if (f == 5) { - Request.Data = byteBlock.AsSegmentTake(1); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 1); BodyLength = 1; return true; } else if (f == 6) { - Request.Data = byteBlock.AsSegmentTake(2); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 2); BodyLength = 1; return true; } else if (f == 15) { - Request.Length = byteBlock.ReadUInt16(EndianType.Big); + Request.Length = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); BodyLength = Request.Length + 2; return true; } else if (f == 16) { - Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2); + Request.Length = (ushort)(ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) * 2); BodyLength = Request.Length + 2; return true; } @@ -75,16 +75,16 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage { var pos = byteBlock.Position - HeaderLength; var crcLen = 0; - Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength); + Bytes = byteBlock.Memory.Slice(pos, HeaderLength + BodyLength); var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode; if (f == 15) { - Request.Data = byteBlock.AsSegmentTake(Request.Length).AsSpan().ByteToBoolArray(Request.Length).Select(a => a ? (byte)0xff : (byte)0).ToArray(); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length).Span.ByteToBoolArray(Request.Length).BoolToByte(); } else if (f == 16) { - Request.Data = byteBlock.AsSegmentTake(Request.Length); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length); } crcLen = HeaderLength + BodyLength - 2; @@ -92,8 +92,8 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen)); //Crc - var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2).ToArray(); - if (crc.SequenceEqual(checkCrc)) + var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2); + if (checkCrc.SequenceEqual(crc)) { OperCode = 0; return FilterResult.Success; diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusTcpSlaveMessage.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusTcpSlaveMessage.cs index 06fba35b4..c34dc4ac0 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusTcpSlaveMessage.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/Core/ModbusTcpSlaveMessage.cs @@ -27,43 +27,43 @@ public class ModbusTcpSlaveMessage : MessageBase, IResultMessage public override bool CheckHead(ref TByteBlock byteBlock) { - Sign = byteBlock.ReadUInt16(EndianType.Big); + Sign = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); byteBlock.Position += 2; - BodyLength = byteBlock.ReadUInt16(EndianType.Big) - 6; - Request.Station = byteBlock.ReadByte(); - Request.FunctionCode = byteBlock.ReadByte(); + BodyLength = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) - 6; + Request.Station = ReaderExtension.ReadValue(ref byteBlock); + Request.FunctionCode = ReaderExtension.ReadValue(ref byteBlock); var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode; - Request.StartAddress = byteBlock.ReadUInt16(EndianType.Big); + Request.StartAddress = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); if (f == 3 || f == 4) { - Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2); + Request.Length = (ushort)(ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) * 2); return true; } else if (f == 1 || f == 2) { - Request.Length = byteBlock.ReadUInt16(EndianType.Big); + Request.Length = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); return true; } else if (f == 5) { - Request.Data = byteBlock.AsSegmentTake(1); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 1); return true; } else if (f == 6) { - Request.Data = byteBlock.AsSegmentTake(2); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 2); return true; } else if (f == 15) { - Request.Length = byteBlock.ReadUInt16(EndianType.Big); + Request.Length = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); return true; } else if (f == 16) { - Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2); + Request.Length = (ushort)(ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) * 2); return true; } return false; @@ -72,19 +72,19 @@ public class ModbusTcpSlaveMessage : MessageBase, IResultMessage public override FilterResult CheckBody(ref TByteBlock byteBlock) { var pos = byteBlock.Position - HeaderLength; - Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength); + Bytes = byteBlock.Memory.Slice(pos, HeaderLength + BodyLength); var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode; if (f == 15) { byteBlock.Position += 1; - Request.Data = byteBlock.AsSegmentTake(Request.Length).AsSpan().ByteToBoolArray(Request.Length).Select(a => a ? (byte)0xff : (byte)0).ToArray(); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length).Span.ByteToBoolArray(Request.Length).BoolToByte(); } else if (f == 16) { byteBlock.Position += 1; - Request.Data = byteBlock.AsSegmentTake(Request.Length); + Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length); } OperCode = 0; diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs index 2dbe71290..902ef500b 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Slave/ModbusSlave.cs @@ -8,7 +8,6 @@ // QQ群:605534569 //------------------------------------------------------------------------------ -using System.Buffers; using System.Collections.Concurrent; using TouchSocket.Sockets; @@ -26,29 +25,28 @@ public class ModbusSlave : DeviceBase, IModbusAddress /// /// 继电器 /// - private ConcurrentDictionary ModbusServer01ByteBlocks = new(); + private ConcurrentDictionary ModbusServer01ByteBlocks = new(); /// /// 开关输入 /// - private ConcurrentDictionary ModbusServer02ByteBlocks = new(); + private ConcurrentDictionary ModbusServer02ByteBlocks = new(); /// /// 输入寄存器 /// - private ConcurrentDictionary ModbusServer03ByteBlocks = new(); + private ConcurrentDictionary ModbusServer03ByteBlocks = new(); /// /// 保持寄存器 /// - private ConcurrentDictionary ModbusServer04ByteBlocks = new(); + private ConcurrentDictionary ModbusServer04ByteBlocks = new(); /// public override void InitChannel(IChannel channel, ILog? deviceLog = null) { base.InitChannel(channel, deviceLog); RegisterByteLength = 2; - channel.MaxSign = ushort.MaxValue; } public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new ThingsGatewayBitConverter(EndianType.Big); @@ -185,10 +183,30 @@ public class ModbusSlave : DeviceBase, IModbusAddress /// private void Init(ModbusRequest mAddress) { - ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ValueByteBlock(new byte[ushort.MaxValue * 2])); - ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ValueByteBlock(new byte[ushort.MaxValue * 2])); - ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ValueByteBlock(new byte[ushort.MaxValue * 2])); - ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ValueByteBlock(new byte[ushort.MaxValue * 2])); + ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => + { + var bytes = new ByteBlock(ushort.MaxValue * 2); + bytes.SetLength(ushort.MaxValue * 2); + return bytes; + }); + ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => + { + var bytes = new ByteBlock(ushort.MaxValue * 2); + bytes.SetLength(ushort.MaxValue * 2); + return bytes; + }); + ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => + { + var bytes = new ByteBlock(ushort.MaxValue * 2); + bytes.SetLength(ushort.MaxValue * 2); + return bytes; + }); + ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => + { + var bytes = new ByteBlock(ushort.MaxValue * 2); + bytes.SetLength(ushort.MaxValue * 2); + return bytes; + }); } public override Action ConfigurePlugins(TouchSocketConfig config) @@ -294,22 +312,22 @@ public class ModbusSlave : DeviceBase, IModbusAddress } /// - public override ValueTask> ReadAsync(string address, int length, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) { var mAddress = GetModbusAddress(address, Station); mAddress.Length = (ushort)(length * RegisterByteLength); var result = ModbusRequest(mAddress, true, cancellationToken); if (result.IsSuccess) { - return EasyValueTask.FromResult(new OperResult() { Content = result.Content.ToArray() }); + return EasyValueTask.FromResult(new OperResult>() { Content = result.Content }); } else { - return EasyValueTask.FromResult(new OperResult(result)); + return EasyValueTask.FromResult(new OperResult>(result)); } } - public override ValueTask> ReadAsync(object state, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(object state, CancellationToken cancellationToken = default) { try { @@ -318,21 +336,21 @@ public class ModbusSlave : DeviceBase, IModbusAddress var result = ModbusRequest(mAddress, true, cancellationToken); if (result.IsSuccess) { - return EasyValueTask.FromResult(new OperResult() { Content = result.Content.ToArray() }); + return EasyValueTask.FromResult(new OperResult>() { Content = result.Content }); } else { - return EasyValueTask.FromResult(new OperResult(result)); + return EasyValueTask.FromResult(new OperResult>(result)); } } else { - return EasyValueTask.FromResult(new OperResult(new ArgumentException("State must be of type ModbusAddress", nameof(state)))); + return EasyValueTask.FromResult(new OperResult>(new ArgumentException("State must be of type ModbusAddress", nameof(state)))); } } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } @@ -342,7 +360,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress return mAddress; } /// - public override async ValueTask WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default) { try { @@ -358,7 +376,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress } /// - public override async ValueTask WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default) { try { @@ -366,7 +384,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress var mAddress = GetModbusAddress(address, Station); if (mAddress.IsBitFunction) { - mAddress.Data = new ReadOnlyMemory(value.Select(a => a ? (byte)0xff : (byte)0).ToArray()); + mAddress.Data = value.Span.BoolToByte(); ModbusRequest(mAddress, false, cancellationToken); return OperResult.Success; } @@ -378,9 +396,10 @@ public class ModbusSlave : DeviceBase, IModbusAddress var readData = ModbusRequest(mAddress, true, cancellationToken); if (!readData.IsSuccess) return readData; var writeData = TouchSocketBitConverter.BigEndian.To(readData.Content.Span); + var span = value.Span; for (int i = 0; i < value.Length; i++) { - writeData = writeData.SetBit(mAddress.BitIndex.Value + i, value[i]); + writeData = writeData.SetBit(mAddress.BitIndex.Value + i, span[i]); } mAddress.Data = ThingsGatewayBitConverter.GetBytes(writeData); ModbusRequest(mAddress, false, cancellationToken); @@ -442,44 +461,44 @@ public class ModbusSlave : DeviceBase, IModbusAddress var data = ModbusRequest(modbusRequest, true); if (data.IsSuccess) { - ValueByteBlock valueByteBlock = new(1024); + ValueByteBlock byteBlock = new(1024); try { if (modbusRtu) { - valueByteBlock.Write(Bytes.Slice(0, 2).Span); + byteBlock.Write(Bytes.Slice(0, 2).Span); if (modbusRequest.IsBitFunction) { - var bitdata = data.Content.ToArray().Select(m => m > 0).ToArray().BoolArrayToByte(); - ReadOnlyMemory bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? new ReadOnlyMemory(bitdata) : new ReadOnlyMemory(bitdata).Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0)); - valueByteBlock.WriteByte((byte)bitwritedata.Length); - valueByteBlock.Write(bitwritedata.Span); + var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte(); + ReadOnlyMemory bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? bitdata.AsMemory() : bitdata.AsMemory().Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0)); + WriterExtension.WriteValue(ref byteBlock, (byte)bitwritedata.Length); + byteBlock.Write(bitwritedata.Span); } else { - valueByteBlock.WriteByte((byte)data.Content.Length); - valueByteBlock.Write(data.Content.Span); + WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length); + byteBlock.Write(data.Content.Span); } - valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory.Span)); - await ReturnData(client, valueByteBlock.Memory, e).ConfigureAwait(false); + byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Memory.Span)); + await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); } else { - valueByteBlock.Write(Bytes.Slice(0, 8).Span); + byteBlock.Write(Bytes.Slice(0, 8).Span); if (modbusRequest.IsBitFunction) { - var bitdata = data.Content.ToArray().Select(m => m > 0).ToArray().BoolArrayToByte(); - ReadOnlyMemory bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? new ReadOnlyMemory(bitdata) : new ReadOnlyMemory(bitdata).Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0)); - valueByteBlock.WriteByte((byte)bitwritedata.Length); - valueByteBlock.Write(bitwritedata.Span); + var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte(); + ReadOnlyMemory bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? bitdata.AsMemory() : bitdata.AsMemory().Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0)); + WriterExtension.WriteValue(ref byteBlock, (byte)bitwritedata.Length); + byteBlock.Write(bitwritedata.Span); } else { - valueByteBlock.WriteByte((byte)data.Content.Length); - valueByteBlock.Write(data.Content.Span); + WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length); + byteBlock.Write(data.Content.Span); } - valueByteBlock[5] = (byte)(valueByteBlock.Length - 6); - await ReturnData(client, valueByteBlock.Memory, e).ConfigureAwait(false); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), 5); + await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); } } catch @@ -488,7 +507,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress } finally { - valueByteBlock.SafeDispose(); + byteBlock.SafeDispose(); } } else @@ -585,56 +604,56 @@ public class ModbusSlave : DeviceBase, IModbusAddress if (client is IUdpClientSender udpClientSender) await udpClientSender.SendAsync(((UdpReceivedDataEventArgs)e).EndPoint, sendData).ConfigureAwait(false); else - await client.SendAsync(sendData).ConfigureAwait(false); + await client.SendAsync(sendData, client.ClosedToken).ConfigureAwait(false); } private async Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlyMemory bytes, ReceivedDataEventArgs e) { - ValueByteBlock valueByteBlock = new(20); + ValueByteBlock byteBlock = new(20); try { if (modbusRtu) { - valueByteBlock.Write(bytes.Slice(0, 2).Span); - valueByteBlock.WriteByte(1); - valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Span)); - valueByteBlock[1] = (byte)(valueByteBlock[1] + 128); + byteBlock.Write(bytes.Slice(0, 2).Span); + WriterExtension.WriteValue(ref byteBlock, (byte)1); + byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span)); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[1] + 128), 1); } else { - valueByteBlock.Write(bytes.Slice(0, 8).Span); - valueByteBlock.WriteByte(1); - valueByteBlock[5] = (byte)(valueByteBlock.Length - 6); - valueByteBlock[7] = (byte)(valueByteBlock[7] + 128); + byteBlock.Write(bytes.Slice(0, 8).Span); + WriterExtension.WriteValue(ref byteBlock, (byte)1); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), 5); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[7] + 128), 7); } - await ReturnData(client, valueByteBlock.Memory, e).ConfigureAwait(false); + await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); } finally { - valueByteBlock.SafeDispose(); + byteBlock.SafeDispose(); } } private async Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlyMemory bytes, ReceivedDataEventArgs e) { - ValueByteBlock valueByteBlock = new(20); + ValueByteBlock byteBlock = new(20); try { if (modbusRtu) { - valueByteBlock.Write(bytes.Slice(0, 6).Span); - valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Span)); + byteBlock.Write(bytes.Slice(0, 6).Span); + byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span)); } else { - valueByteBlock.Write(bytes.Slice(0, 12).Span); - valueByteBlock[5] = (byte)(valueByteBlock.Length - 6); + byteBlock.Write(bytes.Slice(0, 12).Span); + ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), 5); } - await ReturnData(client, valueByteBlock.Memory, e).ConfigureAwait(false); + await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false); } finally { - valueByteBlock.SafeDispose(); + byteBlock.SafeDispose(); } } diff --git a/src/Plugin/ThingsGateway.Foundation.OpcDa/Utils/DictionaryExtension.cs b/src/Plugin/ThingsGateway.Foundation.OpcDa/Utils/DictionaryExtension.cs index b59499d4c..1358c9a99 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcDa/Utils/DictionaryExtension.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcDa/Utils/DictionaryExtension.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/DictionaryExtension.cs b/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/DictionaryExtension.cs index 9b83ce56f..d671e45bd 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/DictionaryExtension.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/DictionaryExtension.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 diff --git a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7BitConverter.cs b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7BitConverter.cs index 9eeed5c5e..e1ef428f2 100644 --- a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7BitConverter.cs +++ b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7BitConverter.cs @@ -28,8 +28,10 @@ public class S7BitConverter : ThingsGatewayBitConverter public bool SMART200 { get; set; } = false; public bool WStringEnable { get; set; } = false; public override int? StringLength { get; set; } = 100; + + /// - public override string ToString(byte[] buffer, int offset, int length) + public override string ToString(ReadOnlySpan buffer, int offset, int length) { if (WStringEnable) { diff --git a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Message.cs b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Message.cs index 99580a69b..a8cde2ace 100644 --- a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Message.cs +++ b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Message.cs @@ -28,25 +28,25 @@ public class S7Message : MessageBase, IResultMessage public override bool CheckHead(ref TByteBlock byteBlock) { byteBlock.Position += 2; - BodyLength = byteBlock.ReadUInt16(EndianType.Big) - 4; + BodyLength = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) - 4; return true; } public override FilterResult CheckBody(ref TByteBlock byteBlock) { var pos = byteBlock.Position; - - if (byteBlock[pos + 1] == 0xD0) // 首次握手0XD0连接确认 + var span = byteBlock.Span; + if (span[pos + 1] == 0xD0) // 首次握手0XD0连接确认 { OperCode = 0; return FilterResult.Success; } - else if (byteBlock[pos + 15] == 0xF0) // PDU + else if (span[pos + 15] == 0xF0) // PDU { // 其余情况判断错误代码 - if (byteBlock[pos + 13] + byteBlock[pos + 14] > 0) // 如果错误代码不为0 + if (span[pos + 13] + span[pos + 14] > 0) // 如果错误代码不为0 { OperCode = 999; - ErrorMessage = string.Format(AppResource.ReturnError, byteBlock[pos + 13].ToString("X2"), byteBlock[pos + 14].ToString("X2")); + ErrorMessage = string.Format(AppResource.ReturnError, span[pos + 13].ToString("X2"), span[pos + 14].ToString("X2")); return FilterResult.Success; } else @@ -58,22 +58,22 @@ public class S7Message : MessageBase, IResultMessage } //分bit/byte解析 - else if (byteBlock[pos + 15] == 0x04) // Read + else if (span[pos + 15] == 0x04) // Read { byteBlock.Position = pos + 7; - var sign = byteBlock.ReadUInt16(EndianType.Big);//数据ID标识 + var sign = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big);//数据ID标识 Sign = sign; byteBlock.Position = pos; - int length = byteBlock[pos + 17]; - int itemLen = byteBlock[pos + 16]; + int length = span[pos + 17]; + int itemLen = span[pos + 16]; //添加错误代码校验 // 其余情况判断错误代码 - if (byteBlock[pos + 13] + byteBlock[pos + 14] > 0) // 如果错误代码不为0 + if (span[pos + 13] + span[pos + 14] > 0) // 如果错误代码不为0 { OperCode = 999; - ErrorMessage = string.Format(AppResource.ReturnError, byteBlock[pos + 13].ToString("X2"), byteBlock[pos + 14].ToString("X2")); + ErrorMessage = string.Format(AppResource.ReturnError, span[pos + 13].ToString("X2"), span[pos + 14].ToString("X2")); return FilterResult.Success; } else @@ -84,10 +84,10 @@ public class S7Message : MessageBase, IResultMessage ErrorMessage = AppResource.DataLengthError; return FilterResult.Success; } - if (byteBlock[pos + 17] != byte.MaxValue) + if (span[pos + 17] != byte.MaxValue) { OperCode = 999; - ErrorMessage = string.Format(AppResource.ValidateDataError, byteBlock[pos + 17], SiemensHelper.GetCpuError(byteBlock[pos + 17])); + ErrorMessage = string.Format(AppResource.ValidateDataError, span[pos + 17], SiemensHelper.GetCpuError(span[pos + 17])); return FilterResult.Success; } @@ -95,24 +95,24 @@ public class S7Message : MessageBase, IResultMessage var dataIndex = pos + 17; for (int index = 0; index < itemLen; index++) { - if (byteBlock[dataIndex] != byte.MaxValue) + if (span[dataIndex] != byte.MaxValue) { OperCode = 999; - ErrorMessage = string.Format(AppResource.ValidateDataError, byteBlock[dataIndex], SiemensHelper.GetCpuError(byteBlock[dataIndex])); + ErrorMessage = string.Format(AppResource.ValidateDataError, span[dataIndex], SiemensHelper.GetCpuError(span[dataIndex])); return FilterResult.Success; } - if (byteBlock[dataIndex + 1] == 4)//Bit:3;Byte:4;Counter或者Timer:9 + if (span[dataIndex + 1] == 4)//Bit:3;Byte:4;Counter或者Timer:9 { byteBlock.Position = dataIndex + 2; - var byteLength = byteBlock.ReadUInt16(EndianType.Big) / 8; - data.Write(byteBlock.Span.Slice(dataIndex + 4, byteLength)); + var byteLength = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big) / 8; + data.Write(span.Slice(dataIndex + 4, byteLength)); dataIndex += byteLength + 4; } - else if (byteBlock[dataIndex + 1] == 9)//Counter或者Timer:9 + else if (span[dataIndex + 1] == 9)//Counter或者Timer:9 { byteBlock.Position = dataIndex + 2; - var byteLength = byteBlock.ReadUInt16(EndianType.Big); + var byteLength = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big); if (byteLength % 3 == 0) { for (int indexCT = 0; indexCT < byteLength / 3; indexCT++) @@ -136,17 +136,17 @@ public class S7Message : MessageBase, IResultMessage return FilterResult.Success; } } - else if (byteBlock[pos + 15] == 0x05) // Write + else if (span[pos + 15] == 0x05) // Write { byteBlock.Position = pos + 7; - var sign = byteBlock.ReadUInt16(EndianType.Big);//数据ID标识 + var sign = ReaderExtension.ReadValue(ref byteBlock, EndianType.Big);//数据ID标识 Sign = sign; byteBlock.Position = pos; - int itemLen = byteBlock[pos + 16]; - if (byteBlock[pos + 13] + byteBlock[pos + 14] > 0) // 如果错误代码不为0 + int itemLen = span[pos + 16]; + if (span[pos + 13] + span[pos + 14] > 0) // 如果错误代码不为0 { OperCode = 999; - ErrorMessage = string.Format(AppResource.ReturnError, byteBlock[pos + 13].ToString("X2"), byteBlock[pos + 14].ToString("X2")); + ErrorMessage = string.Format(AppResource.ReturnError, span[pos + 13].ToString("X2"), span[pos + 14].ToString("X2")); return FilterResult.Success; } if (byteBlock.Length < pos + 18) @@ -157,10 +157,10 @@ public class S7Message : MessageBase, IResultMessage } for (int i = 0; i < itemLen; i++) { - if (byteBlock[pos + 17 + i] != byte.MaxValue) + if (span[pos + 17 + i] != byte.MaxValue) { OperCode = 999; - ErrorMessage = string.Format(AppResource.ValidateDataError, byteBlock[pos + 17 + i], SiemensHelper.GetCpuError(byteBlock[pos + 17 + i])); + ErrorMessage = string.Format(AppResource.ValidateDataError, span[pos + 17 + i], SiemensHelper.GetCpuError(span[pos + 17 + i])); return FilterResult.Success; } } diff --git a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs index db8a5f94e..262367567 100644 --- a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs +++ b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Core/S7Send.cs @@ -31,7 +31,7 @@ public class S7Request /// /// 写入数据 /// - public byte[] Data { get; set; } + public ReadOnlyMemory Data { get; set; } /// /// 数据块代码 @@ -78,7 +78,7 @@ public class S7Send : ISendMessage public int MaxLength => 2048; public int Sign { get; set; } - public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlock + public void Build(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter { if (Handshake == true) { @@ -95,76 +95,76 @@ public class S7Send : ISendMessage } } - internal void GetReadCommand(ref TByteBlock valueByteBlock, SiemensS7Address[] siemensS7Address) where TByteBlock : IByteBlock + internal void GetReadCommand(ref TByteBlock byteBlock, SiemensS7Address[] siemensS7Address) where TByteBlock : IByteBlockWriter { byte len = (byte)siemensS7Address.Length; ushort telegramLen = (ushort)(len * 12 + 19); ushort parameterLen = (ushort)(len * 12 + 2); //TPKT - valueByteBlock.WriteByte(3);//版本 - valueByteBlock.WriteByte(0);//保留 - valueByteBlock.WriteUInt16(telegramLen, EndianType.Big);//长度,item.len*12+19 + WriterExtension.WriteValue(ref byteBlock, (byte)3);//版本 + WriterExtension.WriteValue(ref byteBlock, (byte)0);//保留 + WriterExtension.WriteValue(ref byteBlock, (ushort)telegramLen, EndianType.Big);//长度,item.len*12+19 //COTP信息 - valueByteBlock.WriteByte(2);//长度 - valueByteBlock.WriteByte(0xf0);//pdu类型 - valueByteBlock.WriteByte(0x80);//目标引用 + WriterExtension.WriteValue(ref byteBlock, (byte)2);//长度 + WriterExtension.WriteValue(ref byteBlock, (byte)0xf0);//pdu类型 + WriterExtension.WriteValue(ref byteBlock, (byte)0x80);//目标引用 //header - valueByteBlock.WriteByte(0x32);//协议id - valueByteBlock.WriteByte(0x01);//请求 - valueByteBlock.WriteUInt16(0x00, EndianType.Big);//冗余识别 - valueByteBlock.WriteUInt16((ushort)Sign, EndianType.Big);//数据ID标识 - valueByteBlock.WriteUInt16(parameterLen, EndianType.Big);//参数长度,item.len*12+2 - valueByteBlock.WriteUInt16(0x00, EndianType.Big);//数据长度,data.len+4 ,写入时填写,读取时为0 + WriterExtension.WriteValue(ref byteBlock, (byte)0x32);//协议id + WriterExtension.WriteValue(ref byteBlock, (byte)0x01);//请求 + WriterExtension.WriteValue(ref byteBlock, (ushort)0x00, EndianType.Big);//冗余识别 + WriterExtension.WriteValue(ref byteBlock, (ushort)Sign, EndianType.Big);//数据ID标识 + WriterExtension.WriteValue(ref byteBlock, (ushort)parameterLen, EndianType.Big);//参数长度,item.len*12+2 + WriterExtension.WriteValue(ref byteBlock, (ushort)0x00, EndianType.Big);//数据长度,data.len+4 ,写入时填写,读取时为0 //par - valueByteBlock.WriteByte(0x04);//功能码,4 Read Var, 5 Write Var - valueByteBlock.WriteByte(len);//Item数量 + WriterExtension.WriteValue(ref byteBlock, (byte)0x04);//功能码,4 Read Var, 5 Write Var + WriterExtension.WriteValue(ref byteBlock, (byte)len);//Item数量 //通信项构建 for (int index = 0; index < len; index++) { - valueByteBlock.WriteByte(0x12);//Var 规范 - valueByteBlock.WriteByte(0x0a);//剩余的字节长度 - valueByteBlock.WriteByte(0x10);//Syntax ID + WriterExtension.WriteValue(ref byteBlock, (byte)0x12);//Var 规范 + WriterExtension.WriteValue(ref byteBlock, (byte)0x0a);//剩余的字节长度 + WriterExtension.WriteValue(ref byteBlock, (byte)0x10);//Syntax ID if (siemensS7Address[index].DataCode == S7Area.CT || siemensS7Address[index].DataCode == S7Area.TM) { - valueByteBlock.WriteByte((byte)siemensS7Address[index].DataCode);//数据类型 + WriterExtension.WriteValue(ref byteBlock, (byte)siemensS7Address[index].DataCode);//数据类型 } else { - valueByteBlock.WriteByte((byte)S7WordLength.Byte);//数据类型 + WriterExtension.WriteValue(ref byteBlock, (byte)S7WordLength.Byte);//数据类型 } - valueByteBlock.WriteUInt16((ushort)siemensS7Address[index].Length, EndianType.Big);//读取长度 - valueByteBlock.WriteUInt16(siemensS7Address[index].DbBlock, EndianType.Big);//DB编号 - valueByteBlock.WriteByte((byte)siemensS7Address[index].DataCode);//数据块类型 - valueByteBlock.WriteByte((byte)(siemensS7Address[index].AddressStart / 256 / 256 % 256));//数据块偏移量 - valueByteBlock.WriteByte((byte)(siemensS7Address[index].AddressStart / 256 % 256));//数据块偏移量 - valueByteBlock.WriteByte((byte)(siemensS7Address[index].AddressStart % 256));//数据块偏移量 + WriterExtension.WriteValue(ref byteBlock, (ushort)siemensS7Address[index].Length, EndianType.Big);//读取长度 + WriterExtension.WriteValue(ref byteBlock, (ushort)siemensS7Address[index].DbBlock, EndianType.Big);//DB编号 + WriterExtension.WriteValue(ref byteBlock, (byte)siemensS7Address[index].DataCode);//数据块类型 + WriterExtension.WriteValue(ref byteBlock, (byte)(siemensS7Address[index].AddressStart / 256 / 256 % 256));//数据块偏移量 + WriterExtension.WriteValue(ref byteBlock, (byte)(siemensS7Address[index].AddressStart / 256 % 256));//数据块偏移量 + WriterExtension.WriteValue(ref byteBlock, (byte)(siemensS7Address[index].AddressStart % 256));//数据块偏移量 } } - internal void GetWriteByteCommand(ref TByteBlock valueByteBlock, SiemensS7Address[] addresss) where TByteBlock : IByteBlock + internal void GetWriteByteCommand(ref TByteBlock byteBlock, SiemensS7Address[] addresss) where TByteBlock : IByteBlockWriter { byte itemLen = (byte)addresss.Length; ushort parameterLen = (ushort)(itemLen * 12 + 2); //TPKT - valueByteBlock.WriteByte(3);//版本 - valueByteBlock.WriteByte(0); - valueByteBlock.WriteUInt16(0, EndianType.Big);//长度,item.len*12+19 + WriterExtension.WriteValue(ref byteBlock, (byte)3);//版本 + WriterExtension.WriteValue(ref byteBlock, (byte)0); + WriterExtension.WriteValue(ref byteBlock, (ushort)0, EndianType.Big);//长度,item.len*12+19 //COTP信息 - valueByteBlock.WriteByte(2);//长度 - valueByteBlock.WriteByte(0xf0);//pdu类型 - valueByteBlock.WriteByte(0x80);//目标引用 + WriterExtension.WriteValue(ref byteBlock, (byte)2);//长度 + WriterExtension.WriteValue(ref byteBlock, (byte)0xf0);//pdu类型 + WriterExtension.WriteValue(ref byteBlock, (byte)0x80);//目标引用 //header - valueByteBlock.WriteByte(0x32);//协议id - valueByteBlock.WriteByte(0x01);//请求 - valueByteBlock.WriteUInt16(0x00, EndianType.Big);//冗余识别 - valueByteBlock.WriteUInt16((ushort)Sign, EndianType.Big);//数据ID标识 - valueByteBlock.WriteUInt16(parameterLen, EndianType.Big);//参数长度,item.len*12+2 - valueByteBlock.WriteUInt16(0, EndianType.Big);//数据长度,data.len+4 ,写入时填写,读取时为0 + WriterExtension.WriteValue(ref byteBlock, (byte)0x32);//协议id + WriterExtension.WriteValue(ref byteBlock, (byte)0x01);//请求 + WriterExtension.WriteValue(ref byteBlock, (ushort)0x00, EndianType.Big);//冗余识别 + WriterExtension.WriteValue(ref byteBlock, (ushort)Sign, EndianType.Big);//数据ID标识 + WriterExtension.WriteValue(ref byteBlock, (ushort)parameterLen, EndianType.Big);//参数长度,item.len*12+2 + WriterExtension.WriteValue(ref byteBlock, (ushort)0, EndianType.Big);//数据长度,data.len+4 ,写入时填写,读取时为0 //par - valueByteBlock.WriteByte(0x05);//功能码,4 Read Var, 5 Write Var - valueByteBlock.WriteByte(itemLen);//Item数量 + WriterExtension.WriteValue(ref byteBlock, (byte)0x05);//功能码,4 Read Var, 5 Write Var + WriterExtension.WriteValue(ref byteBlock, (byte)itemLen);//Item数量 //写入Item与读取大致相同 foreach (var address in addresss) @@ -172,16 +172,16 @@ public class S7Send : ISendMessage var data = address.Data; byte len = (byte)address.Length; bool isBit = (address.IsBit && len == 1); - valueByteBlock.WriteByte(0x12);//Var 规范 - valueByteBlock.WriteByte(0x0a);//剩余的字节长度 - valueByteBlock.WriteByte(0x10);//Syntax ID - valueByteBlock.WriteByte(isBit ? (byte)S7WordLength.Bit : (byte)S7WordLength.Byte);//数据类型 - valueByteBlock.WriteUInt16(len, EndianType.Big);//长度 - valueByteBlock.WriteUInt16(address.DbBlock, EndianType.Big);//DB编号 - valueByteBlock.WriteByte((byte)address.DataCode);//数据块类型 - valueByteBlock.WriteByte((byte)((address.AddressStart + address.BitCode) / 256 / 256));//数据块偏移量 - valueByteBlock.WriteByte((byte)((address.AddressStart + address.BitCode) / 256));//数据块偏移量 - valueByteBlock.WriteByte((byte)((address.AddressStart + address.BitCode) % 256));//数据块偏移量 + WriterExtension.WriteValue(ref byteBlock, (byte)0x12);//Var 规范 + WriterExtension.WriteValue(ref byteBlock, (byte)0x0a);//剩余的字节长度 + WriterExtension.WriteValue(ref byteBlock, (byte)0x10);//Syntax ID + WriterExtension.WriteValue(ref byteBlock, (byte)(isBit ? (byte)S7WordLength.Bit : (byte)S7WordLength.Byte));//数据类型 + WriterExtension.WriteValue(ref byteBlock, (ushort)len, EndianType.Big);//长度 + WriterExtension.WriteValue(ref byteBlock, (ushort)address.DbBlock, EndianType.Big);//DB编号 + WriterExtension.WriteValue(ref byteBlock, (byte)address.DataCode);//数据块类型 + WriterExtension.WriteValue(ref byteBlock, (byte)((address.AddressStart + address.BitCode) / 256 / 256));//数据块偏移量 + WriterExtension.WriteValue(ref byteBlock, (byte)((address.AddressStart + address.BitCode) / 256));//数据块偏移量 + WriterExtension.WriteValue(ref byteBlock, (byte)((address.AddressStart + address.BitCode) % 256));//数据块偏移量 } ushort dataLen = 0; //data @@ -192,18 +192,18 @@ public class S7Send : ISendMessage bool isBit = (address.IsBit && len == 1); data = data.ArrayExpandToLengthEven(); //后面跟的是写入的数据信息 - valueByteBlock.WriteByte(0); - valueByteBlock.WriteByte((byte)(isBit ? address.DataCode == S7Area.CT ? 9 : 3 : 4));//Bit:3;Byte:4;Counter或者Timer:9 - valueByteBlock.WriteUInt16((ushort)(isBit ? (byte)address.BitLength : len * 8), EndianType.Big); - valueByteBlock.Write(data); + WriterExtension.WriteValue(ref byteBlock, (byte)0); + WriterExtension.WriteValue(ref byteBlock, (byte)(isBit ? address.DataCode == S7Area.CT ? 9 : 3 : 4));//Bit:3;Byte:4;Counter或者Timer:9 + WriterExtension.WriteValue(ref byteBlock, (ushort)(isBit ? (byte)address.BitLength : len * 8), EndianType.Big); + byteBlock.Write(data.Span); dataLen = (ushort)(dataLen + data.Length + 4); } ushort telegramLen = (ushort)(itemLen * 12 + 19 + dataLen); - valueByteBlock.Position = 2; - valueByteBlock.WriteUInt16(telegramLen, EndianType.Big);//长度 - valueByteBlock.Position = 15; - valueByteBlock.WriteUInt16(dataLen, EndianType.Big);//长度 - valueByteBlock.SeekToEnd(); + byteBlock.Position = 2; + WriterExtension.WriteValue(ref byteBlock, (ushort)telegramLen, EndianType.Big);//长度 + byteBlock.Position = 15; + WriterExtension.WriteValue(ref byteBlock, (ushort)dataLen, EndianType.Big);//长度 + byteBlock.Position = byteBlock.Length; } } diff --git a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/S7DateTime.cs b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/S7DateTime.cs index 853693563..c04fea5d2 100644 --- a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/S7DateTime.cs +++ b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/S7DateTime.cs @@ -34,7 +34,7 @@ public static class S7DateTime /// Thrown when the length of /// is not 8 or any value in /// is outside the valid range of values. - public static System.DateTime FromByteArray(this byte[]? bytes) + public static System.DateTime FromByteArray(this ReadOnlySpan bytes) { return FromByteArrayImpl(bytes); } @@ -47,7 +47,7 @@ public static class S7DateTime /// Thrown when the length of /// is not a multiple of 8 or any value in /// is outside the valid range of values. - public static System.DateTime[] ToArray(byte[] bytes) + public static System.DateTime[] ToArray(ReadOnlySpan bytes) { if (bytes.Length % 8 != 0) throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length, @@ -57,7 +57,7 @@ public static class S7DateTime var result = new System.DateTime[bytes.Length / 8]; for (var i = 0; i < cnt; i++) - result[i] = FromByteArrayImpl(new ArraySegment(bytes, i * 8, 8)); + result[i] = FromByteArrayImpl(bytes.Slice(i * 8, 8)); return result; } @@ -118,13 +118,13 @@ public static class S7DateTime return bytes.ToArray(); } - private static System.DateTime FromByteArrayImpl(IList? bytes) + private static System.DateTime FromByteArrayImpl(ReadOnlySpan bytes) { if (bytes == null) throw new ArgumentNullException(nameof(bytes)); - if (bytes.Count != 8) - throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Count, - $"Parsing a DateTime requires exactly 8 bytes of input data, input data is {bytes.Count} bytes long."); + if (bytes.Length != 8) + throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length, + $"Parsing a DateTime requires exactly 8 bytes of input data, input data is {bytes.Length} bytes long."); int DecodeBcd(byte input) => 10 * (input >> 4) + (input & 0b00001111); diff --git a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/SiemensHelper.cs b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/SiemensHelper.cs index 652fbc056..e042334d6 100644 --- a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/SiemensHelper.cs +++ b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/Helper/SiemensHelper.cs @@ -10,6 +10,8 @@ using System.Text; +using ThingsGateway.NewLife.Extension; + namespace ThingsGateway.Foundation.SiemensS7; internal sealed partial class SiemensHelper @@ -75,18 +77,19 @@ internal sealed partial class SiemensHelper { return new OperResult(result1); } - if (result1.Content[0] == 0 || result1.Content[0] == byte.MaxValue) + var span = result1.Content.Span; + if (span[0] == 0 || span[0] == byte.MaxValue) { return new OperResult(AppResource.NotString); } - var result2 = await plc.ReadAsync(address, 2 + result1.Content[1], cancellationToken).ConfigureAwait(false); + var result2 = await plc.ReadAsync(address, 2 + span[1], cancellationToken).ConfigureAwait(false); if (!result2.IsSuccess) { return new OperResult(result2); } else { - return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 2, result2.Content.Length - 2)); + return OperResult.CreateSuccessResult(encoding.GetString(result2.Content.Span.Slice(2, result2.Content.Length - 2))); } } else @@ -94,14 +97,15 @@ internal sealed partial class SiemensHelper var result1 = await plc.ReadAsync(address, 1, cancellationToken).ConfigureAwait(false); if (!result1.IsSuccess) return new OperResult(result1); - var result2 = await plc.ReadAsync(address, 1 + result1.Content[0], cancellationToken).ConfigureAwait(false); + var span = result1.Content.Span; + var result2 = await plc.ReadAsync(address, 1 + span[0], cancellationToken).ConfigureAwait(false); if (!result2.IsSuccess) { return new OperResult(result2); } else { - return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 1, result2.Content.Length - 1)); + return OperResult.CreateSuccessResult(encoding.GetString(result2.Content.Span.Slice(1, result2.Content.Length - 1))); } } } @@ -114,12 +118,14 @@ internal sealed partial class SiemensHelper { var result = await plc.ReadAsync(address, 2, cancellationToken).ConfigureAwait(false); if (!result.IsSuccess) return result; - if (result.Content[0] == byte.MaxValue) return new OperResult(AppResource.NotString); - if (result.Content[0] == 0) result.Content[0] = 254; - if (value.Length > result.Content[0]) return new OperResult(AppResource.WriteDataLengthMore); + var span = result.Content.Span; + var len = span[0]; + if (len == byte.MaxValue) return new OperResult(AppResource.NotString); + if (len == 0) len = 254; + if (value.Length > span[0]) return new OperResult(AppResource.WriteDataLengthMore); return await plc.WriteAsync( address, - DataTransUtil.SpliceArray([result.Content[0], (byte)value.Length], + DataTransUtil.SpliceArray([len, (byte)value.Length], inBytes ), DataTypeEnum.String, cancellationToken).ConfigureAwait(false); } @@ -137,18 +143,19 @@ internal sealed partial class SiemensHelper { return new OperResult(result1); } - if (result1.Content[0] == 0 || result1.Content[0] == byte.MaxValue) + var span = result1.Content.Span; + if (span[0] == 0 || span[0] == byte.MaxValue) { return new OperResult(AppResource.NotString); } - var result2 = await plc.ReadAsync(address, 4 + (plc.ThingsGatewayBitConverter.ToUInt16(result1.Content, 2) * 2), cancellationToken).ConfigureAwait(false); + var result2 = await plc.ReadAsync(address, 4 + (plc.ThingsGatewayBitConverter.ToUInt16(span, 2) * 2), cancellationToken).ConfigureAwait(false); if (!result2.IsSuccess) { return new OperResult(result2); } else { - return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 4, result2.Content.Length - 4)); + return OperResult.CreateSuccessResult(encoding.GetString(result2.Content.Span.Slice(4, result2.Content.Length - 4))); } } else @@ -157,14 +164,14 @@ internal sealed partial class SiemensHelper var result1 = await plc.ReadAsync(address, 1, cancellationToken).ConfigureAwait(false); if (!result1.IsSuccess) return new OperResult(result1); - var result2 = await plc.ReadAsync(address, 1 + (result1.Content[0] * 2), cancellationToken).ConfigureAwait(false); + var result2 = await plc.ReadAsync(address, 1 + (result1.Content.Span[0] * 2), cancellationToken).ConfigureAwait(false); if (!result2.IsSuccess) { return new OperResult(result2); } else { - return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 1, result2.Content.Length - 1)); + return OperResult.CreateSuccessResult(encoding.GetString(result2.Content.Span.Slice(1, result2.Content.Length - 1))); } } } @@ -177,7 +184,7 @@ internal sealed partial class SiemensHelper byte[] inBytes1 = (encoding ?? Encoding.BigEndianUnicode).GetBytes(value); var result = await plc.ReadAsync(address, 4, cancellationToken).ConfigureAwait(false); if (!result.IsSuccess) return result; - var num = plc.ThingsGatewayBitConverter.ToUInt16(result.Content, 0); + var num = plc.ThingsGatewayBitConverter.ToUInt16(result.Content.Span, 0); if (num == 0) num = 254; if (value.Length > num) return new OperResult(AppResource.WriteDataLengthMore); diff --git a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs index df3436716..dc05260b4 100644 --- a/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs +++ b/src/Plugin/ThingsGateway.Foundation.SiemensS7/S7/SiemensS7Master.cs @@ -139,7 +139,7 @@ public partial class SiemensS7Master : DeviceBase /// /// 此方法并不会智能分组以最大化效率,减少传输次数,因为返回值是byte[],所以一切都按地址数组的顺序执行,最后合并数组 /// - public async ValueTask> S7ReadAsync(SiemensS7Address[] sAddresss, CancellationToken cancellationToken = default) + public async ValueTask>> S7ReadAsync(SiemensS7Address[] sAddresss, CancellationToken cancellationToken = default) { { var byteBlock = new ValueByteBlock(2048); @@ -160,7 +160,7 @@ public partial class SiemensS7Master : DeviceBase var result = await SendThenReturnAsync(new S7Send([sAddress], true), cancellationToken: cancellationToken).ConfigureAwait(false); if (!result.IsSuccess) return result; - byteBlock.Write(result.Content); + byteBlock.Write(result.Content.Span); num += len; if (sAddress.DataCode == S7Area.TM || sAddress.DataCode == S7Area.CT) @@ -179,11 +179,11 @@ public partial class SiemensS7Master : DeviceBase } } - return new OperResult() { Content = byteBlock.ToArray() }; + return new OperResult>() { Content = byteBlock.ToArray() }; } catch (Exception ex) { - return new OperResult(ex); + return new OperResult>(ex); } finally { @@ -293,7 +293,7 @@ public partial class SiemensS7Master : DeviceBase #region 读写 /// - public override ValueTask> ReadAsync(string address, int length, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) { try { @@ -302,11 +302,11 @@ public partial class SiemensS7Master : DeviceBase } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } - public override ValueTask> ReadAsync(object state, CancellationToken cancellationToken = default) + public override ValueTask>> ReadAsync(object state, CancellationToken cancellationToken = default) { try { @@ -316,17 +316,17 @@ public partial class SiemensS7Master : DeviceBase } else { - return EasyValueTask.FromResult(new OperResult(new ArgumentException("State must be of type SiemensS7Address", nameof(state)))); + return EasyValueTask.FromResult(new OperResult>(new ArgumentException("State must be of type SiemensS7Address", nameof(state)))); } } catch (Exception ex) { - return EasyValueTask.FromResult(new OperResult(ex)); + return EasyValueTask.FromResult(new OperResult>(ex)); } } /// - public override async ValueTask WriteAsync(string address, byte[] value, DataTypeEnum dataType, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, DataTypeEnum dataType, CancellationToken cancellationToken = default) { try { @@ -342,12 +342,12 @@ public partial class SiemensS7Master : DeviceBase } /// - public override async ValueTask WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) + public override async ValueTask WriteAsync(string address, ReadOnlyMemory value, CancellationToken cancellationToken = default) { try { var sAddress = SiemensS7Address.ParseFrom(address); - sAddress.Data = value.BoolArrayToByte(); + sAddress.Data = value.Span.BoolArrayToByte(); sAddress.Length = sAddress.Data.Length; sAddress.BitLength = value.Length; sAddress.IsBit = true; @@ -437,7 +437,7 @@ public partial class SiemensS7Master : DeviceBase try { - var result2 = await SendThenReturnMessageBaseAsync(new S7Send(ISO_CR), channel).ConfigureAwait(false); + var result2 = await SendThenReturnMessageAsync(new S7Send(ISO_CR), channel, channel.ClosedToken).ConfigureAwait(false); if (!result2.IsSuccess) { await channel.CloseAsync().ConfigureAwait(false); @@ -455,7 +455,7 @@ public partial class SiemensS7Master : DeviceBase } try { - var result2 = await SendThenReturnMessageBaseAsync(new S7Send(S7_PN), channel).ConfigureAwait(false); + var result2 = await SendThenReturnMessageAsync(new S7Send(S7_PN), channel, channel.ClosedToken).ConfigureAwait(false); if (!result2.IsSuccess) { await channel.CloseAsync().ConfigureAwait(false); @@ -463,12 +463,12 @@ public partial class SiemensS7Master : DeviceBase Logger?.LogWarning(string.Format(AppResource.HandshakeError2, channel.ToString(), result2)); return true; } - if (result2.Content == null) + if (result2.Content.IsEmpty) { await channel.CloseAsync().ConfigureAwait(false); return true; } - PduLength = ThingsGatewayBitConverter.ToUInt16(result2.Content, 0) - 28; + PduLength = ThingsGatewayBitConverter.ToUInt16(result2.Content.Span, 0) - 28; Logger?.LogInformation($"PduLength:{PduLength}"); PduLength = PduLength < 200 ? 200 : PduLength; } @@ -508,7 +508,7 @@ public partial class SiemensS7Master : DeviceBase { return (await ReadAsync(address, 2, cancellationToken).ConfigureAwait(false)). Then(m => OperResult.CreateSuccessResult(S7DateTime.SpecMinimumDateTime.AddDays( - ThingsGatewayBitConverter.ToUInt16(m, 0))) + ThingsGatewayBitConverter.ToUInt16(m.Span, 0))) ); } @@ -518,7 +518,7 @@ public partial class SiemensS7Master : DeviceBase /// public async ValueTask> ReadDateTimeAsync(string address, CancellationToken cancellationToken) { - return OperResultExtension.GetResultFromBytes(await ReadAsync(address, 8, cancellationToken).ConfigureAwait(false), S7DateTime.FromByteArray); + return OperResultExtension.GetResultFromBytes(await ReadAsync(address, 8, cancellationToken).ConfigureAwait(false), (a) => S7DateTime.FromByteArray(a.Span)); } /// diff --git a/src/Plugin/ThingsGateway.Foundation.Test/Dlt645Test.cs b/src/Plugin/ThingsGateway.Foundation.Test/Dlt645Test.cs index 49829c82e..0d44a4b28 100644 --- a/src/Plugin/ThingsGateway.Foundation.Test/Dlt645Test.cs +++ b/src/Plugin/ThingsGateway.Foundation.Test/Dlt645Test.cs @@ -8,6 +8,8 @@ // QQ群:605534569 // ------------------------------------------------------------------------------ +using System.Diagnostics; + using ThingsGateway.Foundation.Dlt645; using ThingsGateway.Foundation.Extension.String; @@ -21,14 +23,21 @@ public class Dlt645Test [InlineData("02010100", "FE FE FE FE 68 11 11 11 11 11 11 68 91 07 33 34 34 35 33 59 36 60 16 ")] public async Task Dlt645_Read_OK(string address, string data) { - byte[] bytes = data.HexStringToBytes(); var dltChannel = new TouchSocketConfig().GetChannel(new ChannelOptions() { ChannelType = ChannelTypeEnum.Other }) as IClientChannel; + dltChannel.Config.ConfigureContainer(a => + { + a.AddEasyLogger((a, b, c, d) => + { + Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}"); + }, LogLevel.Trace); + }); var dltMaster = new Dlt645_2007Master() { Timeout = 10000, Station = "111111111111" }; dltMaster.InitChannel(dltChannel); await dltChannel.SetupAsync(dltChannel.Config); + await dltChannel.ConnectAsync(dltChannel.ChannelOptions.ConnectTimeout, CancellationToken.None); var adapter = dltChannel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter; var task1 = Task.Run(async () => @@ -39,9 +48,10 @@ public class Dlt645Test await Task.Delay(50); var task2 = Task.Run(async () => { + var bytes = data.HexStringToBytes().GetArray(); foreach (var item in bytes) { - var data = new ByteBlock([item]); + var data = new ByteBlock(1); data.WriteByte(item); await adapter.ReceivedInputAsync(data).ConfigureAwait(false); } }); diff --git a/src/Plugin/ThingsGateway.Foundation.Test/ModbusTest.cs b/src/Plugin/ThingsGateway.Foundation.Test/ModbusTest.cs index 9d5e55561..b68ea0cf6 100644 --- a/src/Plugin/ThingsGateway.Foundation.Test/ModbusTest.cs +++ b/src/Plugin/ThingsGateway.Foundation.Test/ModbusTest.cs @@ -8,6 +8,8 @@ // QQ群:605534569 // ------------------------------------------------------------------------------ +using System.Diagnostics; + using ThingsGateway.Foundation.Extension.String; using ThingsGateway.Foundation.Modbus; @@ -26,14 +28,21 @@ public class ModbusTest [InlineData("000045", false, "0000000000060105002CFF00", "true", DataTypeEnum.Boolean)] public async Task ModbusTcp_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16) { - byte[] bytes = data.HexStringToBytes(); var modbusChannel = new TouchSocketConfig().GetChannel(new ChannelOptions() { ChannelType = ChannelTypeEnum.Other }) as IClientChannel; + modbusChannel.Config.ConfigureContainer(a => + { + a.AddEasyLogger((a, b, c, d) => + { + Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}"); + }, LogLevel.Trace); + }); var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusTcp, Timeout = 10000 }; modbusMaster.InitChannel(modbusChannel); await modbusChannel.SetupAsync(modbusChannel.Config); + await modbusChannel.ConnectAsync(modbusChannel.ChannelOptions.ConnectTimeout, CancellationToken.None); var adapter = modbusChannel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter; var task1 = Task.Run(async () => @@ -45,16 +54,17 @@ public class ModbusTest } else { - var result = await modbusMaster.WriteAsync(address, JTokenUtil.GetJTokenFromString(writeData), dataTypeEnum).ConfigureAwait(false); + var result = await modbusMaster.WriteJTokenAsync(address, JTokenUtil.GetJTokenFromString(writeData), dataTypeEnum).ConfigureAwait(false); Assert.True(result.IsSuccess, result.ToString()); } }); await Task.Delay(50); var task2 = Task.Run(async () => { + var bytes = data.HexStringToBytes().GetArray(); foreach (var item in bytes) { - var data = new ByteBlock([item]); + var data = new ByteBlock(1); data.WriteByte(item); await adapter.ReceivedInputAsync(data).ConfigureAwait(false); } }); @@ -70,14 +80,21 @@ public class ModbusTest [InlineData("000045", false, "0105002CFF004DF3", "true", DataTypeEnum.Boolean)] public async Task ModbusRtu_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16) { - byte[] bytes = data.HexStringToBytes(); var modbusChannel = new TouchSocketConfig().GetChannel(new ChannelOptions() { ChannelType = ChannelTypeEnum.Other }) as IClientChannel; + modbusChannel.Config.ConfigureContainer(a => + { + a.AddEasyLogger((a, b, c, d) => + { + Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}"); + }, LogLevel.Trace); + }); var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusRtu, Timeout = 10000, Station = 1 }; modbusMaster.InitChannel(modbusChannel); await modbusChannel.SetupAsync(modbusChannel.Config); + await modbusChannel.ConnectAsync(modbusChannel.ChannelOptions.ConnectTimeout, CancellationToken.None); var adapter = modbusChannel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter; var task1 = Task.Run(async () => @@ -89,17 +106,18 @@ public class ModbusTest } else { - var result = await modbusMaster.WriteAsync(address, JTokenUtil.GetJTokenFromString(writeData), dataTypeEnum).ConfigureAwait(false); + var result = await modbusMaster.WriteJTokenAsync(address, JTokenUtil.GetJTokenFromString(writeData), dataTypeEnum).ConfigureAwait(false); Assert.True(result.IsSuccess, result.ToString()); } }); await Task.Delay(50); var task2 = Task.Run(async () => { + var bytes = data.HexStringToBytes().GetArray(); foreach (var item in bytes) { - var data = new ByteBlock([item]); - await adapter.ReceivedInputAsync(data).ConfigureAwait(false); + var data = new ByteBlock(1); data.WriteByte(item); + await (adapter).ReceivedInputAsync(data).ConfigureAwait(false); } }); await Task.WhenAll(task1, task2); diff --git a/src/Plugin/ThingsGateway.Foundation.Test/SiemensS7Test.cs b/src/Plugin/ThingsGateway.Foundation.Test/SiemensS7Test.cs index 2642e3a41..98dc0bb02 100644 --- a/src/Plugin/ThingsGateway.Foundation.Test/SiemensS7Test.cs +++ b/src/Plugin/ThingsGateway.Foundation.Test/SiemensS7Test.cs @@ -8,6 +8,8 @@ // QQ群:605534569 // ------------------------------------------------------------------------------ +using System.Diagnostics; + using ThingsGateway.Foundation.Extension.String; using ThingsGateway.Foundation.SiemensS7; using ThingsGateway.NewLife.Extension; @@ -24,14 +26,22 @@ public class SiemensS7Test [InlineData("M100", false, "03 00 00 16 02 F0 80 32 03 00 00 00 03 00 02 00 01 00 00 05 01 FF", "1", DataTypeEnum.UInt16)] public async Task SiemensS7_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16) { - byte[] bytes = data.HexStringToBytes(); var siemensS7Channel = new TouchSocketConfig().GetChannel(new ChannelOptions() { ChannelType = ChannelTypeEnum.Other }) as IClientChannel; + siemensS7Channel.Config.ConfigureContainer(a => + { + a.AddEasyLogger((a, b, c, d) => + { + Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}"); + }, LogLevel.Trace); + }); + var siemensS7Master = new SiemensS7Master() { SiemensS7Type = SiemensTypeEnum.S1200, Timeout = 10000 }; siemensS7Master.InitChannel(siemensS7Channel); await siemensS7Channel.SetupAsync(siemensS7Channel.Config); + await siemensS7Channel.ConnectAsync(siemensS7Channel.ChannelOptions.ConnectTimeout, CancellationToken.None); var adapter = siemensS7Channel.ReadOnlyDataHandlingAdapter as SingleStreamDataHandlingAdapter; await siemensS7Master.ConnectAsync(CancellationToken.None); var task1 = Task.Run(async () => @@ -43,17 +53,18 @@ public class SiemensS7Test } else { - var result = await siemensS7Master.WriteAsync(address, JTokenUtil.GetJTokenFromString(writeData), dataTypeEnum).ConfigureAwait(false); + var result = await siemensS7Master.WriteJTokenAsync(address, JTokenUtil.GetJTokenFromString(writeData), dataTypeEnum).ConfigureAwait(false); Assert.True(result.IsSuccess, result.ToString()); } }); var task2 = Task.Run(async () => { await Task.Delay(1000).ConfigureAwait(false); + var bytes = data.HexStringToBytes().GetArray(); bytes[12] = (byte)(((IClientChannel)(siemensS7Master.Channel)).WaitHandlePool.GetValue("m_currentSign").ToInt() - 1); foreach (var item in bytes) { - var data = new ByteBlock([item]); + var data = new ByteBlock(1); data.WriteByte(item); await adapter.ReceivedInputAsync(data).ConfigureAwait(false); } }); diff --git a/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs b/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs index 6fb845d44..84b975f51 100644 --- a/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs +++ b/src/Plugin/ThingsGateway.Plugin.Modbus/ModbusSlave/ModbusSlave.cs @@ -177,11 +177,11 @@ public class ModbusSlave : BusinessBase var type = variableRuntime.GetPropertyValue(CurrentDevice.Id, nameof(ModbusSlaveVariableProperty.DataType)); if (Enum.TryParse(type, out DataTypeEnum result)) { - await _plc.WriteAsync(item.Key, JToken.FromObject(variableRuntime.Value), result, cancellationToken).ConfigureAwait(false); + await _plc.WriteJTokenAsync(item.Key, JToken.FromObject(variableRuntime.Value), result, cancellationToken).ConfigureAwait(false); } else { - await _plc.WriteAsync(item.Key, JToken.FromObject(variableRuntime.Value), variableRuntime.DataType, cancellationToken).ConfigureAwait(false); + await _plc.WriteJTokenAsync(item.Key, JToken.FromObject(variableRuntime.Value), variableRuntime.DataType, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs b/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs index d5351e927..8aedf9abe 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcDa/OpcDaMaster/OpcDaMaster.cs @@ -138,16 +138,16 @@ public class OpcDaMaster : CollectBase } /// - protected override ValueTask> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) + protected override ValueTask>> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) { try { _plc.ReadItemsWithGroup(deviceVariableSourceRead.RegisterAddress); - return ValueTask.FromResult(OperResult.CreateSuccessResult(Array.Empty())); + return ValueTask.FromResult(OperResult.CreateSuccessResult(ReadOnlyMemory.Empty)); } catch (Exception ex) { - return ValueTask.FromResult(new OperResult($"ReadSourceAsync Error:{Environment.NewLine}{ex}")); + return ValueTask.FromResult(new OperResult>($"ReadSourceAsync Error:{Environment.NewLine}{ex}")); } } diff --git a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs index dda9a4dae..c43ebf739 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaMaster/OpcUaMaster.cs @@ -224,7 +224,7 @@ public class OpcUaMaster : CollectBase } /// - protected override async ValueTask> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) + protected override async ValueTask>> ReadSourceAsync(VariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken) { DateTime time = DateTime.Now; var addresss = deviceVariableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToArray(); @@ -264,11 +264,11 @@ public class OpcUaMaster : CollectBase } } - return OperResult.CreateSuccessResult(null); + return OperResult.CreateSuccessResult>(null); } catch (Exception ex) { - return new OperResult($"ReadSourceAsync {addresss.ToSystemTextJsonString()}:{Environment.NewLine}{ex}"); + return new OperResult>($"ReadSourceAsync {addresss.ToSystemTextJsonString()}:{Environment.NewLine}{ex}"); } } diff --git a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/Core/ThingsGatewayNodeManager.cs b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/Core/ThingsGatewayNodeManager.cs index 6a8a5d57a..1bf681212 100644 --- a/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/Core/ThingsGatewayNodeManager.cs +++ b/src/Plugin/ThingsGateway.Plugin.OpcUa/OpcUaServer/Core/ThingsGatewayNodeManager.cs @@ -621,6 +621,9 @@ public class ThingsGatewayNodeManager : CustomNodeManager2 } #region 多写 + + private object lockObject = new(); + public override void Write(OperationContext context, IList nodesToWrite, IList errors) { if (nodesToWrite.Any(a => a.AttributeId != Attributes.Value)) @@ -633,7 +636,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2 IDictionary operationCache = new NodeIdDictionary(); List nodesToValidate = new List(); - lock (Lock) + lock (lockObject) { bool[] writeEnable = new bool[nodesToWrite.Count]; Dictionary hashSetNodeId = new(); diff --git a/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs b/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs index 9e228a642..8a98ff4ae 100644 --- a/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs +++ b/src/Plugin/ThingsGateway.Plugin.SiemensS7/SiemensS7Master/SiemensS7Master.cs @@ -79,42 +79,16 @@ public class SiemensS7Master : CollectFoundationBase /// /// /// - public byte[] GetBytes(DataTypeEnum dataType, JToken value) + public ReadOnlyMemory GetBytes(DataTypeEnum dataType, JToken value) { //排除字符串 - if (value is JArray jArray) + if (value is JArray jArray && jArray.Count > 1) { - return dataType switch - { - DataTypeEnum.Boolean => jArray.ToObject().BoolArrayToByte(), - DataTypeEnum.Byte => jArray.ToObject(), - DataTypeEnum.Int16 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - DataTypeEnum.UInt16 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - DataTypeEnum.Int32 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - DataTypeEnum.UInt32 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - DataTypeEnum.Int64 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - DataTypeEnum.UInt64 => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - DataTypeEnum.Single => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - DataTypeEnum.Double => _plc.ThingsGatewayBitConverter.GetBytes(jArray.ToObject()), - _ => throw new(string.Format(ThingsGateway.Foundation.AppResource.DataTypeNotSupported, dataType)), - }; + return this._plc.ThingsGatewayBitConverter.GetBytesFormData(value, dataType, true); } else { - return dataType switch - { - DataTypeEnum.Boolean => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.Byte => [value.ToObject()], - DataTypeEnum.Int16 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.UInt16 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.Int32 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.UInt32 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.Int64 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.UInt64 => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.Single => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - DataTypeEnum.Double => _plc.ThingsGatewayBitConverter.GetBytes(value.ToObject()), - _ => throw new(string.Format(ThingsGateway.Foundation.AppResource.DataTypeNotSupported, dataType)), - }; + return this._plc.ThingsGatewayBitConverter.GetBytesFormData(value, dataType, false); } } @@ -167,7 +141,7 @@ public class SiemensS7Master : CollectFoundationBase try { // 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果 - 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); // 将操作结果添加到结果字典中,使用变量名称作为键 operResults.TryAdd(writeInfo.Key.Name, result); diff --git a/src/ThingsGateway.Server/Configuration/Website.Demo.json b/src/ThingsGateway.Server/Configuration/Website.Demo.json index f91c6fcf9..1c1dc2612 100644 --- a/src/ThingsGateway.Server/Configuration/Website.Demo.json +++ b/src/ThingsGateway.Server/Configuration/Website.Demo.json @@ -10,6 +10,8 @@ "Title": "ThingsGateway", "Demo": true, "ShowAuthorize": true, - "WebPageEnable": true + "WebPageEnable": true, + "BlazorConnectionLimitEnable": false, + "MaxBlazorConnections": 5 } } \ No newline at end of file diff --git a/src/ThingsGateway.Server/Configuration/Website.PostgreSQL .json b/src/ThingsGateway.Server/Configuration/Website.PostgreSQL .json index 6dee73f59..e64e100d1 100644 --- a/src/ThingsGateway.Server/Configuration/Website.PostgreSQL .json +++ b/src/ThingsGateway.Server/Configuration/Website.PostgreSQL .json @@ -10,6 +10,8 @@ "Title": "ThingsGateway", "Demo": false, "ShowAuthorize": true, - "WebPageEnable": true + "WebPageEnable": true, + "BlazorConnectionLimitEnable": false, + "MaxBlazorConnections": 5 } } \ No newline at end of file diff --git a/src/ThingsGateway.Server/Configuration/Website.json b/src/ThingsGateway.Server/Configuration/Website.json index 6dee73f59..e64e100d1 100644 --- a/src/ThingsGateway.Server/Configuration/Website.json +++ b/src/ThingsGateway.Server/Configuration/Website.json @@ -10,6 +10,8 @@ "Title": "ThingsGateway", "Demo": false, "ShowAuthorize": true, - "WebPageEnable": true + "WebPageEnable": true, + "BlazorConnectionLimitEnable": false, + "MaxBlazorConnections": 5 } } \ No newline at end of file diff --git a/src/ThingsGateway.Server/Program/Startup.cs b/src/ThingsGateway.Server/Program/Startup.cs index 60c0a9267..c5d2bade1 100644 --- a/src/ThingsGateway.Server/Program/Startup.cs +++ b/src/ThingsGateway.Server/Program/Startup.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server.Circuits; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; @@ -37,6 +38,8 @@ public class Startup : AppStartup { public void ConfigBlazorServer(IServiceCollection services) { + + // 增加中文编码支持网页源码显示汉字 services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All)); //并发启动/停止host @@ -174,7 +177,11 @@ public class Startup : AppStartup a.LoginPath = "/Account/Login/"; }); - var websiteOptions = App.GetOptions()!; + var websiteOptions = App.GetConfig("Website"); + if (websiteOptions.BlazorConnectionLimitEnable) + { + services.AddSingleton(); + } if (websiteOptions.Demo) { authenticationBuilder.AddOAuth>("Gitee", "Gitee", options => diff --git a/src/ThingsGateway.Server/Program/TestCollectPlugin.cs b/src/ThingsGateway.Server/Program/TestCollectPlugin.cs index 14bf8213b..880ef21d0 100644 --- a/src/ThingsGateway.Server/Program/TestCollectPlugin.cs +++ b/src/ThingsGateway.Server/Program/TestCollectPlugin.cs @@ -226,7 +226,7 @@ // /// 读取源变量,在 为true时,添加源读取任务,任务启动时会执行 // /// 一般需要更新设备变量值,调用 // /// -// protected override ValueTask> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken) +// protected override ValueTask>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken) // { // return base.ReadSourceAsync(variableSourceRead, cancellationToken); // } diff --git a/src/ThingsGateway.Server/Service/ConnectionLimiterCircuitHandler.cs b/src/ThingsGateway.Server/Service/ConnectionLimiterCircuitHandler.cs new file mode 100644 index 000000000..84b98b938 --- /dev/null +++ b/src/ThingsGateway.Server/Service/ConnectionLimiterCircuitHandler.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://thingsgateway.cn/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +using Microsoft.AspNetCore.Components.Server.Circuits; + +using ThingsGateway.Common; + +namespace ThingsGateway.Server; +public class ConnectionLimiterCircuitHandler : CircuitHandler +{ + private int _currentConnectionCount = 0; + private WebsiteOptions WebsiteOptions; + + + public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken) + { + WebsiteOptions ??= App.GetOptions(); + + if (!WebsiteOptions.BlazorConnectionLimitEnable) + return base.OnCircuitOpenedAsync(circuit, cancellationToken); + + + if (Interlocked.Increment(ref _currentConnectionCount) > WebsiteOptions.MaxBlazorConnections) + { + // 已达上限,断开连接 + Interlocked.Decrement(ref _currentConnectionCount); // 回滚计数 + throw new InvalidOperationException("The server connection limit has been reached, please try again later."); + } + + return base.OnCircuitOpenedAsync(circuit, cancellationToken); + } + + public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken) + { + WebsiteOptions ??= App.GetOptions(); + + if (!WebsiteOptions.BlazorConnectionLimitEnable) + return base.OnCircuitClosedAsync(circuit, cancellationToken); + + Interlocked.Decrement(ref _currentConnectionCount); + + return base.OnCircuitClosedAsync(circuit, cancellationToken); + } +} + diff --git a/src/ThingsGateway.Server/targets/ProPluginDebug.targets b/src/ThingsGateway.Server/targets/ProPluginDebug.targets index 64f7b5b20..882c6a457 100644 --- a/src/ThingsGateway.Server/targets/ProPluginDebug.targets +++ b/src/ThingsGateway.Server/targets/ProPluginDebug.targets @@ -30,7 +30,8 @@ - + + diff --git a/src/ThingsGateway.sln b/src/ThingsGateway.sln index c331848e0..8c912ae08 100644 --- a/src/ThingsGateway.sln +++ b/src/ThingsGateway.sln @@ -9,8 +9,13 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{0B748352-5D27-4F14-8A20-364034C72867}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + ..\.gitignore = ..\.gitignore Directory.Build.props = Directory.Build.props - PluginVersion.props = PluginVersion.props + Foundation.props = Foundation.props + NuGet.Config = NuGet.Config + PackNuget.props = PackNuget.props + ..\README.md = ..\README.md + ..\README.zh-CN.md = ..\README.zh-CN.md Version.props = Version.props EndProjectSection EndProject @@ -84,7 +89,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Plugin.Mqtt", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Plugin.RabbitMQ", "Plugin\ThingsGateway.Plugin.RabbitMQ\ThingsGateway.Plugin.RabbitMQ.csproj", "{2F28B6CF-4253-4075-ACA3-3ADED72D01FA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Blazor.Diagrams", "Gateway\ThingsGateway.Blazor.Diagrams\ThingsGateway.Blazor.Diagrams.csproj", "{014345C1-116F-4C45-A61B-5BFE257E1D56}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Blazor.Diagrams", "Gateway\ThingsGateway.Blazor.Diagrams\ThingsGateway.Blazor.Diagrams.csproj", "{14D1D908-2480-48B3-8E62-7685798B03BE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Plugin.Http", "Plugin\ThingsGateway.Plugin.Http\ThingsGateway.Plugin.Http.csproj", "{7E4696E3-1170-6AF6-844C-265BBCEC5DA7}" EndProject @@ -96,7 +101,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.UpgradeServer EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.SqlSugar", "Admin\ThingsGateway.SqlSugar\ThingsGateway.SqlSugar.csproj", "{544EDA9F-978F-84F7-48BF-FA5888F52FFB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.DB", "Admin\ThingsGateway.DB\ThingsGateway.DB.csproj", "{317B8159-28B1-49B1-1884-97D1BBDED982}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.DB", "Admin\ThingsGateway.DB\ThingsGateway.DB.csproj", "{3D262416-506C-A034-DF6C-AF8F7AB52394}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Blazor.Diagrams.Core", "Gateway\ThingsGateway.Blazor.Diagrams.Core\ThingsGateway.Blazor.Diagrams.Core.csproj", "{AFC0BEE4-E682-BCED-F631-99707421015A}" EndProject @@ -106,6 +111,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Common", "Adm EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.RemoteWebApp", "ThingsGateway.RemoteWebApp\ThingsGateway.RemoteWebApp.csproj", "{EAEE6A03-D2E7-7283-0F7A-F15B6261EE96}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1D9CD7A3-9700-A851-0ABD-183347D9CC33}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{E6EF2033-F02A-CDAD-5A72-EE397A89742E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "debug", "debug", "{053AB5FA-9742-96EC-76A1-2AEC739860C6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -244,10 +255,10 @@ Global {2F28B6CF-4253-4075-ACA3-3ADED72D01FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {2F28B6CF-4253-4075-ACA3-3ADED72D01FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F28B6CF-4253-4075-ACA3-3ADED72D01FA}.Release|Any CPU.Build.0 = Release|Any CPU - {014345C1-116F-4C45-A61B-5BFE257E1D56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {014345C1-116F-4C45-A61B-5BFE257E1D56}.Debug|Any CPU.Build.0 = Debug|Any CPU - {014345C1-116F-4C45-A61B-5BFE257E1D56}.Release|Any CPU.ActiveCfg = Release|Any CPU - {014345C1-116F-4C45-A61B-5BFE257E1D56}.Release|Any CPU.Build.0 = Release|Any CPU + {14D1D908-2480-48B3-8E62-7685798B03BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14D1D908-2480-48B3-8E62-7685798B03BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14D1D908-2480-48B3-8E62-7685798B03BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14D1D908-2480-48B3-8E62-7685798B03BE}.Release|Any CPU.Build.0 = Release|Any CPU {7E4696E3-1170-6AF6-844C-265BBCEC5DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7E4696E3-1170-6AF6-844C-265BBCEC5DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU {7E4696E3-1170-6AF6-844C-265BBCEC5DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -264,10 +275,10 @@ Global {544EDA9F-978F-84F7-48BF-FA5888F52FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU {544EDA9F-978F-84F7-48BF-FA5888F52FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {544EDA9F-978F-84F7-48BF-FA5888F52FFB}.Release|Any CPU.Build.0 = Release|Any CPU - {317B8159-28B1-49B1-1884-97D1BBDED982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {317B8159-28B1-49B1-1884-97D1BBDED982}.Debug|Any CPU.Build.0 = Debug|Any CPU - {317B8159-28B1-49B1-1884-97D1BBDED982}.Release|Any CPU.ActiveCfg = Release|Any CPU - {317B8159-28B1-49B1-1884-97D1BBDED982}.Release|Any CPU.Build.0 = Release|Any CPU + {3D262416-506C-A034-DF6C-AF8F7AB52394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D262416-506C-A034-DF6C-AF8F7AB52394}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D262416-506C-A034-DF6C-AF8F7AB52394}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D262416-506C-A034-DF6C-AF8F7AB52394}.Release|Any CPU.Build.0 = Release|Any CPU {AFC0BEE4-E682-BCED-F631-99707421015A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AFC0BEE4-E682-BCED-F631-99707421015A}.Debug|Any CPU.Build.0 = Debug|Any CPU {AFC0BEE4-E682-BCED-F631-99707421015A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -296,23 +307,23 @@ Global {A1163B55-31D8-4F58-872F-1DD5E0F14490} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F} {6B92AF2D-0997-4887-B1C5-31861F298238} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F} {569F229F-FB10-4789-B00B-D9D12383177A} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7} - {DF82CB79-08A3-4544-85FD-489177E0E5F9} = {36510D70-161F-4241-B8D0-781E21032816} + {DF82CB79-08A3-4544-85FD-489177E0E5F9} = {E6EF2033-F02A-CDAD-5A72-EE397A89742E} {F499F1D7-EDBE-44BF-99B0-543F2650E6FA} = {72C65578-92A5-4E99-9779-27835B12B32F} {2477608C-A4E9-47D6-A38F-6EE107287123} = {72C65578-92A5-4E99-9779-27835B12B32F} {D56A6669-28C5-4A99-89CF-356C7964B8E1} = {72C65578-92A5-4E99-9779-27835B12B32F} {8198BEEA-AC24-4D70-B3D7-2601A803610B} = {72C65578-92A5-4E99-9779-27835B12B32F} {93C0CC9A-500E-419A-B896-F36225377D43} = {72C65578-92A5-4E99-9779-27835B12B32F} {DC4F412A-7043-44D6-A002-E58DA7C05383} = {36510D70-161F-4241-B8D0-781E21032816} - {5DE0A4B0-B506-4738-9337-CD9EDD656EB4} = {36510D70-161F-4241-B8D0-781E21032816} - {F99EC816-DD93-4519-92D6-6C02C2D43A15} = {36510D70-161F-4241-B8D0-781E21032816} + {5DE0A4B0-B506-4738-9337-CD9EDD656EB4} = {053AB5FA-9742-96EC-76A1-2AEC739860C6} + {F99EC816-DD93-4519-92D6-6C02C2D43A15} = {053AB5FA-9742-96EC-76A1-2AEC739860C6} {0CD3C39C-93E1-4691-B4AC-B17587094EAA} = {72C65578-92A5-4E99-9779-27835B12B32F} - {6294AC29-DC48-46C3-9251-39D35799B1CF} = {36510D70-161F-4241-B8D0-781E21032816} + {6294AC29-DC48-46C3-9251-39D35799B1CF} = {1D9CD7A3-9700-A851-0ABD-183347D9CC33} {6035A570-D63D-4DFA-80D1-819E60B93353} = {72C65578-92A5-4E99-9779-27835B12B32F} - {4BC95497-E641-44A2-8B56-2D5B321750F0} = {36510D70-161F-4241-B8D0-781E21032816} + {4BC95497-E641-44A2-8B56-2D5B321750F0} = {E6EF2033-F02A-CDAD-5A72-EE397A89742E} {3EB2FFF2-5343-4902-8893-AD2D97E27D71} = {36510D70-161F-4241-B8D0-781E21032816} - {903B57EF-6799-4F2D-84D7-62022A6F9A77} = {36510D70-161F-4241-B8D0-781E21032816} - {542F5F10-712C-417C-9E41-FC81BC8DD6D4} = {36510D70-161F-4241-B8D0-781E21032816} - {1DEED7D5-DEFB-4338-911A-08C9B72EBA2F} = {36510D70-161F-4241-B8D0-781E21032816} + {903B57EF-6799-4F2D-84D7-62022A6F9A77} = {E6EF2033-F02A-CDAD-5A72-EE397A89742E} + {542F5F10-712C-417C-9E41-FC81BC8DD6D4} = {E6EF2033-F02A-CDAD-5A72-EE397A89742E} + {1DEED7D5-DEFB-4338-911A-08C9B72EBA2F} = {E6EF2033-F02A-CDAD-5A72-EE397A89742E} {13FCD67E-FEFA-4009-AD87-2C304FFACF4D} = {36510D70-161F-4241-B8D0-781E21032816} {89595D17-7A7D-45A0-AA55-D65EE9874F45} = {36510D70-161F-4241-B8D0-781E21032816} {3AA05841-87BE-45DE-B178-78AA0EF3EDC3} = {36510D70-161F-4241-B8D0-781E21032816} @@ -320,19 +331,22 @@ Global {3301EF1C-771F-491B-B64F-53BFAF42A0F8} = {36510D70-161F-4241-B8D0-781E21032816} {AF74C6B4-BBCC-4CB7-85ED-74EB7ADC8968} = {36510D70-161F-4241-B8D0-781E21032816} {2F28B6CF-4253-4075-ACA3-3ADED72D01FA} = {36510D70-161F-4241-B8D0-781E21032816} - {014345C1-116F-4C45-A61B-5BFE257E1D56} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F} + {14D1D908-2480-48B3-8E62-7685798B03BE} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F} {7E4696E3-1170-6AF6-844C-265BBCEC5DA7} = {36510D70-161F-4241-B8D0-781E21032816} {7D5E01DE-D6D7-E45D-58FD-E01B38A312B2} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {29DCAC9C-2D0F-E251-E907-F07D804CA117} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {544EDA9F-978F-84F7-48BF-FA5888F52FFB} = {72C65578-92A5-4E99-9779-27835B12B32F} - {317B8159-28B1-49B1-1884-97D1BBDED982} = {72C65578-92A5-4E99-9779-27835B12B32F} + {3D262416-506C-A034-DF6C-AF8F7AB52394} = {72C65578-92A5-4E99-9779-27835B12B32F} {AFC0BEE4-E682-BCED-F631-99707421015A} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F} {4C412EC1-8501-8211-5A57-3CEEE7EEA6B3} = {72C65578-92A5-4E99-9779-27835B12B32F} {28579DA6-6976-A674-9011-EA681BE9E1AF} = {72C65578-92A5-4E99-9779-27835B12B32F} + {1D9CD7A3-9700-A851-0ABD-183347D9CC33} = {36510D70-161F-4241-B8D0-781E21032816} + {E6EF2033-F02A-CDAD-5A72-EE397A89742E} = {36510D70-161F-4241-B8D0-781E21032816} + {053AB5FA-9742-96EC-76A1-2AEC739860C6} = {36510D70-161F-4241-B8D0-781E21032816} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282} RESX_Rules = {"EnabledRules":[]} RESX_NeutralResourcesLanguage = zh-Hans - SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282} EndGlobalSection EndGlobal diff --git a/src/ThingsGateway.slnx b/src/ThingsGateway.slnx new file mode 100644 index 000000000..1b519cf0d --- /dev/null +++ b/src/ThingsGateway.slnx @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Upgrade/ThingsGateway.Upgrade/ThingsGateway.Upgrade.csproj b/src/Upgrade/ThingsGateway.Upgrade/ThingsGateway.Upgrade.csproj index fe0ad667f..151fef7b3 100644 --- a/src/Upgrade/ThingsGateway.Upgrade/ThingsGateway.Upgrade.csproj +++ b/src/Upgrade/ThingsGateway.Upgrade/ThingsGateway.Upgrade.csproj @@ -16,7 +16,7 @@ - +