release:6.0.3.50 (#10)

release:6.0.3.50
This commit is contained in:
Diego2098
2024-06-26 20:16:31 +08:00
committed by GitHub
parent 4905eea503
commit 446610b9df
112 changed files with 2631 additions and 2861 deletions

View File

@@ -46,18 +46,23 @@ public class ModbusBenchmark : IDisposable
};
thingsgatewaymodbus.Channel.Connect();
}
//{
// var clientConfig = new TouchSocket.Core.TouchSocketConfig().SetRemoteIPHost("127.0.0.1:502");
// modbusTcpMaster = new();
// modbusTcpMaster.Setup(clientConfig);
// modbusTcpMaster.Connect();
//}
//TcpClient client = new TcpClient("127.0.0.1", 502);
//var factory = new NModbus.ModbusFactory();
//nmodbus = factory.CreateMaster(client);
{
var clientConfig = new TouchSocket.Core.TouchSocketConfig().SetRemoteIPHost("127.0.0.1:502");
modbusTcpMaster = new();
modbusTcpMaster.Setup(clientConfig);
modbusTcpMaster.Connect();
}
TcpClient client = new TcpClient("127.0.0.1", 502);
var factory = new NModbus.ModbusFactory();
nmodbus = factory.CreateMaster(client);
modbusTcpNet = new("127.0.0.1", 503);
modbusTcpNet = new("127.0.0.1", 502);
modbusTcpNet.ConnectServer();
thingsgatewaymodbus.ReadAsync("40001", 100).GetFalseAwaitResult();
nmodbus.ReadHoldingRegistersAsync(1, 0, 100).GetFalseAwaitResult();
modbusTcpMaster.ReadHoldingRegistersAsync(1, 0, 100, 3000, CancellationToken.None).GetFalseAwaitResult();
modbusTcpNet.ReadAsync("0", 100).GetFalseAwaitResult();
}
//[Benchmark]
@@ -127,6 +132,29 @@ public class ModbusBenchmark : IDisposable
}
}
[Benchmark]
public async Task NModbus4()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < Program.NumberOfItems; i++)
{
var result = await nmodbus.ReadHoldingRegistersAsync(1, 0, 100);
}
await Task.WhenAll(tasks);
}
[Benchmark]
public async Task TouchSocket()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < Program.NumberOfItems; i++)
{
var result = await modbusTcpMaster.ReadHoldingRegistersAsync(1, 0, 100, 3000, CancellationToken.None);
}
await Task.WhenAll(tasks);
}
[Benchmark]
public async Task HslCommunication()
{

View File

@@ -1,70 +1,70 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
////------------------------------------------------------------------------------
//// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
//// 此代码版权除特别声明外的代码归作者本人Diego所有
//// 源代码使用协议遵循本仓库的开源协议及附加协议
//// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
//// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
//// 使用文档https://kimdiego2098.github.io/
//// QQ群605534569
////------------------------------------------------------------------------------
using BenchmarkConsoleApp;
//using BenchmarkConsoleApp;
using BenchmarkDotNet.Attributes;
//using BenchmarkDotNet.Attributes;
using ThingsGateway.Foundation.Modbus;
using ThingsGateway.Foundation.SiemensS7;
//using ThingsGateway.Foundation.Modbus;
//using ThingsGateway.Foundation.SiemensS7;
using TouchSocket.Core;
using TouchSocket.Modbus;
using TouchSocket.Sockets;
//using TouchSocket.Core;
//using TouchSocket.Modbus;
//using TouchSocket.Sockets;
namespace ThingsGateway.Foundation;
//namespace ThingsGateway.Foundation;
[MemoryDiagnoser]
[ThreadingDiagnoser]
public class S7Benchmark : IDisposable
{
private SiemensS7Master siemensS7;
//[MemoryDiagnoser]
//[ThreadingDiagnoser]
//public class S7Benchmark : IDisposable
//{
// private SiemensS7Master siemensS7;
public S7Benchmark()
{
{
var clientConfig = new TouchSocket.Core.TouchSocketConfig();
var clientChannel = clientConfig.GetTcpClientWithIPHost("127.0.0.1:102");
siemensS7 = new(clientChannel)
{
//modbus协议格式
SiemensS7Type = SiemensTypeEnum.S1500
};
siemensS7.Channel.Connect();
}
}
// public S7Benchmark()
// {
// {
// var clientConfig = new TouchSocket.Core.TouchSocketConfig();
// var clientChannel = clientConfig.GetTcpClientWithIPHost("127.0.0.1:102");
// siemensS7 = new(clientChannel)
// {
// //modbus协议格式
// SiemensS7Type = SiemensTypeEnum.S1500
// };
// siemensS7.Channel.Connect();
// }
// }
[Benchmark]
public async Task ThingsGateway()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < Program.TaskNumberOfItems; i++)
{
tasks.Add(Task.Run(async () =>
{
for (int i = 0; i < Program.NumberOfItems; i++)
{
var result = await siemensS7.ReadAsync("M1", 1000);
if (!result.IsSuccess)
{
throw new Exception(result.ToString());
}
}
}));
}
await Task.WhenAll(tasks);
}
// [Benchmark]
// public async Task ThingsGateway()
// {
// List<Task> tasks = new List<Task>();
// for (int i = 0; i < Program.TaskNumberOfItems; i++)
// {
// tasks.Add(Task.Run(async () =>
// {
// for (int i = 0; i < Program.NumberOfItems; i++)
// {
// var result = await siemensS7.ReadAsync("M1", 1000);
// if (!result.IsSuccess)
// {
// throw new Exception(result.ToString());
// }
// }
// }));
// }
// await Task.WhenAll(tasks);
// }
public void Dispose()
{
siemensS7.Channel.SafeDispose();
siemensS7.SafeDispose();
}
}
// public void Dispose()
// {
// siemensS7.Channel.SafeDispose();
// siemensS7.SafeDispose();
// }
//}

View File

@@ -11,13 +11,10 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
<PackageReference Include="HslCommunication" Version="12.0.2" />
<PackageReference Include="MemoryPack" Version="1.10.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="NModbus" Version="3.0.81" />
<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="6.0.3.47" />
<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="6.0.3.47" />
<PackageReference Include="TouchSocket.Modbus" Version="2.1.0-alpha.21" />
<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="6.0.3.50" />
<!--<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="6.0.3.47" />-->
<PackageReference Include="TouchSocket.Modbus" Version="2.1.0-alpha.22" />
</ItemGroup>
</Project>

View File

@@ -49,20 +49,20 @@ namespace BenchmarkConsoleApp
{
BenchmarkRunner.Run(typeof(TaskCompletionSourceBenchmark));
});
consoleAction.Add("3", "S7测试", async () =>
{
S7Benchmark s7Benchmark = new();
//consoleAction.Add("3", "S7测试", async () =>
//{
// S7Benchmark s7Benchmark = new();
Stopwatch stopwatch = new();
stopwatch.Start();
await s7Benchmark.ThingsGateway();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
});
// Stopwatch stopwatch = new();
// stopwatch.Start();
// await s7Benchmark.ThingsGateway();
// stopwatch.Stop();
// Console.WriteLine(stopwatch.ElapsedMilliseconds);
//});
consoleAction.Add("1001", "单独测试 ThingsGateway", async () => { await ThingsGateway(); });
//consoleAction.Add("1002", "单独测试 NModbus4", async () => { await NModbus4(); });
//consoleAction.Add("1003", "单独测试 TouchSocket", async () => { await TouchSocket(); });
consoleAction.Add("1002", "单独测试 NModbus4", async () => { await NModbus4(); });
consoleAction.Add("1003", "单独测试 TouchSocket", async () => { await TouchSocket(); });
consoleAction.Add("1004", "单独测试 TaskCompletionSource", async () =>
{
TaskCompletionSourceBenchmark taskCompletionSourceBenchmark = new();
@@ -114,58 +114,58 @@ namespace BenchmarkConsoleApp
}
}
//private static async Task TouchSocket()
//{
// try
// {
// Console.WriteLine(" TouchSocket 测试已开始");
// Stopwatch stopwatch = new();
// stopwatch.Start();
// List<Task> tasks = new List<Task>();
// for (int i = 0; i < ClientCount; i++)
// {
// tasks.Add(Task.Run(async () =>
// {
// ModbusBenchmark modbusBenchmarker = new();
// await modbusBenchmarker.TouchSocket();
// modbusBenchmarker.Dispose();
// }));
// }
// await Task.WhenAll(tasks);
// stopwatch.Stop();
// Console.WriteLine($" TouchSocket 耗时:{stopwatch.ElapsedMilliseconds}");
// }
// catch (Exception ex)
// {
// Console.WriteLine($" TouchSocket 发生错误:{ex.Message}");
// }
//}
private static async Task TouchSocket()
{
try
{
Console.WriteLine(" TouchSocket 测试已开始");
Stopwatch stopwatch = new();
stopwatch.Start();
List<Task> tasks = new List<Task>();
for (int i = 0; i < ClientCount; i++)
{
tasks.Add(Task.Run(async () =>
{
ModbusBenchmark modbusBenchmarker = new();
await modbusBenchmarker.TouchSocket();
modbusBenchmarker.Dispose();
}));
}
await Task.WhenAll(tasks);
stopwatch.Stop();
Console.WriteLine($" TouchSocket 耗时:{stopwatch.ElapsedMilliseconds}");
}
catch (Exception ex)
{
Console.WriteLine($" TouchSocket 发生错误:{ex.Message}");
}
}
//private static async Task NModbus4()
//{
// try
// {
// Console.WriteLine(" NModbus4 测试已开始");
// Stopwatch stopwatch = new();
// stopwatch.Start();
// List<Task> tasks = new List<Task>();
// for (int i = 0; i < ClientCount; i++)
// {
// tasks.Add(Task.Run(async () =>
// {
// ModbusBenchmark modbusBenchmarker = new();
// await modbusBenchmarker.NModbus4();
// modbusBenchmarker.Dispose();
// }));
// }
// await Task.WhenAll(tasks);
// stopwatch.Stop();
// Console.WriteLine($" NModbus4 耗时:{stopwatch.ElapsedMilliseconds}");
// }
// catch (Exception ex)
// {
// Console.WriteLine($" NModbus4 发生错误:{ex.Message}");
// }
//}
private static async Task NModbus4()
{
try
{
Console.WriteLine(" NModbus4 测试已开始");
Stopwatch stopwatch = new();
stopwatch.Start();
List<Task> tasks = new List<Task>();
for (int i = 0; i < ClientCount; i++)
{
tasks.Add(Task.Run(async () =>
{
ModbusBenchmark modbusBenchmarker = new();
await modbusBenchmarker.NModbus4();
modbusBenchmarker.Dispose();
}));
}
await Task.WhenAll(tasks);
stopwatch.Stop();
Console.WriteLine($" NModbus4 耗时:{stopwatch.ElapsedMilliseconds}");
}
catch (Exception ex)
{
Console.WriteLine($" NModbus4 发生错误:{ex.Message}");
}
}
}
}

View File

@@ -72,13 +72,13 @@ public static class UnifyContext
/// <summary>
/// 跳过规范化处理的 Response Content-Type
/// </summary>
internal static string[] ResponseContentTypesOfNonUnify = new[]
{
internal static string[] ResponseContentTypesOfNonUnify =
[
"text/event-stream",
"application/pdf",
"application/octet-stream",
"image/"
};
];
/// <summary>
/// 检查 HttpContext 是否进行规范化处理

View File

@@ -435,8 +435,8 @@ public static class ObjectExtensions
{
if (str == null) return Array.Empty<string>();
if (string.IsNullOrWhiteSpace(str)) return new string[] { str };
if (str.Length == 1) return new string[] { str };
if (string.IsNullOrWhiteSpace(str)) return [str];
if (str.Length == 1) return [str];
return Regex.Split(str, @"(?=\p{Lu}\p{Ll})|(?<=\p{Ll})(?=\p{Lu})")
.Where(u => u.Length > 0)

View File

@@ -33,8 +33,7 @@ public static class SqlSugarExtensions
public static ISugarQueryable<TEntity> ExportIgnoreColumns<TEntity>(this ISugarQueryable<TEntity> queryable)
{
return queryable.IgnoreColumns(
new string[]
{
[
nameof(BaseEntity.Id),
nameof(BaseEntity.CreateTime),
nameof(BaseEntity.CreateUser),
@@ -44,7 +43,7 @@ public static class SqlSugarExtensions
nameof(BaseEntity.UpdateTime),
nameof(BaseEntity.UpdateUser),
nameof(BaseEntity.UpdateUserId),
}
]
);
}

View File

@@ -31,19 +31,19 @@ public static class StartupExtensions
/// <summary>
/// 排除的配置文件前缀
/// </summary>
private static readonly string[] excludeJsonPrefixs = new[] { "appsettings", "bundleconfig", "compilerconfig" };
private static readonly string[] excludeJsonPrefixs = ["appsettings", "bundleconfig", "compilerconfig"];
/// <summary>
/// 排除运行时 Json 后缀
/// </summary>
private static readonly string[] runtimeJsonSuffixs = new string[]
{
private static readonly string[] runtimeJsonSuffixs =
[
"deps.json",
"runtimeconfig.dev.json",
"runtimeconfig.prod.json",
"runtimeconfig.json",
"staticwebassets.runtime.json"
};
];
/// <summary>
/// 对配置文件名进行分组

View File

@@ -42,7 +42,7 @@ public class FileService : IFileService
if (file == null) throw Oops.Bah("FileNullError");
if (file.Size > maxSize * 1024 * 1024) throw Oops.Bah("FileLengthError", maxSize);
var fileSuffix = Path.GetExtension(file.Name).ToLower().Split(".")[1]; // 文件后缀
string[] allowTypeS = allowTypes == null ? new string[] { "xlsx" } : allowTypes;//允许上传的文件类型
string[] allowTypeS = allowTypes == null ? ["xlsx"] : allowTypes;//允许上传的文件类型
if (!allowTypeS.Contains(fileSuffix)) throw Oops.Bah("FileTypeError", fileSuffix);
}

View File

@@ -9,7 +9,7 @@
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.159" />
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="BootstrapBlazor" Version="8.6.3" />
<PackageReference Include="BootstrapBlazor" Version="8.6.4" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup>

View File

@@ -37,7 +37,7 @@ public class JWTEncryption
/// <summary>
/// 刷新 Token 身份标识
/// </summary>
private static readonly string[] _refreshTokenClaims = new[] { "f", "e", "s", "l", "k" };
private static readonly string[] _refreshTokenClaims = ["f", "e", "s", "l", "k"];
/// <summary>
/// 生成 Token
@@ -244,7 +244,7 @@ public class JWTEncryption
// 处理 axios 问题
httpContext.Response.Headers.TryGetValue(accessControlExposeKey, out var acehs);
httpContext.Response.Headers[accessControlExposeKey] = string.Join(',', StringValues.Concat(acehs, new StringValues(new[] { accessTokenKey, xAccessTokenKey })).Distinct());
httpContext.Response.Headers[accessControlExposeKey] = string.Join(',', StringValues.Concat(acehs, new StringValues([accessTokenKey, xAccessTokenKey])).Distinct());
return true;
}
@@ -482,7 +482,7 @@ public class JWTEncryption
/// <summary>
/// 日期类型的 Claim 类型
/// </summary>
private static readonly string[] DateTypeClaimTypes = new[] { JwtRegisteredClaimNames.Iat, JwtRegisteredClaimNames.Nbf, JwtRegisteredClaimNames.Exp };
private static readonly string[] DateTypeClaimTypes = [JwtRegisteredClaimNames.Iat, JwtRegisteredClaimNames.Nbf, JwtRegisteredClaimNames.Exp];
/// <summary>
/// 框架 App 静态类

View File

@@ -91,8 +91,8 @@ public static class StringExtensions
{
if (str == null) return Array.Empty<string>();
if (string.IsNullOrWhiteSpace(str)) return new string[] { str };
if (str.Length == 1) return new string[] { str };
if (string.IsNullOrWhiteSpace(str)) return [str];
if (str.Length == 1) return [str];
return Regex.Split(str, @"(?=\p{Lu}\p{Ll})|(?<=\p{Ll})(?=\p{Lu})")
.Where(u => u.Length > 0)

View File

@@ -23,6 +23,7 @@ public interface IClientChannel : IChannel, ISender, IClient, IClientSender, IOn
AsyncAutoResetEvent WaitLock { get; }
WaitHandlePool<MessageBase> WaitHandlePool { get; }
DataHandlingAdapter ReadOnlyDataHandlingAdapter { get; }
#endregion
}

View File

@@ -25,6 +25,8 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
/// <inheritdoc/>
public AsyncAutoResetEvent WaitLock { get; } = new AsyncAutoResetEvent(true);
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
/// <summary>
/// 等待池
/// </summary>

View File

@@ -33,6 +33,8 @@ public class TcpClientChannel : TcpClient, IClientChannel
/// </summary>
public WaitHandlePool<MessageBase> WaitHandlePool { get; } = new();
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
/// <summary>
/// 接收到数据
/// </summary>

View File

@@ -26,6 +26,8 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
/// </summary>
public WaitHandlePool<MessageBase> WaitHandlePool { get; private set; } = new();
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
/// <inheritdoc/>
public ConcurrentList<IProtocol> Collects { get; } = new();

View File

@@ -21,6 +21,8 @@ public class UdpSessionChannel : UdpSession, IClientChannel
/// <inheritdoc/>
public AsyncAutoResetEvent WaitLock { get; } = new AsyncAutoResetEvent(true);
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
/// <summary>
/// 等待池
/// </summary>

View File

@@ -29,7 +29,7 @@ public class ChannelData
/// <summary>
/// 远程地址,可由<see cref="IPHost.IPHost(string)"/>与<see href="IPHost.ToString()"/>相互转化
/// </summary>
public string? RemoteUrl { get; set; }
public string? RemoteUrl { get; set; } = "127.0.0.1:502";
/// <summary>
/// 本地地址,可由<see cref="IPHost.IPHost(string)"/>与<see href="IPHost.ToString()"/>相互转化
@@ -39,17 +39,17 @@ public class ChannelData
/// <summary>
/// COM
/// </summary>
public string? PortName { get; set; }
public string? PortName { get; set; } = "COM1";
/// <summary>
/// 波特率
/// </summary>
public int? BaudRate { get; set; }
public int? BaudRate { get; set; } = 9600;
/// <summary>
/// 数据位
/// </summary>
public int? DataBits { get; set; }
public int? DataBits { get; set; } = 8;
/// <summary>
/// 校验位
@@ -64,12 +64,12 @@ public class ChannelData
/// <summary>
/// DtrEnable
/// </summary>
public bool? DtrEnable { get; set; }
public bool? DtrEnable { get; set; } = true;
/// <summary>
/// RtsEnable
/// </summary>
public bool? RtsEnable { get; set; }
public bool? RtsEnable { get; set; } = true;
/// <summary>
/// TouchSocketConfig

View File

@@ -15,42 +15,46 @@ namespace ThingsGateway.Foundation;
/// </summary>
public interface IResultMessage : IOperResult, IRequestInfo
{
/// <summary>
/// 消息头的指令长度,不固定时返回0
/// </summary>
int HeadBytesLength { get; }
/// <summary>
/// 实体数据长度,不固定是返回0
/// </summary>
int BodyLength { get; set; }
/// <summary>
/// 解析的字节信息
/// </summary>
byte[] Content { get; set; }
/// <summary>
/// 接收的字节信息
/// </summary>
IByteBlock ReceivedBytes { get; set; }
/// <summary>
/// 等待标识,对于并发协议,必须从协议中例如固定头部获取标识字段
/// </summary>
int Sign { get; set; }
/// <summary>
/// 检查头子节的合法性,并赋值<see cref="BodyLength"/><br />
/// 数据体长度
/// </summary>
int BodyLength { get; set; }
/// <summary>
/// 消息头的指令长度,不固定时返回0
/// </summary>
int HeaderLength { get; }
/// <summary>
/// 当收到数据,由框架封送有效载荷数据。
/// 此时流位置为<see cref="HeaderLength"/>
/// <para>但是如果是因为数据错误,则需要修改<see cref="ByteBlock.Position"/>到正确位置,如果都不正确,则设置<see cref="ByteBlock.Position"/>等于<see cref="ByteBlock.Length"/></para>
/// <para>然后返回<see cref="FilterResult.GoOn"/></para>
/// </summary>
/// <returns>是否成功有效</returns>
FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock;
/// <summary>
/// 检查头子节的合法性,并赋值<see cref="BodyLength"/><br />
/// <para>如果返回false意味着放弃本次解析的所有数据包括已经解析完成的Header</para>
/// </summary>
/// <param name="headBytes">接收的头子节</param>
/// <returns>是否成功的结果</returns>
bool CheckHeadBytes(byte[]? headBytes);
bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock;
/// <summary>
/// 发送前的信息处理,例如存储某些特征信息:站号/功能码等等用于验证后续的返回信息是否合法
/// </summary>
/// <param name="sendBytes"></param>
/// <param name="sendMessage"></param>
/// <returns></returns>
void SendInfo(ReadOnlyMemory<byte> sendBytes);
void SendInfo(ISendMessage sendMessage);
}

View File

@@ -13,7 +13,6 @@ namespace ThingsGateway.Foundation;
/// <summary>
/// 发送消息
/// </summary>
public interface ISendMessage : IRequestInfo, IWaitHandle
public interface ISendMessage : IRequestInfo, IWaitHandle, IRequestInfoBuilder
{
ReadOnlyMemory<byte> SendBytes { get; }
}

View File

@@ -13,6 +13,8 @@ namespace ThingsGateway.Foundation;
/// <inheritdoc cref="IResultMessage"/>
public class MessageBase : OperResultClass<byte[]>, IResultMessage, IWaitHandle
{
#region
public MessageBase()
{
}
@@ -21,38 +23,34 @@ public class MessageBase : OperResultClass<byte[]>, IResultMessage, IWaitHandle
{
}
public MessageBase(string msg) : base(msg)
{
}
public MessageBase(Exception ex) : base(ex)
{
}
public MessageBase(string msg, Exception ex) : base(msg, ex)
{
}
#endregion
/// <inheritdoc/>
public int BodyLength { get; set; }
/// <inheritdoc/>
public virtual int HeadBytesLength { get; }
public virtual int HeaderLength { get; set; }
/// <inheritdoc/>
public virtual int Sign { get; set; }
/// <inheritdoc/>
public IByteBlock ReceivedBytes { get; set; }
/// <inheritdoc/>
public virtual bool CheckHeadBytes(byte[]? headBytes)
public virtual bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
{
return true;
}
/// <inheritdoc/>
public virtual void SendInfo(ReadOnlyMemory<byte> sendBytes)
public virtual void SendInfo(ISendMessage sendMessage)
{
}
public virtual FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
{
return FilterResult.Success;
}
}

View File

@@ -15,10 +15,10 @@ namespace ThingsGateway.Foundation;
/// <summary>
/// TCP/Serial适配器基类
/// </summary>
public abstract class ReadWriteDevicesSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingAdapter<TRequest> where TRequest : class, IResultMessage, new()
public class ProtocolSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingAdapter<TRequest> where TRequest : class, IResultMessage, new()
{
/// <inheritdoc cref="ReadWriteDevicesSingleStreamDataHandleAdapter{TRequest}"/>
public ReadWriteDevicesSingleStreamDataHandleAdapter()
/// <inheritdoc cref="ProtocolSingleStreamDataHandleAdapter{TRequest}"/>
public ProtocolSingleStreamDataHandleAdapter()
{
CacheTimeoutEnable = true;
}
@@ -66,62 +66,52 @@ public abstract class ReadWriteDevicesSingleStreamDataHandleAdapter<TRequest> :
var pos = byteBlock.Position;
if (request.HeadBytesLength > byteBlock.CanReadLength)
if (request.HeaderLength > byteBlock.CanReadLength)
{
return FilterResult.Cache;//当头部都无法解析时,直接缓存
}
ArraySegment<byte>? header = null;
//传入新的ByteBlock对象避免影响原有的游标
//当解析消息设定固定头长度大于0时获取头部字节
if (request.HeadBytesLength > 0)
{
header = byteBlock.AsSegment(byteBlock.Position, request.HeadBytesLength);
}
else
{
}
//检查头部合法性
if (request.CheckHeadBytes(header?.Array))
if (request.CheckHead(ref byteBlock))
{
byteBlock.Position = pos;
if (request.BodyLength > this.MaxPackageSize)
{
this.OnError(default, $"Received BodyLength={request.BodyLength}, greater than the set MaxPackageSize={this.MaxPackageSize}", true, true);
return FilterResult.GoOn;
}
if (request.BodyLength + request.HeadBytesLength > byteBlock.CanReadLength)
if (request.BodyLength + request.HeaderLength > byteBlock.CanReadLength)
{
//body不满足解析开始缓存然后保存对象
tempCapacity = request.BodyLength + request.HeadBytesLength;
tempCapacity = request.BodyLength + request.HeaderLength;
return FilterResult.Cache;
}
if (request.BodyLength <= 0)
{
//如果body长度无法确定直接读取全部
request.BodyLength = byteBlock.Length;
}
var result = UnpackResponse(request, byteBlock);
if (result.FilterResult == FilterResult.Cache)
//if (request.BodyLength <= 0)
//{
// //如果body长度无法确定直接读取全部
// request.BodyLength = byteBlock.Length;
//}
var headPos = pos + request.HeaderLength;
byteBlock.Position = headPos;
var result = request.CheckBody(ref byteBlock);
if (result == FilterResult.Cache)
{
if (Logger.LogLevel <= LogLevel.Trace)
Logger.Trace($"{ToString()}-Received incomplete, cached message, current length:{byteBlock.Length} {request?.ErrorMessage}");
tempCapacity = request.BodyLength + request.HeadBytesLength;
tempCapacity = request.BodyLength + request.HeaderLength;
request.OperCode = -1;
}
else if (result.FilterResult == FilterResult.GoOn)
else if (result == FilterResult.GoOn)
{
byteBlock.Position += 1;
if (byteBlock.Position == headPos)
byteBlock.Position += 1;
request.OperCode = -1;
}
else if (result.FilterResult == FilterResult.Success)
else if (result == FilterResult.Success)
{
byteBlock.Position = request.HeadBytesLength + request.BodyLength + pos;
if (request.IsSuccess)
{
request.Content = result.Content;
request.ReceivedBytes = byteBlock;
}
byteBlock.Position = request.HeaderLength + request.BodyLength + pos;
}
return result.FilterResult;
return result;
}
else
{
@@ -160,32 +150,35 @@ public abstract class ReadWriteDevicesSingleStreamDataHandleAdapter<TRequest> :
{
if (!(requestInfo is ISendMessage sendMessage))
{
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(SendMessage)}");
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
}
var sendData = sendMessage.SendBytes;
if (Logger.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Send:{(IsHexData ? sendData.Span.ToHexString() : (sendData.Span.ToString(Encoding.UTF8)))}");
//非并发主从协议
if (IsSingleThread)
var requestInfoBuilder = (ISendMessage)requestInfo;
var byteBlock = new ValueByteBlock(requestInfoBuilder.MaxLength);
try
{
SetRequest(sendMessage.Sign, sendData);
requestInfoBuilder.Build(ref byteBlock);
if (Logger.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Send:{(IsHexData ? byteBlock.Span.ToHexString() : (byteBlock.Span.ToString(Encoding.UTF8)))}");
//非并发主从协议
if (IsSingleThread)
{
SetRequest(sendMessage.Sign, requestInfoBuilder);
}
await this.GoSendAsync(byteBlock.Memory).ConfigureFalseAwait();
}
finally
{
byteBlock.SafeDispose();
}
//发送
await this.GoSendAsync(sendData).ConfigureFalseAwait();
}
public void SetRequest(int sign, ReadOnlyMemory<byte> sendData)
public void SetRequest(int sign, ISendMessage sendMessage)
{
var request = GetInstance();
request.Sign = sign;
request.SendInfo(sendData);
request.SendInfo(sendMessage);
Request = request;
}
/// <summary>
/// 解包获取实际数据
/// </summary>
protected abstract AdapterResult UnpackResponse(TRequest request, IByteBlock byteBlock);
}

View File

@@ -16,7 +16,7 @@ namespace ThingsGateway.Foundation;
/// <summary>
/// UDP适配器基类
/// </summary>
public abstract class ReadWriteDevicesUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where TRequest : class, IResultMessage, new()
public class ProtocolUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where TRequest : class, IResultMessage, new()
{
/// <inheritdoc/>
public override bool CanSendRequestInfo => true;
@@ -77,52 +77,60 @@ public abstract class ReadWriteDevicesUdpDataHandleAdapter<TRequest> : UdpDataHa
{
request = GetInstance();
}
ArraySegment<byte>? header = null;
//传入新的ByteBlock对象避免影响原有的游标
//当解析消息设定固定头长度大于0时获取头部字节
if (request.HeadBytesLength > 0)
{
header = byteBlock.AsSegment(0, request.HeadBytesLength);
}
else
var pos = byteBlock.Position;
if (request.HeaderLength > byteBlock.CanReadLength)
{
return;//当头部都无法解析时,直接缓存
}
//检查头部合法性
if (request.CheckHeadBytes(header?.Array))
if (request.CheckHead(ref byteBlock))
{
byteBlock.Position = pos;
if (request.BodyLength > this.MaxPackageSize)
{
this.OnError(default, $"Received BodyLength={request.BodyLength}, greater than the set MaxPackageSize={this.MaxPackageSize}", true, true);
await GoReceived(remoteEndPoint, null, request);
}
if (request.BodyLength + request.HeadBytesLength > byteBlock.CanReadLength)
{
await GoReceived(remoteEndPoint, null, request);
return;
}
if (request.BodyLength <= 0)
if (request.BodyLength + request.HeaderLength > byteBlock.CanReadLength)
{
//如果body长度无法确定,直接读取全部
request.BodyLength = byteBlock.Length;
//body不满足解析,开始缓存,然后保存对象
return;
}
var result = UnpackResponse(request, byteBlock);
byteBlock.Position = byteBlock.Length;
if (request.IsSuccess)
//if (request.BodyLength <= 0)
//{
// //如果body长度无法确定直接读取全部
// request.BodyLength = byteBlock.Length;
//}
var headPos = pos + request.HeaderLength;
byteBlock.Position = headPos;
var result = request.CheckBody(ref byteBlock);
if (result == FilterResult.Cache)
{
request.Content = result.Content;
request.ReceivedBytes = byteBlock;
if (Logger.LogLevel <= LogLevel.Trace)
Logger.Trace($"{ToString()}-Received incomplete, cached message, current length:{byteBlock.Length} {request?.ErrorMessage}");
request.OperCode = -1;
}
else if (result == FilterResult.GoOn)
{
if (byteBlock.Position == headPos)
byteBlock.Position += 1;
request.OperCode = -1;
}
else if (result == FilterResult.Success)
{
byteBlock.Position = request.HeaderLength + request.BodyLength + pos;
await GoReceived(remoteEndPoint, null, request);
}
await GoReceived(remoteEndPoint, null, request);
return;
}
else
{
byteBlock.Position = byteBlock.Length;//移动游标
request.OperCode = -1;
await GoReceived(remoteEndPoint, null, request);
return;
}
}
@@ -132,32 +140,35 @@ public abstract class ReadWriteDevicesUdpDataHandleAdapter<TRequest> : UdpDataHa
{
if (!(requestInfo is ISendMessage sendMessage))
{
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(SendMessage)}");
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
}
var sendData = sendMessage.SendBytes;
if (Logger.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Send:{(IsHexData ? sendData.Span.ToHexString() : (sendData.Span.ToString(Encoding.UTF8)))}");
//非并发主从协议
if (IsSingleThread)
var requestInfoBuilder = (ISendMessage)requestInfo;
var byteBlock = new ValueByteBlock(requestInfoBuilder.MaxLength);
try
{
SetRequest(sendMessage.Sign, sendData);
requestInfoBuilder.Build(ref byteBlock);
if (Logger.LogLevel <= LogLevel.Trace)
Logger?.Trace($"{ToString()}- Send:{(IsHexData ? byteBlock.Span.ToHexString() : (byteBlock.Span.ToString(Encoding.UTF8)))}");
//非并发主从协议
if (IsSingleThread)
{
SetRequest(sendMessage.Sign, requestInfoBuilder);
}
await this.GoSendAsync(endPoint, byteBlock.Memory).ConfigureFalseAwait();
}
finally
{
byteBlock.SafeDispose();
}
//发送
await this.GoSendAsync(endPoint, sendData).ConfigureFalseAwait();
}
public void SetRequest(int sign, ReadOnlyMemory<byte> sendData)
public void SetRequest(int sign, ISendMessage sendMessage)
{
var request = GetInstance();
request.Sign = sign;
request.SendInfo(sendData);
request.SendInfo(sendMessage);
Request = request;
}
/// <summary>
/// 解包获取实际数据包
/// </summary>
protected abstract AdapterResult UnpackResponse(TRequest request, IByteBlock byteBlock);
}

View File

@@ -50,4 +50,7 @@ public enum DataTypeEnum
/// <inheritdoc/>
Double,
/// <inheritdoc/>
Decimal,
}

View File

@@ -62,6 +62,23 @@ public static class ByteExtensions
return result;
}
/// <summary>
/// 数组内容分别相加某个数字
/// </summary>
/// <param name="bytes"></param>
/// <param name="value"></param>
/// <returns></returns>
public static ReadOnlySpan<byte> BytesAdd(this ReadOnlySpan<byte> bytes, int value)
{
byte[] result = new byte[bytes.Length];
for (int index = 0; index < bytes.Length; index++)
{
result[index] = (byte)(bytes[index] + value);
}
return result;
}
/// <summary>
/// 获取byte数据类型的第offset位是否为True<br />
/// </summary>

View File

@@ -68,7 +68,7 @@ public static class DataTypeExtensions
/// </summary>
/// <param name="coreDataType"></param>
/// <returns></returns>
public static int GetByteLength(this DataTypeEnum coreDataType)
public static byte GetByteLength(this DataTypeEnum coreDataType)
{
return coreDataType switch
{
@@ -82,6 +82,7 @@ public static class DataTypeExtensions
DataTypeEnum.UInt64 => 8,
DataTypeEnum.Single => 4,
DataTypeEnum.Double => 8,
DataTypeEnum.Decimal => 16,
_ => 0,
};
}

View File

@@ -427,7 +427,7 @@ public interface IProtocol : IDisposable
/// <param name="token">取消令箭</param>
/// <param name="channel">通道</param>
/// <returns>返回消息体</returns>
ValueTask SendAsync(SendMessage sendMessage, IClientChannel channel = null, CancellationToken token = default);
ValueTask<OperResult> SendAsync(ISendMessage sendMessage, IClientChannel channel = default, CancellationToken token = default);
/// <summary>
/// 发送会经过适配器可传入socketId如果为空则默认通道必须为<see cref="IClientChannel"/>类型
@@ -436,7 +436,7 @@ public interface IProtocol : IDisposable
/// <param name="sendMessage">发送字节数组</param>
/// <param name="cancellationToken">取消令箭</param>
/// <returns>返回消息体</returns>
ValueTask<OperResult> SendAsync(string socketId, SendMessage sendMessage, CancellationToken cancellationToken);
ValueTask<OperResult> SendAsync(ISendMessage sendMessage, string socketId, CancellationToken cancellationToken);
/// <summary>
/// 发送并等待返回,会经过适配器,可传入<see cref="IClientChannel"/>,如果为空,则默认通道必须为<see cref="IClientChannel"/>类型
@@ -446,7 +446,7 @@ public interface IProtocol : IDisposable
/// <param name="cancellationToken">取消令箭</param>
/// <param name="channel">通道</param>
/// <returns>返回消息体</returns>
ValueTask<OperResult<byte[]>> SendThenReturnAsync(SendMessage command, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default, IClientChannel channel = default);
ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage command, IClientChannel channel = default, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default);
/// <summary>
/// 发送并等待返回会经过适配器可传入socketId如果为空则默认通道必须为<see cref="IClientChannel"/>类型
@@ -456,11 +456,7 @@ public interface IProtocol : IDisposable
/// <param name="waitData">waitData</param>
/// <param name="cancellationToken">取消令箭</param>
/// <returns>返回消息体</returns>
ValueTask<OperResult<byte[]>> SendThenReturnAsync(string socketId, SendMessage sendMessage, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default);
ValueTask<OperResult<byte[]>> SendThenReturnAsync(string socketId, ReadOnlyMemory<byte> sendBytes, CancellationToken cancellationToken = default);
ValueTask<OperResult<byte[]>> SendThenReturnAsync(ReadOnlyMemory<byte> sendBytes, CancellationToken cancellationToken = default);
ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage sendMessage, string socketId, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default);
/// <summary>
/// 布尔量解析时是否需要按字反转

View File

@@ -235,57 +235,49 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
}
/// <inheritdoc/>
public virtual async ValueTask SendAsync(SendMessage sendMessage, IClientChannel channel = default, CancellationToken token = default)
public virtual async ValueTask<OperResult> SendAsync(ISendMessage sendMessage, IClientChannel channel = default, CancellationToken token = default)
{
if (!Channel.Online)
await Channel.ConnectAsync(ConnectTimeout, token).ConfigureAwait(false);
if (SendDelayTime != 0)
await Task.Delay(SendDelayTime, token).ConfigureAwait(false);
if (token.IsCancellationRequested)
return;
if (channel == default)
try
{
if (Channel is not IClientChannel clientChannel) { throw new ArgumentNullException(nameof(channel)); }
await clientChannel.SendAsync(sendMessage).ConfigureAwait(false);
}
else
{
await channel.SendAsync(sendMessage).ConfigureAwait(false);
}
}
if (!Channel.Online)
await Channel.ConnectAsync(ConnectTimeout, token).ConfigureAwait(false);
if (SendDelayTime != 0)
await Task.Delay(SendDelayTime, token).ConfigureAwait(false);
/// <inheritdoc/>
public virtual async ValueTask<OperResult> SendAsync(string socketId, SendMessage sendMessage, CancellationToken cancellationToken)
{
if (Channel.ChannelType == ChannelTypeEnum.TcpService)
{
if (((TcpServiceChannel)Channel).Clients.TryGetClient($"ID={socketId}", out TcpSessionClientChannel? client))
if (token.IsCancellationRequested)
return new(new OperationCanceledException());
if (channel == default)
{
await SendAsync(sendMessage, client);
return OperResult.Success;
if (Channel is not IClientChannel clientChannel) { throw new ArgumentNullException(nameof(channel)); }
await clientChannel.SendAsync(sendMessage).ConfigureAwait(false);
}
else
return new OperResult(DefaultResource.Localizer["DtuNoConnectedWaining"]);
}
else
{
await SendAsync(sendMessage);
{
await channel.SendAsync(sendMessage).ConfigureAwait(false);
}
return OperResult.Success;
}
catch (Exception ex)
{
return new(ex);
}
}
/// <inheritdoc/>
public virtual ValueTask<OperResult<byte[]>> SendThenReturnAsync(ReadOnlyMemory<byte> sendBytes, CancellationToken cancellationToken = default)
public virtual async ValueTask<OperResult> SendAsync(ISendMessage sendMessage, string socketId, CancellationToken cancellationToken)
{
return SendThenReturnAsync(new SendMessage(sendBytes), default, cancellationToken);
}
try
{
var channelResult = GetChannel(socketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
/// <inheritdoc/>
public virtual ValueTask<OperResult<byte[]>> SendThenReturnAsync(string socketId, ReadOnlyMemory<byte> sendBytes, CancellationToken cancellationToken = default)
{
return SendThenReturnAsync(socketId, new SendMessage(sendBytes), default, cancellationToken);
return await SendAsync(sendMessage, channelResult.Content, cancellationToken);
}
catch (Exception ex)
{
return new(ex);
}
}
/// <inheritdoc/>
@@ -303,15 +295,15 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
}
/// <inheritdoc/>
public virtual async ValueTask<OperResult<byte[]>> SendThenReturnAsync(string socketId, SendMessage sendMessage, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default)
public virtual async ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage sendMessage, string socketId, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default)
{
var channelResult = GetChannel(socketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
return await SendThenReturnAsync(sendMessage, waitData, cancellationToken, channelResult.Content);
return await SendThenReturnAsync(sendMessage, channelResult.Content, waitData, cancellationToken);
}
/// <inheritdoc/>
public virtual async ValueTask<OperResult<byte[]>> SendThenReturnAsync(SendMessage command, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default, IClientChannel channel = default)
public virtual async ValueTask<OperResult<byte[]>> SendThenReturnAsync(ISendMessage command, IClientChannel channel = default, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default)
{
if (!Channel.Online)
await Channel.ConnectAsync(ConnectTimeout, cancellationToken).ConfigureAwait(false);
@@ -329,7 +321,7 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
waitData = clientChannel.WaitHandlePool.GetWaitDataAsync(out var sign);
command.Sign = sign;
}
result = await GetResponsedDataAsync(command, Timeout, clientChannel, waitData, cancellationToken).ConfigureAwait(false);
result = await GetResponsedDataAsync(command, clientChannel, waitData, Timeout, cancellationToken).ConfigureAwait(false);
}
else
{
@@ -338,7 +330,7 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
waitData = channel.WaitHandlePool.GetWaitDataAsync(out var sign);
command.Sign = sign;
}
result = await GetResponsedDataAsync(command, Timeout, channel, waitData, cancellationToken).ConfigureAwait(false);
result = await GetResponsedDataAsync(command, channel, waitData, Timeout, cancellationToken).ConfigureAwait(false);
}
return new OperResult<byte[]>(result) { Content = result.Content };
@@ -350,7 +342,7 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
/// <summary>
/// 发送并等待数据
/// </summary>
protected virtual async ValueTask<MessageBase> GetResponsedDataAsync(SendMessage command, int timeout, IClientChannel clientChannel, WaitDataAsync<MessageBase> waitData = default, CancellationToken cancellationToken = default)
protected virtual async ValueTask<MessageBase> GetResponsedDataAsync(ISendMessage command, IClientChannel clientChannel, WaitDataAsync<MessageBase> waitData = default, int timeout = 3000, CancellationToken cancellationToken = default)
{
if (waitData == default)
{
@@ -372,7 +364,7 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
}
else
{
return new MessageBase(result);
throw new Exception(result.ErrorMessage, result.Exception);
}
}
finally

View File

@@ -11,8 +11,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.6" />
<PackageReference Include="TouchSocket" Version="2.1.0-alpha.21" />
<PackageReference Include="TouchSocket.SerialPorts" Version="2.1.0-alpha.21" />
<PackageReference Include="TouchSocket" Version="2.1.0-alpha.22" />
<PackageReference Include="TouchSocket.SerialPorts" Version="2.1.0-alpha.22" />
<ProjectReference Include="$(SolutionDir)\ThingsGateway.NewLife.X\ThingsGateway.NewLife.X.csproj" />
</ItemGroup>

View File

@@ -18,31 +18,27 @@ public class CRC16Utils
/// <summary>
/// 通过指定多项式码来获取对应的数据的CRC校验码
/// </summary>
/// <param name="memory">需要校验的数据不包含CRC字节</param>
/// <param name="bytes">需要校验的数据不包含CRC字节</param>
/// <returns>返回带CRC校验码的字节数组可用于串口发送</returns>
public static byte[] Crc16Only(ReadOnlyMemory<byte> memory)
public static byte[] Crc16Only(ReadOnlySpan<byte> bytes)
{
return Crc16Only(memory, 0xA001);
return Crc16Only(bytes, 0xA001);
}
/// <summary>
/// 通过指定多项式码来获取对应的数据的CRC校验码
/// </summary>
/// <param name="memory">需要校验的数据不包含CRC字节</param>
/// <param name="bytes">需要校验的数据不包含CRC字节</param>
/// <param name="xdapoly">多项式</param>
/// <param name="crc16">crc16</param>
/// <returns>返回带CRC校验码的字节数组可用于串口发送</returns>
public static byte[] Crc16Only(ReadOnlyMemory<byte> memory, int xdapoly, bool crc16 = true)
public static byte[] Crc16Only(ReadOnlySpan<byte> bytes, int xdapoly, bool crc16 = true)
{
var memoryArray = memory.GetArray();
byte[] data = memoryArray.Array;
int offset = memoryArray.Offset;
int length = memoryArray.Count;
int length = bytes.Length;
int num = 0xFFFF;
for (int i = offset; i < length; i++)
for (int i = 0; i < length; i++)
{
num = (num >> ((!crc16) ? 8 : 0)) ^ data[i];
num = (num >> ((!crc16) ? 8 : 0)) ^ bytes[i];
for (int j = 0; j < 8; j++)
{
int num2 = num & 1;
@@ -54,14 +50,14 @@ public class CRC16Utils
}
}
return (!crc16) ? new byte[2]
{
return (!crc16) ?
[
(byte)(num >> 8),
(byte)((uint)num & 0xFFu)
} : new byte[2]
{
] :
[
(byte)((uint)num & 0xFFu),
(byte)(num >> 8)
};
];
}
}

View File

@@ -6,7 +6,7 @@
<PackageReference Include="SqlSugar.TDengineCore" Version="4.18.0" />
<PackageReference Include="Rougamo.Fody" Version="3.0.0" />
<PackageReference Include="CS-Script" Version="4.8.16" />
<PackageReference Include="TouchSocket.Dmtp" Version="2.1.0-alpha.21" />
<PackageReference Include="TouchSocket.Dmtp" Version="2.1.0-alpha.22" />
</ItemGroup>
<ItemGroup>

View File

@@ -134,7 +134,7 @@ public static class Pool
{
//if (ms == null) return null;
var buf = requireResult ? ms.ToArray() : new Byte[0];
var buf = requireResult ? ms.ToArray() : [];
Pool.MemoryStream.Put(ms);
@@ -149,7 +149,7 @@ public static class Pool
{
//if (ms == null) return null;
var buf = returnResult ? ms.ToArray() : new Byte[0];
var buf = returnResult ? ms.ToArray() : [];
Pool.MemoryStream.Put(ms);

View File

@@ -71,7 +71,7 @@ public class QueueService<T> : IQueueService<T>
foreach (var item in clients)
{
var queue = item.Value;
rs += queue.Add(new[] { value });
rs += queue.Add([value]);
}
}

View File

@@ -20,7 +20,7 @@ public class PinYin
{
#region
private static readonly Int32[] pyValue = new[] {
private static readonly Int32[] pyValue = [
-20319, -20317, -20304, -20295, -20292, -20283, -20265, -20257, -20242,
-20230, -20051, -20036, -20032, -20026, -20002, -19990, -19986, -19982,
-19976, -19805, -19784, -19775, -19774, -19763, -19756, -19751, -19746,
@@ -65,9 +65,9 @@ public class PinYin
-11014, -10838, -10832, -10815, -10800, -10790, -10780, -10764, -10587,
-10544, -10533, -10519, -10331, -10329, -10328, -10322, -10315, -10309,
-10307, -10296, -10281, -10274, -10270, -10262, -10260, -10256, -10254
};
];
private static readonly String[] pyName = new[] {
private static readonly String[] pyName = [
"A", "Ai", "An", "Ang", "Ao", "Ba", "Bai", "Ban", "Bang", "Bao", "Bei",
"Ben", "Beng", "Bi", "Bian", "Biao", "Bie", "Bin", "Bing", "Bo", "Bu",
"Ba", "Cai", "Can", "Cang", "Cao", "Ce", "Ceng", "Cha", "Chai", "Chan",
@@ -108,14 +108,14 @@ public class PinYin
"Zheng", "Zhi", "Zhong", "Zhou", "Zhu", "Zhua", "Zhuai", "Zhuan",
"Zhuang", "Zhui", "Zhun", "Zhuo", "Zi", "Zong", "Zou", "Zu", "Zuan",
"Zui", "Zun", "Zuo"
};
];
#endregion
#region
/// <summary>二级汉字数组</summary>
private static readonly Char[] _py2 = new[] {
private static readonly Char[] _py2 = [
'亍','丌','兀','丐','廿','卅','丕','亘','丞','鬲','孬','噩','丨','禺','丿'
,'匕','乇','夭','爻','卮','氐','囟','胤','馗','毓','睾','鼗','','亟','鼐','乜'
,'乩','亓','芈','孛','啬','嘏','仄','厍','厝','厣','厥','厮','靥','赝','匚','叵'
@@ -308,10 +308,10 @@ public class PinYin
,'餍','餮','饕','饔','髟','髡','髦','髯','髫','髻','髭','髹','鬈','鬏','鬓','鬟'
,'鬣','麽','麾','縻','麂','麇','麈','麋','麒','鏖','麝','麟','黛','黜','黝','黠'
,'黟','黢','黩','黧','黥','黪','黯','鼢','鼬','鼯','鼹','鼷','鼽','鼾','齄'
};
];
/// <summary>二级汉字对应拼音数组</summary>
private static readonly String[] _pyValue2 = new[] {
private static readonly String[] _pyValue2 = [
"chu","ji","wu","gai","nian","sa","pi","gen","cheng","ge","nao","e","shu","yu","pie"
,"bi","tuo","yao","yao","zhi","di","xin","yin","kui","yu","gao","tao","dian","ji","nai","nie"
,"ji","qi","mi","bei","se","gu","ze","she","cuo","yan","jue","si","ye","yan","fang","po"
@@ -504,21 +504,21 @@ public class PinYin
,"yan","tie","tao","yong","biao","kun","mao","ran","tiao","ji","zi","xiu","quan","jiu","bin","huan"
,"lie","me","hui","mi","ji","jun","zhu","mi","qi","ao","she","lin","dai","chu","you","xia"
,"yi","qu","du","li","qing","can","an","fen","you","wu","yan","xi","qiu","han","zha"
};
];
#endregion
#region
private static readonly Char[] _py3 = new[] {
private static readonly Char[] _py3 = [
'沚', '埇', '瀍', '浉', '猇', '鄠', '崁', '埗', '漷', '甪',
'滘', '垱'
};
];
private static readonly String[] _pyValue3 = new[] {
private static readonly String[] _pyValue3 = [
"Zhi", "Yong", "Chan", "Shi", "Xiao", "Hu", "Kan", "Bu", "Huo", "Lu",
"Jiao", "Dang"
};
];
#endregion
@@ -656,7 +656,7 @@ public class PinYin
/// <returns>转换后的拼音(全拼)字符串</returns>
public static String[] GetAll(String str)
{
if (str.IsNullOrEmpty()) return new String[0];
if (str.IsNullOrEmpty()) return [];
// 重点地区支持
if (str == "重庆") return ["Chong", "Qing"];

View File

@@ -60,8 +60,8 @@ internal class SpeakProvider
{
try
{
synth = _type.CreateInstance(new Object[0]);
synth?.Invoke("SetOutputToDefaultAudioDevice", new Object[0]);
synth = _type.CreateInstance([]);
synth?.Invoke("SetOutputToDefaultAudioDevice", []);
}
catch
{

View File

@@ -95,7 +95,7 @@ public static class StringHelper
public static String[] Split(this String? value, params String[] separators)
{
//!! netcore3.0中新增Split(String? separator, StringSplitOptions options = StringSplitOptions.None)优先于StringHelper扩展
if (value == null || String.IsNullOrEmpty(value)) return new String[0];
if (value == null || String.IsNullOrEmpty(value)) return [];
if (separators == null || separators.Length <= 0 || separators.Length == 1 && separators[0].IsNullOrEmpty()) separators = [",", ";"];
return value.Split(separators, StringSplitOptions.RemoveEmptyEntries);
@@ -108,7 +108,7 @@ public static class StringHelper
/// <returns></returns>
public static Int32[] SplitAsInt(this String? value, params String[] separators)
{
if (value == null || String.IsNullOrEmpty(value)) return new Int32[0];
if (value == null || String.IsNullOrEmpty(value)) return [];
if (separators == null || separators.Length <= 0) separators = [",", ";"];
var ss = value.Split(separators, StringSplitOptions.RemoveEmptyEntries);
@@ -291,7 +291,7 @@ public static class StringHelper
public static Byte[] GetBytes(this String? value, Encoding? encoding = null)
{
//if (value == null) return null;
if (String.IsNullOrEmpty(value)) return new Byte[0];
if (String.IsNullOrEmpty(value)) return [];
encoding ??= Encoding.UTF8;
return encoding.GetBytes(value);
@@ -604,7 +604,7 @@ public static class StringHelper
/// <returns></returns>
public static String[] LevenshteinSearch(String key, String[] words)
{
if (IsNullOrWhiteSpace(key)) return new String[0];
if (IsNullOrWhiteSpace(key)) return [];
var keys = key.Split(new Char[] { ' ', ' ' }, StringSplitOptions.RemoveEmptyEntries);
@@ -677,7 +677,7 @@ public static class StringHelper
/// <returns></returns>
public static String[] LCSSearch(String key, String[] words)
{
if (IsNullOrWhiteSpace(key) || words == null || words.Length == 0) return new String[0];
if (IsNullOrWhiteSpace(key) || words == null || words.Length == 0) return [];
var keys = key
.Split(new Char[] { ' ', '\u3000' }, StringSplitOptions.RemoveEmptyEntries)

View File

@@ -198,7 +198,7 @@ public static class IOHelper
public static Byte[] ReadArray(this Stream des)
{
var len = des.ReadEncodedInt();
if (len <= 0) return new Byte[0];
if (len <= 0) return [];
// 避免数据错乱超长
//if (des.CanSeek && len > des.Length - des.Position) len = (Int32)(des.Length - des.Position);
@@ -242,7 +242,7 @@ public static class IOHelper
/// <returns>返回复制的总字节数</returns>
public static Byte[] ReadBytes(this Byte[] src, Int32 offset, Int32 count)
{
if (count == 0) return new Byte[0];
if (count == 0) return [];
// 即使是全部,也要复制一份,而不只是返回原数组,因为可能就是为了复制数组
if (count < 0) count = src.Length - offset;
@@ -284,7 +284,7 @@ public static class IOHelper
public static Byte[] ReadBytes(this Stream stream, Int64 length)
{
//if (stream == null) return null;
if (length == 0) return new Byte[0];
if (length == 0) return [];
if (length > 0 && stream.CanSeek && stream.Length - stream.Position < length)
throw new XException("Unable to read {1} bytes of data from a data stream with a length of only {0}", stream.Length - stream.Position, length);
@@ -844,7 +844,7 @@ public static class IOHelper
/// <returns></returns>
public static Byte[] ToHex(this String? data, Int32 startIndex = 0, Int32 length = -1)
{
if (data.IsNullOrEmpty()) return new Byte[0];
if (data.IsNullOrEmpty()) return [];
// 过滤特殊字符
data = data.Trim()
@@ -906,7 +906,7 @@ public static class IOHelper
/// <returns></returns>
public static Byte[] ToBase64(this String? data)
{
if (data.IsNullOrWhiteSpace()) return new Byte[0];
if (data.IsNullOrWhiteSpace()) return [];
data = data.Trim();
if (data[^1] != '=')

View File

@@ -461,7 +461,7 @@ public static class PathHelper
/// <returns></returns>
public static String[] CopyTo(this DirectoryInfo di, String destDirName, String? exts = null, Boolean allSub = false, Action<String>? callback = null)
{
if (!di.Exists) return new String[0];
if (!di.Exists) return [];
var list = new List<String>();
@@ -490,7 +490,7 @@ public static class PathHelper
public static String[] CopyToIfNewer(this DirectoryInfo di, String destDirName, String? exts = null, Boolean allSub = false, Action<String>? callback = null)
{
var dest = destDirName.AsDirectory();
if (!dest.Exists) return new String[0];
if (!dest.Exists) return [];
var list = new List<String>();

View File

@@ -29,14 +29,14 @@ public static class AttributeX
/// <returns></returns>
public static TAttribute[] GetCustomAttributes<TAttribute>(this Assembly assembly)
{
if (assembly == null) return new TAttribute[0];
if (assembly == null) return [];
var key = $"{assembly.FullName}_{typeof(TAttribute).FullName}";
return (TAttribute[])_asmCache.GetOrAdd(key, k =>
{
var atts = assembly.GetCustomAttributes(typeof(TAttribute), true) as TAttribute[];
return atts ?? (new TAttribute[0]);
return atts ?? ([]);
});
}

View File

@@ -82,7 +82,7 @@ public static class Reflect
/// <returns></returns>
public static MethodInfo[] GetMethodsEx(this Type type, String name, Int32 paramCount = -1)
{
if (name.IsNullOrEmpty()) return new MethodInfo[0];
if (name.IsNullOrEmpty()) return [];
return Provider.GetMethods(type, name, paramCount);
}

View File

@@ -129,7 +129,7 @@ public sealed class CbcTransform : ICryptoTransform
/// <exception cref="ArgumentException"></exception>
public Byte[] TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
{
if (inputCount == 0) return new Byte[0];
if (inputCount == 0) return [];
var blocks = inputCount / InputBlockSize;
var output = new Byte[blocks * OutputBlockSize];

View File

@@ -126,7 +126,7 @@ public sealed class PKCS7PaddingTransform : ICryptoTransform
/// <returns></returns>
public Byte[] TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
{
if (inputCount == 0) return new Byte[0];
if (inputCount == 0) return [];
if (_encryptMode)
{

View File

@@ -27,7 +27,7 @@ internal class RC4
/// <returns></returns>
public static Byte[] Encrypt(Byte[] data, Byte[] pass)
{
if (data == null || data.Length == 0) return new Byte[0];
if (data == null || data.Length == 0) return [];
if (pass == null || pass.Length == 0) return data;
var output = new Byte[data.Length];

View File

@@ -29,8 +29,8 @@ public class SM4 : SymmetricAlgorithm
KeySizeValue = 128;
BlockSizeValue = 128;
FeedbackSizeValue = BlockSizeValue;
LegalBlockSizesValue = new[] { new KeySizes(128, 128, 0) };
LegalKeySizesValue = new[] { new KeySizes(128, 128, 0) };
LegalBlockSizesValue = [new KeySizes(128, 128, 0)];
LegalKeySizesValue = [new KeySizes(128, 128, 0)];
Mode = CipherMode.ECB;
Padding = PaddingMode.PKCS7;
@@ -337,7 +337,7 @@ public class SM4Transform : ICryptoTransform
/// <exception cref="ArgumentException"></exception>
public Byte[] TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
{
if (inputCount == 0) return new Byte[0];
if (inputCount == 0) return [];
var blocks = inputCount / InputBlockSize;
var output = new Byte[blocks * OutputBlockSize];

View File

@@ -84,7 +84,7 @@ public sealed class ZerosPaddingTransform : ICryptoTransform
/// <returns></returns>
public Byte[] TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
{
if (inputCount == 0) return new Byte[0];
if (inputCount == 0) return [];
//todo !!! 仅能临时解决短密文填充清理问题
if (_encryptMode && inputCount % InputBlockSize != 0)

View File

@@ -162,7 +162,7 @@ public class Cron
}
var rs = new List<Int32>();
vs = new Int32[0];
vs = [];
// 递归处理混合值
if (value.Contains(','))

View File

@@ -71,7 +71,7 @@ public class TimerScheduler
private Thread? thread;
private Int32 _tid;
private TimerX[] Timers = new TimerX[0];
private TimerX[] Timers = [];
#endregion

View File

@@ -10,7 +10,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BootstrapBlazor" Version="8.6.3" />
<PackageReference Include="BootstrapBlazor" Version="8.6.4" />
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="8.0.4" />
</ItemGroup>

View File

@@ -17,7 +17,7 @@ public abstract class AppAuthorizeHandler : IAuthorizationHandler
/// <summary>
/// 刷新 Token 身份标识
/// </summary>
private readonly string[] _refreshTokenClaims = new[] { "f", "e", "s", "l", "k" };
private readonly string[] _refreshTokenClaims = ["f", "e", "s", "l", "k"];
/// <summary>
/// 授权验证核心方法

View File

@@ -41,10 +41,10 @@ public class SingleFilePublish : ISingleFilePublish
{
#if !Admin
InstanceFactory.CustomAssemblies =
new System.Reflection.Assembly[] { typeof(SqlSugar.TDengine.TDengineProvider).Assembly };
[typeof(SqlSugar.TDengine.TDengineProvider).Assembly];
#endif
return new[]
{
return
[
"ThingsGateway.Foundation",
"ThingsGateway.Core",
"ThingsGateway.NewLife.X",
@@ -54,6 +54,6 @@ public class SingleFilePublish : ISingleFilePublish
"ThingsGateway.Admin.Razor" ,
"ThingsGateway.Admin.Application" ,
"SqlSugar.TDengineCore",
};
];
}
}

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>6.0.3.47</Version>
<Version>6.0.3.50</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -145,7 +145,7 @@ internal class ModbusMasterTest
//源生成WriteData1与WriteData2方法(Write{属性名称})
await modbusVariable.WriteData3Async("123", default);
await modbusVariable.WriteData2Async(1, default);
await modbusVariable.WriteData1Async(new ushort[] { 1, 2 }, default);
await modbusVariable.WriteData1Async([1, 2], default);
//执行连读
await modbusVariable.MultiReadAsync();

View File

@@ -11,7 +11,7 @@
<PackageReference Include="ThingsGateway.Foundation.SiemensS7" Version="6.0.3.47" />
<PackageReference Include="ThingsGateway.Foundation.Variable" Version="8.6.0" />
<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="6.0.3.47" />
<PackageReference Include="TouchSocket.Modbus" Version="2.1.0-alpha.21" />
<PackageReference Include="TouchSocket.Modbus" Version="2.1.0-alpha.22" />
</ItemGroup>

View File

@@ -1,25 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using TouchSocket.Core;
namespace ThingsGateway.Foundation.Dlt645;
/// <summary>
/// Dlt645_2007DataHandleAdapter
/// </summary>
internal class Dlt645_2007DataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<Dlt645_2007Message>
{
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(Dlt645_2007Message request, IByteBlock byteBlock)
{
return Dlt645Helper.GetResponse(request, byteBlock);
}
}

View File

@@ -8,29 +8,69 @@
// QQ群605534569
//------------------------------------------------------------------------------
using TouchSocket.Core;
namespace ThingsGateway.Foundation.Dlt645;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class Dlt645_2007Response : Dlt645_2007Request
{
/// <summary>
/// 错误码
/// </summary>
public byte? ErrorCode { get; set; }
}
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class Dlt645_2007Message : MessageBase, IResultMessage
{
/// <inheritdoc/>
public override int HeadBytesLength => 0;
public override int HeaderLength { get; set; } = 10;
public ReadOnlyMemory<byte> SendBytes { get; set; }
public Dlt645_2007Address? Request { get; set; }
public Dlt645_2007Send? Dlt645_2007Send { get; set; }
public override void SendInfo(ReadOnlyMemory<byte> sendBytes)
public Dlt645_2007Response Response { get; set; } = new();
public override void SendInfo(ISendMessage sendMessage)
{
SendBytes = sendBytes;
Dlt645_2007Send = (sendMessage as Dlt645_2007Send);
Request = Dlt645_2007Send.Dlt645_2007Address;
}
private int HeadCodeIndex;
private readonly byte[] ReadStation = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA];
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[]? headBytes)
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
{
if (SendBytes.Length > 0)
if (Request != null)
{
BodyLength = 0;
//因为设备可能带有FE前导符开头这里找到0x68的位置
if (byteBlock != null)
{
for (int index = byteBlock.Position; index < byteBlock.Length; index++)
{
if (byteBlock[index] == 0x68)
{
HeadCodeIndex = index;
break;
}
}
}
//帧起始符 地址域 帧起始符 控制码 数据域长度共10个字节
HeaderLength = HeadCodeIndex - byteBlock.Position + 10;
if (byteBlock.CanReadLength < HeaderLength + HeadCodeIndex)
{
return true;
}
BodyLength = byteBlock[HeadCodeIndex + 9] + 2;
return true;
}
else
@@ -38,4 +78,70 @@ internal class Dlt645_2007Message : MessageBase, IResultMessage
return false;//不是主动请求的可能是心跳DTU包直接放弃
}
}
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
int sendHeadCodeIndex = Dlt645_2007Send.SendHeadCodeIndex;
var pos = byteBlock.Position - HeaderLength;
var endIndex = HeaderLength + BodyLength;
if (byteBlock[endIndex - 1] == 0x16)
{
//检查校验码
int sumCheck = 0;
for (int i = HeadCodeIndex; i < endIndex - 2; i++)
sumCheck += byteBlock[i];
if ((byte)sumCheck != byteBlock[endIndex - 2])
{
//校验错误
this.ErrorMessage = DltResource.Localizer["SumError"];
this.OperCode = 999;
return FilterResult.Success;
}
Response.Station = byteBlock.Span.Slice(HeadCodeIndex + 1, 6).ToArray();
if (!Response.Station.SequenceEqual(Request.Station))//设备地址不符合时,返回错误
{
if (!Request.Station.SequenceEqual(ReadStation))//读写通讯地址例外
{
this.ErrorMessage = DltResource.Localizer["StationNotSame"];
this.OperCode = 999;
return FilterResult.Success;
}
}
var controlCode = byteBlock[HeadCodeIndex + 8];
if ((controlCode & 0x40) == 0x40)//控制码bit6为1时返回错误
{
Response.ErrorCode = (byte)(byteBlock[HeadCodeIndex + 10] - 0x33);
var error = Dlt645Helper.Get2007ErrorMessage(Response.ErrorCode.Value);
this.ErrorMessage = DltResource.Localizer["FunctionError", $"0x{controlCode:X2}", error];
this.OperCode = 999;
return FilterResult.Success;
}
if (controlCode != ((byte)Dlt645_2007Send.ControlCode) + 0x80)//控制码不符合时,返回错误
{
this.ErrorMessage =
DltResource.Localizer["FunctionNotSame", $"0x{controlCode:X2}", $"0x{(byte)Dlt645_2007Send.ControlCode:X2}"];
this.OperCode = 999;
return FilterResult.Success;
}
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))
{
this.ErrorMessage = DltResource.Localizer["DataIdNotSame"];
this.OperCode = 999;
return FilterResult.Success;
}
}
this.OperCode = 0;
this.Content = byteBlock.ToArray(HeadCodeIndex + 10, BodyLength - 2);
return FilterResult.Success;
}
return FilterResult.GoOn;
}
}

View File

@@ -0,0 +1,158 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using System.Text;
using ThingsGateway.Foundation.Extension.String;
using TouchSocket.Core;
namespace ThingsGateway.Foundation.Dlt645;
/// <summary>
/// <inheritdoc/>
/// </summary>
public class Dlt645_2007Request
{
#region Request
/// <summary>
/// 数据标识
/// </summary>
public byte[] DataId { get; set; } = Array.Empty<byte>();
/// <summary>
/// 反转解析
/// </summary>
public bool Reverse { get; set; } = true;
/// <summary>
/// 站号信息
/// </summary>
public byte[] Station { get; set; } = Array.Empty<byte>();
#endregion Request
}
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class Dlt645_2007Send : ISendMessage
{
public int Sign { get; set; }
public int MaxLength => 300;
internal Dlt645_2007Address Dlt645_2007Address { get; }
public int SendHeadCodeIndex { get; private set; }
internal ControlCode ControlCode = default;
private byte[] Fehead = default;
/// <summary>
/// 密码、操作码
/// </summary>
private byte[] Codes = default;
/// <summary>
/// 写入值
/// </summary>
private string[] Datas = default;
public Dlt645_2007Send(Dlt645_2007Address dlt645_2007Address, ushort sign, ControlCode controlCode, byte[] fehead = default, byte[] codes = default, string[] datas = default)
{
Sign = sign;
Dlt645_2007Address = dlt645_2007Address;
ControlCode = controlCode;
Fehead = fehead ?? Array.Empty<byte>();
Codes = codes ?? Array.Empty<byte>();
Datas = datas ?? Array.Empty<string>();
}
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
{
if (Dlt645_2007Address?.DataId.Length < 4)
{
throw new(DltResource.Localizer["DataIdError"]);
}
if (Fehead.Length > 0)
{
byteBlock.Write(Fehead);//帧起始符
SendHeadCodeIndex = Fehead.Length;
}
byteBlock.WriteByte(0x68);//帧起始符
byteBlock.Write(Dlt645_2007Address.Station);//6个字节地址域
byteBlock.WriteByte(0x68);//帧起始符
byteBlock.WriteByte((byte)ControlCode);//控制码
byteBlock.WriteByte((byte)(Dlt645_2007Address.DataId.Length));//数据域长度
byteBlock.Write(Dlt645_2007Address.DataId);//数据域标识DI3、DI2、DI1、DI0
byteBlock.Write(Codes);
if (Datas.Length > 0)
{
var dataInfos = Dlt645Helper.GetDataInfos(Dlt645_2007Address.DataId);
if (Datas.Length != dataInfos.Count)
{
throw new(DltResource.Localizer["CountError"]);
}
for (int i = 0; i < Datas.Length; i++)
{
var dataInfo = dataInfos[i];
byte[] data;
if (dataInfo.IsSigned)//可能为负数
{
var doubleValue = Convert.ToDouble(Datas[i]);
if (dataInfo.Digtal != 0)//无小数点
{
doubleValue *= Math.Pow(10.0, dataInfo.Digtal);
}
data = doubleValue.ToString().HexStringToBytes().Reverse().ToArray();
if (doubleValue < 0)
{
data[0] = (byte)(data[0] & 0x80);
}
}
else
{
if (dataInfo.Digtal < 0)
{
data = Encoding.ASCII.GetBytes(Datas[i]).Reverse().ToArray();
}
else if (dataInfo.Digtal == 0)//无小数点
{
data = Datas[i].HexStringToBytes().Reverse().ToArray();
}
else
{
data = (Convert.ToDouble(Datas[i]) * Math.Pow(10.0, dataInfo.Digtal)).ToString().HexStringToBytes().Reverse().ToArray();
}
}
byteBlock.Write(data);
}
}
byteBlock[Fehead.Length + 9] = (byte)(byteBlock.Length - 10 - Fehead.Length);//数据域长度
for (int index = Fehead.Length + 10; index < byteBlock.Length; ++index)
byteBlock[index] += 0x33;//传输时发送方按字节进行加33H处理接收方按字节进行减33H处理
int num = 0;
for (int index = Fehead.Length; index < byteBlock.Length; ++index)
num += byteBlock[index];
byteBlock.WriteByte((byte)num);//校验码,总加和
byteBlock.WriteByte((byte)0x16);//结束符
}
}

View File

@@ -1,24 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using TouchSocket.Core;
namespace ThingsGateway.Foundation.Dlt645;
/// <summary>
/// Dlt645_2007UdpDataHandleAdapter
/// </summary>
internal class Dlt645_2007UdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<Dlt645_2007Message>
{
protected override AdapterResult UnpackResponse(Dlt645_2007Message request, IByteBlock byteBlock)
{
return Dlt645Helper.GetResponse(request, byteBlock);
}
}

View File

@@ -37,12 +37,12 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
public string Password { get; set; }
/// <inheritdoc/>
public string Station { get; set; }
public string Station { get; set; } = "111111111111";
/// <summary>
/// 默认Dtu注册包,utf-8字符串
/// </summary>
public string DtuId { get; set; } = "TEST";
public string DtuId { get; set; } = "DtuId";
/// <summary>
/// 客户端连接滑动过期时间(TCP服务通道时)
@@ -79,18 +79,18 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
case ChannelTypeEnum.TcpClient:
case ChannelTypeEnum.TcpService:
case ChannelTypeEnum.SerialPort:
return new Dlt645_2007DataHandleAdapter
return new ProtocolSingleStreamDataHandleAdapter<Dlt645_2007Message>
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
case ChannelTypeEnum.UdpSession:
return new Dlt645_2007UdpDataHandleAdapter()
return new ProtocolUdpDataHandleAdapter<Dlt645_2007Message>()
{
};
}
return new Dlt645_2007DataHandleAdapter
return new ProtocolSingleStreamDataHandleAdapter<Dlt645_2007Message>
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
@@ -103,33 +103,17 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
}
/// <inheritdoc/>
public override async ValueTask<OperResult<string[]>> ReadStringAsync(string address, int length, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
{
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(ref address);
var result = await ReadAsync(address, GetLength(address, length, 8), cancellationToken).ConfigureAwait(false);
return result.OperResultFrom(() => new[] { bitConverter.ToString(result.Content, 0, length) });
}
/// <inheritdoc/>
public override async ValueTask<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
public async ValueTask<OperResult<byte[]>> Dlt645RequestAsync(Dlt645_2007Address dAddress, ControlCode controlCode, string feHead, byte[] codes = default, string[] datas = default, CancellationToken cancellationToken = default)
{
try
{
var dAddress = Dlt645_2007Address.ParseFrom(address, Station, DtuId);
var channelResult = GetChannel(dAddress.SocketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var waitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var sign);
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, dAddress, (byte)ControlCode.Read, Station, FEHead);
if (!commandResult.IsSuccess) return new OperResult<byte[]>(commandResult);
return await this.SendThenReturnAsync(new SendMessage(commandResult.Content) { Sign = sign }, waitData, cancellationToken, channelResult.Content).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
var waitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var sign);
return await this.SendThenReturnAsync(
GetSendMessage(dAddress, (ushort)sign, controlCode, feHead, codes, datas),
channelResult.Content, waitData, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -137,6 +121,44 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
}
}
/// <inheritdoc/>
public async ValueTask<OperResult> Dlt645SendAsync(Dlt645_2007Address dAddress, ControlCode controlCode, string feHead, byte[] codes = default, string[] datas = default, CancellationToken cancellationToken = default)
{
try
{
var channelResult = GetChannel(dAddress.SocketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
await this.SendAsync(
GetSendMessage(dAddress, (ushort)0, controlCode, feHead, codes, datas),
channelResult.Content, cancellationToken).ConfigureAwait(false);
return OperResult.Success;
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
private ISendMessage GetSendMessage(Dlt645_2007Address dAddress, ushort sign, ControlCode read, string feHead, byte[] codes = default, string[] datas = default)
{
return new Dlt645_2007Send(dAddress, sign, read, feHead.HexStringToBytes(), codes, datas);
}
/// <inheritdoc/>
public override ValueTask<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
var dAddress = Dlt645_2007Address.ParseFrom(address, Station, DtuId);
return Dlt645RequestAsync(dAddress, ControlCode.Read, FEHead, cancellationToken: cancellationToken);
}
catch (Exception ex)
{
return EasyValueTask.FromResult(new OperResult<byte[]>(ex));
}
}
/// <inheritdoc/>
public override async ValueTask<OperResult> WriteAsync(string address, string value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
{
@@ -149,24 +171,10 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
if (OperCode.Length < 8)
OperCode = OperCode.PadLeft(8, '0');
var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
var codes = DataTransUtil.SpliceArray(Password.HexStringToBytes(), OperCode.HexStringToBytes());
string[] strArray = value.SplitStringBySemicolon();
var dAddress = Dlt645_2007Address.ParseFrom(address, Station, DtuId);
var channelResult = GetChannel(dAddress.SocketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var waitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var sign);
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, dAddress, (byte)ControlCode.Write, Station, FEHead, data, strArray);
if (!commandResult.IsSuccess) return new OperResult<byte[]>(commandResult);
return await this.SendThenReturnAsync(new SendMessage(commandResult.Content) { Sign = sign }, waitData, cancellationToken, channelResult.Content).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
return await Dlt645RequestAsync(dAddress, ControlCode.Write, FEHead, codes, strArray, cancellationToken: cancellationToken);
}
catch (Exception ex)
{
@@ -177,7 +185,26 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
#region
/// <inheritdoc/>
public override ValueTask<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => throw new NotSupportedException();
public override async ValueTask<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
Password ??= string.Empty;
OperCode ??= string.Empty;
if (Password.Length < 8)
Password = Password.PadLeft(8, '0');
if (OperCode.Length < 8)
OperCode = OperCode.PadLeft(8, '0');
var codes = DataTransUtil.SpliceArray(Password.HexStringToBytes(), OperCode.HexStringToBytes());
var dAddress = Dlt645_2007Address.ParseFrom(address, Station, DtuId);
return await Dlt645RequestAsync(dAddress, ControlCode.Write, FEHead, codes, cancellationToken: cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override ValueTask<OperResult> WriteAsync(string address, uint value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), bitConverter, cancellationToken);
@@ -221,18 +248,13 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
{
try
{
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, (byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().ToArray(), "999999999999".ByHexStringToBytes(), FEHead);
await this.SendAsync(socketId, new SendMessage(commandResult), cancellationToken).ConfigureAwait(false);
return OperResult.Success;
}
finally
{
valueByteBlock.SafeDispose();
}
string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
Dlt645_2007Address dAddress = new();
dAddress.Station = str.HexStringToBytes();
dAddress.DataId = "999999999999".HexStringToBytes();
dAddress.SocketId = socketId;
return await Dlt645SendAsync(dAddress, ControlCode.BroadcastTime, FEHead, cancellationToken: cancellationToken);
}
catch (Exception ex)
{
@@ -245,26 +267,20 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
/// </summary>
/// <param name="socketId">socketId</param>
/// <param name="dateTime">时间</param>
/// <param name="station">表号</param>
/// <param name="cancellationToken">取消令箭</param>
/// <returns></returns>
public async ValueTask<OperResult> FreezeAsync(string socketId, DateTime dateTime, CancellationToken cancellationToken = default)
public async ValueTask<OperResult> FreezeAsync(string socketId, DateTime dateTime, string station = null, CancellationToken cancellationToken = default)
{
try
{
string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}";
if (Station.IsNullOrEmpty()) Station = string.Empty;
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
Dlt645_2007Address dAddress = new();
dAddress.SetStation(station ?? Station);
dAddress.DataId = str.HexStringToBytes();
dAddress.SocketId = socketId;
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, (byte)ControlCode.Freeze, str.ByHexStringToBytes().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray(), FEHead);
return await this.SendThenReturnAsync(socketId, commandResult, cancellationToken: cancellationToken).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
return await Dlt645RequestAsync(dAddress, ControlCode.Freeze, FEHead, cancellationToken: cancellationToken);
}
catch (Exception ex)
{
@@ -282,24 +298,19 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
{
try
{
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
Dlt645_2007Address dAddress = new();
dAddress.SetStation("AAAAAAAAAAAA");
dAddress.SocketId = socketId;
var result = await Dlt645RequestAsync(dAddress, ControlCode.ReadStation, FEHead, cancellationToken: cancellationToken);
if (result.IsSuccess)
{
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, (byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes(), FEHead);
var result = await this.SendThenReturnAsync(socketId, commandResult, cancellationToken: cancellationToken).ConfigureAwait(false);
if (result.IsSuccess)
{
var buffer = result.Content.SelectMiddle(0, 6).BytesAdd(-0x33);
return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString());
}
else
{
return new OperResult<string>(result);
}
var buffer = result.Content.SelectMiddle(0, 6).BytesAdd(-0x33);
return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString());
}
finally
else
{
valueByteBlock.SafeDispose();
return new OperResult<string>(result);
}
}
catch (Exception ex)
@@ -313,9 +324,10 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
/// </summary>
/// <param name="socketId">socketId</param>
/// <param name="baudRate">波特率</param>
/// <param name="station">表号</param>
/// <param name="cancellationToken">取消令箭</param>
/// <returns></returns>
public async ValueTask<OperResult> WriteBaudRateAsync(string socketId, int baudRate, CancellationToken cancellationToken = default)
public async ValueTask<OperResult> WriteBaudRateAsync(string socketId, int baudRate, string station = null, CancellationToken cancellationToken = default)
{
try
{
@@ -330,19 +342,13 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
case 19200: baudRateByte = 0x40; break;
default: return new OperResult<string>(DltResource.Localizer["BaudRateError", baudRate]);
}
if (Station.IsNullOrEmpty()) Station = string.Empty;
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, (byte)ControlCode.WriteBaudRate, new byte[1] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray(), FEHead);
return await this.SendThenReturnAsync(socketId, commandResult, cancellationToken: cancellationToken).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
Dlt645_2007Address dAddress = new();
dAddress.SetStation(station ?? Station);
dAddress.SocketId = socketId;
dAddress.DataId = [baudRateByte];
return await Dlt645RequestAsync(dAddress, ControlCode.ReadStation, FEHead, cancellationToken: cancellationToken);
}
catch (Exception ex)
{
@@ -361,16 +367,12 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
{
try
{
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, (byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes(), FEHead);
return await this.SendThenReturnAsync(socketId, commandResult, cancellationToken: cancellationToken).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
Dlt645_2007Address dAddress = new();
dAddress.SocketId = socketId;
dAddress.Station = "AAAAAAAAAAAA".HexStringToBytes();
dAddress.DataId = station.HexStringToBytes().Reverse().ToArray();
return await Dlt645RequestAsync(dAddress, ControlCode.WriteStation, FEHead, cancellationToken: cancellationToken);
}
catch (Exception ex)
{
@@ -385,32 +387,26 @@ public class Dlt645_2007Master : ProtocolBase, IDtu
/// <param name="level">密码等级0-8</param>
/// <param name="oldPassword">旧密码</param>
/// <param name="newPassword">新密码</param>
/// <param name="station">station</param>
/// <param name="cancellationToken">取消令箭</param>
/// <returns></returns>
public async ValueTask<OperResult> WritePasswordAsync(string socketId, byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
public async ValueTask<OperResult> WritePasswordAsync(string socketId, byte level, string oldPassword, string newPassword, string station = null, CancellationToken cancellationToken = default)
{
try
{
if (Station.IsNullOrEmpty()) Station = string.Empty;
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
string str = $"04000C{level + 1:D2}";
var bytes = DataTransUtil.SpliceArray(str.ByHexStringToBytes().Reverse().ToArray()
, (oldPassword.ByHexStringToBytes().Reverse().ToArray())
, (newPassword.ByHexStringToBytes().Reverse().ToArray()));
var bytes = DataTransUtil.SpliceArray(str.HexStringToBytes().Reverse().ToArray()
, (oldPassword.HexStringToBytes().Reverse().ToArray())
, (newPassword.HexStringToBytes().Reverse().ToArray()));
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var commandResult = Dlt645Helper.GetDlt645_2007Command(ref valueByteBlock, (byte)ControlCode.WritePassword,
bytes
, Station.ByHexStringToBytes().Reverse().ToArray(), FEHead);
return await this.SendThenReturnAsync(socketId, commandResult, cancellationToken: cancellationToken).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
Dlt645_2007Address dAddress = new();
dAddress.SetStation(station ?? Station);
dAddress.SocketId = socketId;
dAddress.Station = "AAAAAAAAAAAA".HexStringToBytes();
dAddress.DataId = bytes;
return await Dlt645RequestAsync(dAddress, ControlCode.WritePassword, FEHead, cancellationToken: cancellationToken);
}
catch (Exception ex)
{

View File

@@ -13,7 +13,7 @@ namespace ThingsGateway.Foundation.Dlt645;
/// <summary>
/// 控制码
/// </summary>
internal enum ControlCode : byte
public enum ControlCode : byte
{
/// <summary>
/// 读数据

View File

@@ -21,7 +21,7 @@ namespace ThingsGateway.Foundation.Dlt645;
/// <summary>
/// Dlt645_2007Address
/// </summary>
public class Dlt645_2007Address
public class Dlt645_2007Address : Dlt645_2007Request
{
/// <summary>
/// <inheritdoc/>
@@ -30,20 +30,25 @@ public class Dlt645_2007Address
{
}
/// <summary>
/// 数据标识
/// </summary>
public byte[] DataId { get; set; } = Array.Empty<byte>();
public Dlt645_2007Address(Dlt645_2007Address dlt645_2007Address)
{
this.SocketId = dlt645_2007Address.SocketId;
this.DataId = dlt645_2007Address.DataId;
this.Reverse = dlt645_2007Address.Reverse;
this.Station = dlt645_2007Address.Station;
}
/// <summary>
/// 反转解析
/// </summary>
public bool Reverse { get; set; } = true;
public void SetStation(string station)
{
if (station.Length < 12)
station = station.PadLeft(12, '0');
Station = station.HexStringToBytes().Reverse().ToArray();
}
/// <summary>
/// 站号信息
/// </summary>
public byte[] Station { get; set; } = Array.Empty<byte>();
public void SetDataId(string dataId)
{
DataId = dataId.HexStringToBytes().Reverse().ToArray();
}
/// <summary>
/// 作为Slave时需提供的SocketId用于分辨Socket客户端通常对比的是初始链接时的注册包
@@ -78,26 +83,21 @@ public class Dlt645_2007Address
/// </summary>
public static Dlt645_2007Address ParseFrom(string address, string defaultStation = null, string dtuid = null, bool isCache = true)
{
var cacheKey = $"{nameof(ParseFrom)}_{typeof(Dlt645_2007Address).FullName}_{typeof(Dlt645_2007Address).TypeHandle.Value}_{address}";
var cacheKey = $"{nameof(ParseFrom)}_{typeof(Dlt645_2007Address).FullName}_{typeof(Dlt645_2007Address).TypeHandle.Value}_{address}_{defaultStation}_{dtuid}";
if (isCache)
if (MemoryCache.Instance.TryGetValue(cacheKey, out Dlt645_2007Address dAddress))
return dAddress;
return new(dAddress);
Dlt645_2007Address dlt645_2007Address = new();
if (defaultStation != null)
if (!defaultStation.IsNullOrEmpty())
{
if (defaultStation.IsNullOrEmpty()) defaultStation = string.Empty;
if (defaultStation.Length < 12)
defaultStation = defaultStation.PadLeft(12, '0');
dlt645_2007Address.Station = defaultStation.ByHexStringToBytes().Reverse().ToArray();
dlt645_2007Address.SetStation(defaultStation);
}
if (dtuid != null)
dlt645_2007Address.SocketId = dtuid;
byte[] array;
array = Array.Empty<byte>();
if (address.IndexOf(';') < 0)
{
array = address.ByHexStringToBytes().Reverse().ToArray();
dlt645_2007Address.SetDataId(address);
}
else
{
@@ -108,10 +108,9 @@ public class Dlt645_2007Address
if (strArray[index].ToUpper().StartsWith("S="))
{
var station = strArray[index].Substring(2);
if (station.IsNullOrEmpty()) station = string.Empty;
if (station.Length < 12)
station = station.PadLeft(12, '0');
dlt645_2007Address.Station = station.ByHexStringToBytes().Reverse().ToArray();
dlt645_2007Address.Station = station.HexStringToBytes().Reverse().ToArray();
}
else if (strArray[index].Contains("R="))
{
@@ -123,15 +122,14 @@ public class Dlt645_2007Address
}
else if (!strArray[index].Contains("="))
{
array = strArray[index].ByHexStringToBytes().Reverse().ToArray();
dlt645_2007Address.SetDataId(strArray[index]);
}
}
}
dlt645_2007Address.DataId = array;
if (isCache)
MemoryCache.Instance.Set(cacheKey, dlt645_2007Address, 3600);
return dlt645_2007Address;
return new(dlt645_2007Address);
}
}

View File

@@ -10,8 +10,6 @@
using System.Text;
using ThingsGateway.Foundation.Extension.Generic;
using TouchSocket.Core;
namespace ThingsGateway.Foundation.Dlt645;
@@ -94,14 +92,13 @@ public class Dlt645_2007BitConverter : ThingsGatewayBitConverter
/// <inheritdoc/>
public override string ToString(byte[] buffer, int offset, int length)
{
buffer = buffer.RemoveBegin(offset);
buffer = buffer.BytesAdd(-0x33);
var dataInfos = Dlt645Helper.GetDataInfos(buffer);
var data = new ReadOnlySpan<byte>(buffer, offset, length).BytesAdd(-0x33);
var dataInfos = Dlt645Helper.GetDataInfos(data);
StringBuilder stringBuilder = new();
foreach (var dataInfo in dataInfos)
{
//实际数据
var content = buffer.SelectMiddle(4, dataInfo.ByteLength).Reverse().ToArray();
var content = data.Slice(4, dataInfo.ByteLength).ToArray().Reverse().ToArray();
if (dataInfo.IsSigned)//可能为负数
{
if (content[0] > 0x80)//最高位是表示正负
@@ -129,7 +126,7 @@ public class Dlt645_2007BitConverter : ThingsGatewayBitConverter
return stringBuilder.ToString();
static void ToString(StringBuilder stringBuilder, DataInfo dataInfo, byte[] content)
static void ToString(StringBuilder stringBuilder, Dlt645DataInfo dataInfo, byte[] content)
{
if (dataInfo.Digtal < 0)
{

View File

@@ -33,7 +33,6 @@ internal static class PackHelper
IThingsGatewayBitConverter transformParameter = byteConverter.GetTransByAddress(ref address);
item.ThingsGatewayBitConverter = transformParameter;
item.Index = 0;
//item.Address = address;
if (item.DataType == DataTypeEnum.Boolean)
item.Index = device.GetBitOffset(item.RegisterAddress);
var r = new T()

View File

@@ -17,94 +17,78 @@ using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// Modbus协议地址
/// Modbus协议地址,字符串不包含长度与写入值、读写类型
/// </summary>
public class ModbusAddress
public class ModbusAddress : ModbusRequest
{
public ModbusAddress()
{
}
/// <summary>
/// 可能带小数点的地址表示
/// </summary>
public string Address { get; set; }
public ModbusAddress(ModbusRequest modbusAddress)
{
this.StartAddress = modbusAddress.StartAddress;
this.Data = modbusAddress.Data;
this.FunctionCode = modbusAddress.FunctionCode;
this.Length = modbusAddress.Length;
this.Station = modbusAddress.Station;
this.StartAddress = modbusAddress.StartAddress;
}
/// <summary>
/// 起始地址
/// </summary>
public ushort AddressStart => (ushort)Address.ToInt();
/// <summary>
/// 读取功能码
/// </summary>
public byte ReadFunction { get; set; }
/// <summary>
/// 站号信息
/// </summary>
public byte Station { get; set; }
/// <summary>
/// 写入功能码
/// </summary>
public byte? WriteFunction { get; set; }
public ModbusAddress(ModbusAddress modbusAddress)
{
this.StartAddress = modbusAddress.StartAddress;
this.Data = modbusAddress.Data;
this.FunctionCode = modbusAddress.FunctionCode;
this.Length = modbusAddress.Length;
this.BitIndex = modbusAddress.BitIndex;
this.SocketId = modbusAddress.SocketId;
this.WriteFunctionCode = modbusAddress.WriteFunctionCode;
this.Station = modbusAddress.Station;
this.StartAddress = modbusAddress.StartAddress;
}
/// <summary>
/// BitIndex
/// </summary>
public int? BitIndex
{
get
{
var data = Address?.SplitStringByDelimiter();
if (data?.Length == 2)
{
return Convert.ToInt32(data[1]);
}
else
{
return null;
}
}
}
public int? BitIndex { get; set; }
/// <summary>
/// 打包临时写入,需要读取的字节长度
/// 写入功能码,主站请求时生效,其余情况应选择<see cref="ModbusRequest.FunctionCode"/>
/// </summary>
public int? ByteLength { get; set; }
/// <summary>
/// 读取终止
/// </summary>
public uint AddressEnd => (ushort)(AddressStart + (ByteLength != null ? (Math.Ceiling(ByteLength!.Value / 2.0) > 0 ? Math.Ceiling(ByteLength!.Value / 2.0) : 1) : 1));
public byte? WriteFunctionCode { get; set; }
/// <summary>
/// 作为Slave时需提供的SocketId用于分辨Socket客户端通常对比的是初始链接时的注册包
/// </summary>
public string? SocketId { get; set; }
/// <summary>
/// 读取终止
/// </summary>
public int AddressEnd => (ushort)(StartAddress + Math.Max(Math.Ceiling(Length / 2.0), 1));
/// <inheritdoc/>
public override string ToString()
{
StringBuilder stringGeter = new();
if (Station > 0)
{
stringGeter.Append($"s={Station};");
stringGeter.Append($"S={Station};");
}
if (WriteFunction > 0)
if (WriteFunctionCode > 0)
{
stringGeter.Append($"w={WriteFunction};");
stringGeter.Append($"W={WriteFunctionCode};");
}
if (!string.IsNullOrEmpty(SocketId))
{
stringGeter.Append($"id={SocketId};");
stringGeter.Append($"ID={SocketId};");
}
stringGeter.Append($"{GetFunctionString(ReadFunction)}{AddressStart + 1}{(BitIndex != null ? $".{BitIndex}" : null)}");
stringGeter.Append($"{GetFunctionString(FunctionCode)}{StartAddress + 1}{(BitIndex != null ? $".{BitIndex}" : null)}");
return stringGeter.ToString();
}
private string GetFunctionString(int readF)
private string GetFunctionString(byte readF)
{
return readF switch
{
@@ -125,7 +109,7 @@ public class ModbusAddress
var cacheKey = $"{nameof(ParseFrom)}_{typeof(ModbusAddress).FullName}_{typeof(ModbusAddress).TypeHandle.Value}_{station}_{dtuid}_{address}";
if (isCache)
if (MemoryCache.Instance.TryGetValue(cacheKey, out ModbusAddress mAddress))
return mAddress;
return new(mAddress);
var modbusAddress = new ModbusAddress();
if (station != null)
@@ -143,13 +127,13 @@ public class ModbusAddress
{
if (strArray[index].ToUpper().StartsWith("S="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
if (Convert.ToByte(strArray[index].Substring(2)) > 0)
modbusAddress.Station = byte.Parse(strArray[index].Substring(2));
}
else if (strArray[index].ToUpper().StartsWith("W="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
modbusAddress.WriteFunction = byte.Parse(strArray[index].Substring(2));
if (Convert.ToByte(strArray[index].Substring(2)) > 0)
modbusAddress.WriteFunctionCode = byte.Parse(strArray[index].Substring(2));
}
else if (strArray[index].ToUpper().StartsWith("ID="))
{
@@ -165,7 +149,7 @@ public class ModbusAddress
if (isCache)
MemoryCache.Instance.Set(cacheKey, modbusAddress, 3600);
return modbusAddress;
return new(modbusAddress);
void Address(string address)
{
@@ -175,29 +159,26 @@ public class ModbusAddress
switch (readF)
{
case 0:
modbusAddress.ReadFunction = 1;
modbusAddress.FunctionCode = 1;
break;
case 1:
modbusAddress.ReadFunction = 2;
modbusAddress.FunctionCode = 2;
break;
case 3:
modbusAddress.ReadFunction = 4;
modbusAddress.FunctionCode = 4;
break;
case 4:
modbusAddress.ReadFunction = 3;
modbusAddress.FunctionCode = 3;
break;
}
string[] strArray = address.SplitStringByDelimiter();
modbusAddress.StartAddress = (ushort)(ushort.Parse(strArray[0].Substring(1)) - 1);
if (strArray.Length > 1)
{
modbusAddress.Address = (ushort.Parse(strArray[0].Substring(1)) - 1).ToString() + '.' + strArray[1];
}
else
{
modbusAddress.Address = (ushort.Parse(strArray[0].Substring(1)) - 1).ToString();
modbusAddress.BitIndex = int.Parse(strArray[1]);
}
}
}

View File

@@ -12,58 +12,6 @@ namespace ThingsGateway.Foundation.Modbus;
internal class ModbusHelper
{
#region modbusServer
internal static byte[] ModbusServerAnalysisAddressValue(IModbusServerMessage request, IByteBlock response)
{
var offset = response.Position;
//解析01 03 00 00 00 0A
var station = response[0 + offset];
var function = response[1 + offset];
response.Position = 2 + offset;
int addressStart = response.ReadUInt16(EndianType.Big);
if (function > 4)
{
if (function > 6)
{
request.ModbusAddress = new ModbusAddress()
{
Station = station,
Address = addressStart.ToString(),
WriteFunction = function,
ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3),
};
request.Length = response[5 + offset];
return response.ToArray(7 + offset);
}
else
{
request.ModbusAddress = new ModbusAddress()
{
Station = station,
Address = addressStart.ToString(),
WriteFunction = function,
ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3),
};
request.Length = 1;
return response.ToArray(4 + offset);
}
}
else
{
request.ModbusAddress = new ModbusAddress()
{
Station = station,
Address = addressStart.ToString(),
ReadFunction = function,
};
request.Length = response[5 + offset];
return Array.Empty<byte>();
}
}
#endregion modbusServer
#region
/// <summary>
@@ -95,225 +43,5 @@ internal class ModbusHelper
};
}
/// <summary>
/// 获取modbus数据区内容response需去除Crc和tcp报文头例如01 03 02 00 01
/// </summary>
/// <param name="send">发送数据</param>
/// <param name="response">返回数据</param>
/// <returns></returns>
internal static OperResult<AdapterResult> GetModbusData(ReadOnlySpan<byte> send, IByteBlock response)
{
try
{
if (response[response.Position + 1] >= 0x80)//错误码
return new OperResult<AdapterResult>(GetDescriptionByErrorCode(response[response.Position + 2])) { Content = new AdapterResult() { FilterResult = FilterResult.Success } };
if (send.Length == 0)
{
return new OperResult<AdapterResult>()
{
Content = new()
{
Content = response.ToArray(response.Position + 3, response[response.Position + 2]),
FilterResult = FilterResult.Success
}
};
}
//站号验证
if (send[response.Position + 0] != response[response.Position + 0])
return new OperResult<AdapterResult>(ModbusResource.Localizer["StationNotSame", send[response.Position + 0], response[response.Position + 0]])
{
Content = new()
{
FilterResult = FilterResult.Success
}
};
//功能码验证
if (send[response.Position + 1] != response[response.Position + 1])
return new OperResult<AdapterResult>(ModbusResource.Localizer["FunctionNotSame", send[response.Position + 1], response[response.Position + 1]])
{
Content = new()
{
FilterResult = FilterResult.Success
}
};
return new OperResult<AdapterResult>()
{
Content = new()
{
Content = response.ToArray(response.Position + 3, response[response.Position + 2]),
FilterResult = FilterResult.Success
}
};
}
catch (Exception ex)
{
return new OperResult<AdapterResult>(ex)
{
Content = new()
{
FilterResult = FilterResult.Success
}
};
}
}
/// <summary>
/// 检测crc
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
internal static OperResult CheckCrc(IByteBlock response)
{
//crc校验
var dataLen = response.Length;
var crc = CRC16Utils.Crc16Only(new ReadOnlyMemory<byte>(response.AsSegment().Array, 0, dataLen - 2));
if ((response[dataLen - 2] != crc[0] || response[dataLen - 1] != crc[1]))
return new OperResult($"{ModbusResource.Localizer["CrcError"]} {DataTransUtil.ByteToHexString(response.Span, ' ')}")
{
};
response.SetLength(dataLen - 2);
return OperResult.Success;
}
/// <summary>
/// 去除Crc返回modbus数据区
/// </summary>
/// <param name="send"></param>
/// <param name="response"></param>
/// <returns></returns>
internal static OperResult<AdapterResult> GetModbusRtuData(ReadOnlySpan<byte> send, IByteBlock response)
{
var result = CheckCrc(response);
if (!result.IsSuccess)
{
return new OperResult<AdapterResult>(result)
{
Content = new() { FilterResult = FilterResult.Success }
};
}
return GetModbusData(send, response);
}
#endregion
#region
/// <summary>
/// 获取读取报文
/// </summary>
internal static void GetReadModbusCommand(ref ValueByteBlock valueByteBlock, ModbusAddress mAddress, ushort length, ModbusTypeEnum modbusType, ushort sign, ushort protocolId)
{
if (modbusType == ModbusTypeEnum.ModbusTcp)
{
valueByteBlock.WriteUInt16(sign, EndianType.Big);
valueByteBlock.WriteUInt16(protocolId, EndianType.Big);
valueByteBlock.WriteUInt16(6, EndianType.Big);
}
valueByteBlock.WriteByte(mAddress.Station);
valueByteBlock.WriteByte(mAddress.ReadFunction);
valueByteBlock.WriteUInt16(mAddress.AddressStart, EndianType.Big);
valueByteBlock.WriteUInt16(length, EndianType.Big);
if (modbusType == ModbusTypeEnum.ModbusRtu)
{
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
}
}
/// <summary>
/// 获取05写入布尔量报文
/// </summary>
internal static void GetWriteBoolModbusCommand(ref ValueByteBlock valueByteBlock, ModbusAddress mAddress, bool value, ModbusTypeEnum modbusType, ushort sign, ushort protocolId)
{
if (modbusType == ModbusTypeEnum.ModbusTcp)
{
valueByteBlock.WriteUInt16(sign, EndianType.Big);
valueByteBlock.WriteUInt16(protocolId, EndianType.Big);
valueByteBlock.WriteUInt16(6, EndianType.Big);
}
valueByteBlock.WriteByte(mAddress.Station);
valueByteBlock.WriteByte(5);
valueByteBlock.WriteUInt16(mAddress.AddressStart, EndianType.Big);
valueByteBlock.Write(value ? new byte[] { 0xFF, 0 } : new byte[] { 0, 0 });
if (modbusType == ModbusTypeEnum.ModbusRtu)
{
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
}
}
/// <summary>
/// 获取15写入布尔量报文
/// </summary>
internal static void GetWriteBoolModbusCommand(ref ValueByteBlock valueByteBlock, ModbusAddress mAddress, bool[] values, ushort length, ModbusTypeEnum modbusType, ushort sign, ushort protocolId)
{
if (modbusType == ModbusTypeEnum.ModbusTcp)
{
valueByteBlock.WriteUInt16(sign, EndianType.Big);
valueByteBlock.WriteUInt16(protocolId, EndianType.Big);
valueByteBlock.WriteUInt16(6, EndianType.Big);
}
valueByteBlock.WriteByte(mAddress.Station);
valueByteBlock.WriteByte(15);
valueByteBlock.WriteUInt16(mAddress.AddressStart, EndianType.Big);
valueByteBlock.WriteUInt16(length, EndianType.Big);
byte[] data = values.BoolArrayToByte();
valueByteBlock.WriteByte((byte)data.Length);
valueByteBlock.Write(values.BoolArrayToByte());
if (modbusType == ModbusTypeEnum.ModbusRtu)
{
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
}
}
/// <summary>
/// 获取16写入字报文
/// </summary>
internal static void GetWriteModbusCommand(ref ValueByteBlock valueByteBlock, ModbusAddress mAddress, byte[] values, ushort length, ModbusTypeEnum modbusType, ushort sign, ushort protocolId)
{
if (modbusType == ModbusTypeEnum.ModbusTcp)
{
valueByteBlock.WriteUInt16(sign, EndianType.Big);
valueByteBlock.WriteUInt16(protocolId, EndianType.Big);
valueByteBlock.WriteUInt16(6, EndianType.Big);
}
valueByteBlock.WriteByte(mAddress.Station);
valueByteBlock.WriteByte(16);
valueByteBlock.WriteUInt16(mAddress.AddressStart, EndianType.Big);
valueByteBlock.WriteUInt16(length, EndianType.Big);
valueByteBlock.WriteByte((byte)values.Length);
valueByteBlock.Write(values);
if (modbusType == ModbusTypeEnum.ModbusRtu)
{
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
}
}
/// <summary>
/// 获取6写入字报文
/// </summary>
internal static void GetWriteOneModbusCommand(ref ValueByteBlock valueByteBlock, ModbusAddress mAddress, byte[] values, ModbusTypeEnum modbusType, ushort sign, ushort protocolId)
{
if (modbusType == ModbusTypeEnum.ModbusTcp)
{
valueByteBlock.WriteUInt16(sign, EndianType.Big);
valueByteBlock.WriteUInt16(protocolId, EndianType.Big);
valueByteBlock.WriteUInt16(6, EndianType.Big);
}
valueByteBlock.WriteByte(mAddress.Station);
valueByteBlock.WriteByte(6);
valueByteBlock.WriteUInt16(mAddress.AddressStart, EndianType.Big);
valueByteBlock.Write(values);
if (modbusType == ModbusTypeEnum.ModbusRtu)
{
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
}
}
#endregion
}

View File

@@ -11,22 +11,36 @@
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// ModbusServerMessage
/// <inheritdoc/>
/// </summary>
internal interface IModbusServerMessage : IResultMessage
public class ModbusRequest
{
/// <summary>
/// 读写长度
/// </summary>
int Length { get; set; }
#region Request
/// <summary>
/// 当前关联的地址
/// 功能码
/// </summary>
ModbusAddress ModbusAddress { get; set; }
public byte FunctionCode { get; set; }
/// <summary>
/// 当前关联的字节数组
/// 站号
/// </summary>
ReadOnlyMemory<byte> Bytes { get; set; }
public byte Station { get; set; }
/// <summary>
/// 起始位置
/// </summary>
public ushort StartAddress { get; set; }
/// <summary>
/// 读取字节数组长度
/// </summary>
public ushort Length { get; set; } = 1;
/// <summary>
/// 数据
/// </summary>
public ReadOnlyMemory<byte> Data { get; set; }
#endregion Request
}

View File

@@ -8,18 +8,15 @@
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation;
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
public struct SendMessage : ISendMessage
/// </summary>
internal class ModbusResponse : ModbusRequest
{
public SendMessage(ReadOnlyMemory<byte> bytes)
{
SendBytes = bytes;
}
public ReadOnlyMemory<byte> SendBytes { get; }
/// <inheritdoc/>
public int Sign { get; set; }
/// <summary>
/// 错误码
/// </summary>
public byte? ErrorCode { get; set; }
}

View File

@@ -59,7 +59,7 @@ public class PackHelper
Dictionary<ModbusAddress, IVariable> map = group.ToDictionary((Func<IVariable, ModbusAddress>)(it =>
{
// 计算变量的字节长度
var lastLen = it.DataType.GetByteLength();
var lastLen = (int)it.DataType.GetByteLength();
if (lastLen <= 0)
{
// 如果长度小于等于0则根据数据类型进行处理
@@ -67,7 +67,7 @@ public class PackHelper
{
case DataTypeEnum.String:
// 字符串类型需特殊处理
lastLen = it.ThingsGatewayBitConverter.StringLength == null ? throw new(DefaultResource.Localizer["StringTypePackError"]) : it.ThingsGatewayBitConverter.StringLength.Value;
lastLen = (it.ThingsGatewayBitConverter.StringLength == null ? throw new(DefaultResource.Localizer["StringTypePackError"]) : it.ThingsGatewayBitConverter.StringLength.Value);
break;
default:
@@ -84,7 +84,7 @@ public class PackHelper
// 解析地址,并设置字节长度
var address = it.RegisterAddress;
var result = ModbusAddress.ParseFrom(address, station, dtuid, isCache: false);
result.ByteLength = lastLen;
result.Length = (ushort)lastLen;
return result;
}));
@@ -96,11 +96,11 @@ public class PackHelper
{
var modbusAddressSameSocketIdList = modbusAddressList.Where(t => t.SocketId == socketId);
// 获取所有功能码
var functionCodes = modbusAddressSameSocketIdList.Select(t => t.ReadFunction).Distinct();
var functionCodes = modbusAddressSameSocketIdList.Select(t => t.FunctionCode).Distinct();
foreach (var functionCode in functionCodes)
{
// 获取相同功能码的地址
var modbusAddressSameFunList = modbusAddressSameSocketIdList.Where(t => t.ReadFunction == functionCode);
var modbusAddressSameFunList = modbusAddressSameSocketIdList.Where(t => t.FunctionCode == functionCode);
// 获取相同站号的地址
var stationNumbers = modbusAddressSameFunList.Select(t => t.Station).Distinct();
foreach (var stationNumber in stationNumbers)
@@ -129,11 +129,11 @@ public class PackHelper
// 按地址结束位置排序
var orderByAddressEnd = addressList.Keys.OrderBy(it => it.AddressEnd);
// 按地址开始位置排序
var orderByAddressStart = addressList.Keys.OrderBy(it => it.AddressStart);
var orderByAddressStart = addressList.Keys.OrderBy(it => it.StartAddress);
// 获取地址最小值
var minAddress = orderByAddressStart.First().AddressStart;
var minAddress = orderByAddressStart.First().StartAddress;
// 获取地址最大值
var maxAddress = orderByAddressStart.Last().AddressStart;
var maxAddress = orderByAddressStart.Last().StartAddress;
// 循环直到处理完所有地址
while (maxAddress >= minAddress)
@@ -149,9 +149,9 @@ public class PackHelper
// 获取当前一组打包地址信息,使得地址不超过读取长度
var tempAddressEnd = orderByAddressEnd.Where(t => t.AddressEnd <= minAddress + readLength);
// 获取起始地址(当前组打包地址中起始地址最小的)
var startAddress = tempAddressEnd.OrderBy(it => it.AddressStart).First();
var startAddress = tempAddressEnd.OrderBy(it => it.StartAddress).First();
// 计算读取寄存器长度
var sourceLen = tempAddressEnd.Last().AddressEnd - startAddress.AddressStart;
var sourceLen = tempAddressEnd.Last().AddressEnd - startAddress.StartAddress;
// 创建一个新的变量源读取对象
T sourceRead = new()
@@ -169,15 +169,15 @@ public class PackHelper
// 如果功能码为 -1、3 或 4并且数据类型为布尔型则根据位偏移计算索引
if ((functionCode == -1 || functionCode == 3 || functionCode == 4) && readNode.DataType == DataTypeEnum.Boolean)
{
readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 16) + readNode.Index;
readNode.Index = ((item.StartAddress - startAddress.StartAddress) * 16) + readNode.Index;
}
else
{
// 其他情况根据功能码和地址计算索引
if (functionCode == 1 || functionCode == 2)
readNode.Index = item.AddressStart - startAddress.AddressStart + readNode.Index;
readNode.Index = item.StartAddress - startAddress.StartAddress + readNode.Index;
else
readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 2) + readNode.Index;
readNode.Index = ((item.StartAddress - startAddress.StartAddress) * 2) + readNode.Index;
}
// 将读取节点添加到变量源读取对象中,并从地址列表中移除
@@ -190,7 +190,7 @@ public class PackHelper
// 更新最小地址值,如果还有地址未处理,则继续循环;否则跳出循环
if (orderByAddressEnd.Count() > 0)
minAddress = orderByAddressStart.First().AddressStart;
minAddress = orderByAddressStart.First().StartAddress;
else
break;
}

View File

@@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// Rtu适配器
/// </summary>
internal class ModbusRtuDataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<ModbusRtuMessage>
{
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusRtuMessage request, IByteBlock byteBlock)
{
var result = ModbusHelper.GetModbusRtuData(request.SendBytes.Span, byteBlock);
request.OperCode = result.OperCode;
request.ErrorMessage = result.ErrorMessage;
return result.Content;
}
}

View File

@@ -16,55 +16,124 @@ namespace ThingsGateway.Foundation.Modbus;
internal class ModbusRtuMessage : MessageBase, IResultMessage
{
/// <inheritdoc/>
public override int HeadBytesLength => 3;
public override int HeaderLength => 3;
public ReadOnlyMemory<byte> SendBytes { get; set; }
public ModbusAddress? Request { get; set; }
public override void SendInfo(ReadOnlyMemory<byte> sendBytes)
public ModbusResponse Response { get; set; } = new();
public override void SendInfo(ISendMessage sendMessage)
{
SendBytes = sendBytes;
Request = (sendMessage as ModbusRtuSend).ModbusAddress;
}
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[]? headBytes)
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
{
if (headBytes == null || headBytes.Length <= 0) return false;
//01 03 02 00 01 xx xx
//01 04 02 00 01 xx xx
//01 01 02 00 01 xx xx
//01 02 02 00 01 xx xx
//01 05 00 00 00 00 xx xx
//01 06 00 00 00 00 xx xx
//01 0f 00 00 00 01 xx xx
//01 10 00 00 00 01 xx xx
//modbusRtu 读取
if (headBytes[1] <= 0x04)
Response.Station = byteBlock.ReadByte();
bool error = false;
var code = byteBlock.ReadByte();
if ((code & 0x80) == 0)
{
int num = (headBytes[2]);
if (num > 0xff - 4) return false;
BodyLength = num + 2; //数据区+crc
Response.FunctionCode = code;
}
else
{
if (headBytes[1] <= 0x10)
{
//modbusRtu 写入
BodyLength = 3 + 2; //数据区+crc
}
else
{
BodyLength = 0 + 2; //数据区+crc
}
code = code.SetBit(7, false);
Response.FunctionCode = code;
error = true;
}
if (SendBytes.Length > 0)
if (error)
{
Response.ErrorCode = byteBlock.ReadByte();
this.OperCode = Response.ErrorCode;
this.ErrorMessage = ModbusHelper.GetDescriptionByErrorCode(Response.ErrorCode.Value);
BodyLength = 2;
return true;
}
else
{
return false;//不是主动请求的,直接放弃
//验证发送/返回站号与功能码
//站号验证
if (Request.Station != Response.Station)
{
this.OperCode = -1;
Response.ErrorCode = 1;
this.ErrorMessage = ModbusResource.Localizer["StationNotSame", Request.Station, Response.Station];
return true;
}
if (Response.FunctionCode > 4 ? Request.WriteFunctionCode != Response.FunctionCode : Request.FunctionCode != Response.FunctionCode)
{
this.OperCode = -1;
Response.ErrorCode = 1;
this.ErrorMessage = ModbusResource.Localizer["FunctionNotSame", Request.FunctionCode, Response.FunctionCode];
return true;
}
if (Response.FunctionCode == 5 || Response.FunctionCode == 6)
{
BodyLength = 5;
return true;
}
else if (Response.FunctionCode == 15 || Response.FunctionCode == 16)
{
BodyLength = 5;
return true;
}
else if (Response.FunctionCode <= 4)
{
Response.Length = byteBlock.ReadByte();
BodyLength = Response.Length + 2; //数据区+crc
return true;
}
}
return false;
}
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
if (Response.ErrorCode.HasValue)
{
return FilterResult.Success;
}
var pos = byteBlock.Position - HeaderLength;
var crcLen = 0;
if (Response.FunctionCode <= 4)
{
this.Content = byteBlock.ToArrayTake(BodyLength - 2);
Response.Data = this.Content;
crcLen = 3 + Response.Length;
}
else if (Response.FunctionCode == 5 || Response.FunctionCode == 6)
{
byteBlock.Position = HeaderLength - 1;
Response.StartAddress = byteBlock.ReadUInt16(EndianType.Big);
this.Content = byteBlock.ToArrayTake(BodyLength - 4);
Response.Data = this.Content;
crcLen = 6;
}
else if (Response.FunctionCode == 15 || Response.FunctionCode == 16)
{
byteBlock.Position = HeaderLength - 1;
Response.StartAddress = byteBlock.ReadUInt16(EndianType.Big);
Response.Length = byteBlock.ReadUInt16(EndianType.Big);
this.Content = Array.Empty<byte>();
crcLen = 6;
}
if (crcLen > 0)
{
var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen));
//Crc
var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2).ToArray();
if (crc.SequenceEqual(checkCrc))
{
return FilterResult.Success;
}
}
return FilterResult.GoOn;
}
}

View File

@@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuMessage>
{
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusRtuMessage request, IByteBlock byteBlock)
{
var result = ModbusHelper.GetModbusRtuData(request.SendBytes.Span, byteBlock);
request.OperCode = result.OperCode;
request.ErrorMessage = result.ErrorMessage;
return result.Content;
}
}

View File

@@ -0,0 +1,79 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using TouchSocket.Sockets;
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusRtuSend : ISendMessage
{
public int Sign { get; set; }
public int MaxLength => 300;
internal ModbusAddress ModbusAddress { get; }
private bool Read;
public ModbusRtuSend(ModbusAddress modbusAddress, ushort sign, bool read)
{
Sign = sign;
ModbusAddress = modbusAddress;
Read = read;
}
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
{
if (!Read)
{
if (ModbusAddress.WriteFunctionCode == null)
{
ModbusAddress.WriteFunctionCode = (byte)(ModbusAddress.FunctionCode == 1 ? 5 : 6);
}
if (ModbusAddress.Data.Length > 2 && ModbusAddress.WriteFunctionCode < 15)
{
ModbusAddress.WriteFunctionCode = (byte)(ModbusAddress.FunctionCode == 1 ? 15 : 16);
}
}
var f = Read ? ModbusAddress.FunctionCode : ModbusAddress.WriteFunctionCode;
if (f <= 4)
{
byteBlock.WriteByte(ModbusAddress.Station);
byteBlock.WriteByte((byte)f);
byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big);
byteBlock.WriteUInt16(ModbusAddress.Length, EndianType.Big);
}
else if (f == 5 || f == 6)
{
byteBlock.WriteByte(ModbusAddress.Station);
byteBlock.WriteByte((byte)f);
byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big);
byteBlock.Write(ModbusAddress.Data.Span);
}
else if (f == 15 || f == 16)
{
byteBlock.WriteByte(ModbusAddress.Station);
byteBlock.WriteByte((byte)f);
byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big);
byteBlock.WriteUInt16((ushort)Math.Ceiling(ModbusAddress.Data.Length / 2.0), EndianType.Big);
byteBlock.WriteByte((byte)ModbusAddress.Data.Length);
byteBlock.Write(ModbusAddress.Data.Span);
}
else
{
throw new System.InvalidOperationException(ModbusResource.Localizer["ModbusError1"]);
}
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span));
}
}

View File

@@ -1,39 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// ModbusTcpDataHandleAdapter
/// </summary>
internal class ModbusTcpDataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<ModbusTcpMessage>
{
public ModbusTcpDataHandleAdapter()
{
this.MaxPackageSize = 1024 * 1024 * 1024;
}
public override bool IsSingleThread { get; } = false;
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusTcpMessage request, IByteBlock byteBlock)
{
var pos = byteBlock.Position;
byteBlock.Position = byteBlock.Position + 6;
var result = ModbusHelper.GetModbusData(null, byteBlock);
request.OperCode = result.OperCode;
request.ErrorMessage = result.ErrorMessage;
byteBlock.Position = pos;
request.Sign = byteBlock.ReadUInt16(EndianType.Big);
return result.Content;
}
}

View File

@@ -16,17 +16,79 @@ namespace ThingsGateway.Foundation.Modbus;
internal class ModbusTcpMessage : MessageBase, IResultMessage
{
/// <inheritdoc/>
public override int HeadBytesLength => 6;
public override int HeaderLength => 9;
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[]? headBytes)
public ModbusResponse Response { get; set; } = new();
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
{
if (headBytes == null || headBytes.Length <= 0) return false;
this.Sign = byteBlock.ReadUInt16(EndianType.Big);
byteBlock.Position += 2;
this.BodyLength = byteBlock.ReadUInt16(EndianType.Big) - 3;
Response.Station = byteBlock.ReadByte();
bool error = false;
var code = byteBlock.ReadByte();
if ((code & 0x80) == 0)
{
Response.FunctionCode = code;
}
else
{
code = code.SetBit(7, false);
Response.FunctionCode = code;
error = true;
}
int num = (headBytes[4] * 256) + headBytes[5];
if (num > 0xff + 3) return false;
BodyLength = num;
if (error)
{
Response.ErrorCode = byteBlock.ReadByte();
this.OperCode = Response.ErrorCode;
this.ErrorMessage = ModbusHelper.GetDescriptionByErrorCode(Response.ErrorCode.Value);
return true;
}
else
{
if (Response.FunctionCode <= 4)
{
Response.Length = byteBlock.ReadByte();
}
else
{
Response.StartAddress = byteBlock.ReadUInt16();
}
return true;
}
}
return true;
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
if (Response.ErrorCode.HasValue)
{
return FilterResult.Success;
}
if (Response.FunctionCode <= 4)
{
this.Content = byteBlock.ToArrayTake(BodyLength);
Response.Data = this.Content;
return FilterResult.Success;
}
else if (Response.FunctionCode == 5 || Response.FunctionCode == 6)
{
byteBlock.Position = HeaderLength - 1;
Response.StartAddress = byteBlock.ReadUInt16();
this.Content = byteBlock.ToArrayTake(BodyLength - 1);
Response.Data = this.Content;
return FilterResult.Success;
}
else if (Response.FunctionCode == 15 || Response.FunctionCode == 16)
{
byteBlock.Position = HeaderLength - 1;
Response.StartAddress = byteBlock.ReadUInt16(EndianType.Big);
Response.Length = byteBlock.ReadUInt16(EndianType.Big);
this.Content = Array.Empty<byte>();
return FilterResult.Success;
}
return FilterResult.GoOn;
}
}

View File

@@ -0,0 +1,92 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusTcpSend : ISendMessage
{
/// <summary>
/// 事务处理标识符。即序号
/// </summary>
public ushort TransactionId { get; set; }
/// <summary>
/// 协议标识符
/// </summary>
public ushort ProtocolId { get; set; }
public int Sign { get; set; }
public int MaxLength => 300;
private ModbusAddress ModbusAddress;
private bool Read;
public ModbusTcpSend(ModbusAddress modbusAddress, ushort transactionId, bool read, ushort protocolId = 0)
{
TransactionId = transactionId;
Sign = transactionId;
ProtocolId = protocolId;
ModbusAddress = modbusAddress;
Read = read;
}
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
{
if (!Read)
{
if (ModbusAddress.WriteFunctionCode == null)
{
ModbusAddress.WriteFunctionCode = (byte)(ModbusAddress.FunctionCode == 1 ? 5 : 6);
}
if (ModbusAddress.Data.Length > 2 && ModbusAddress.WriteFunctionCode < 15)
{
ModbusAddress.WriteFunctionCode = (byte)(ModbusAddress.FunctionCode == 1 ? 15 : 16);
}
}
var f = Read ? ModbusAddress.FunctionCode : ModbusAddress.WriteFunctionCode;
byteBlock.WriteUInt16(TransactionId, EndianType.Big);
byteBlock.WriteUInt16(ProtocolId, EndianType.Big);
if (f <= 4)
{
byteBlock.WriteUInt16((ushort)6, EndianType.Big);
byteBlock.WriteByte(ModbusAddress.Station);
byteBlock.WriteByte((byte)f);
byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big);
byteBlock.WriteUInt16(ModbusAddress.Length, EndianType.Big);
}
else if (f == 5 || f == 6)
{
byteBlock.WriteUInt16((ushort)6, EndianType.Big);
byteBlock.WriteByte(ModbusAddress.Station);
byteBlock.WriteByte((byte)f);
byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big);
byteBlock.Write(ModbusAddress.Data.Span);
}
else if (f == 15 || f == 16)
{
byteBlock.WriteUInt16((ushort)(ModbusAddress.Data.Length + 7), EndianType.Big);
byteBlock.WriteByte(ModbusAddress.Station);
byteBlock.WriteByte((byte)f);
byteBlock.WriteUInt16(ModbusAddress.StartAddress, EndianType.Big);
byteBlock.WriteUInt16((ushort)Math.Ceiling(ModbusAddress.Data.Length / 2.0), EndianType.Big);
byteBlock.WriteByte((byte)ModbusAddress.Data.Length);
byteBlock.Write(ModbusAddress.Data.Span);
}
else
{
throw new System.InvalidOperationException(ModbusResource.Localizer["ModbusError1"]);
}
}
}

View File

@@ -1,33 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusTcpMessage>
{
public override bool IsSingleThread { get; } = false;
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusTcpMessage request, IByteBlock byteBlock)
{
var pos = byteBlock.Position;
byteBlock.Position = byteBlock.Position + 6;
var result = ModbusHelper.GetModbusData(null, byteBlock);
request.OperCode = result.OperCode;
request.ErrorMessage = result.ErrorMessage;
byteBlock.Position = pos;
request.Sign = byteBlock.ReadUInt16(EndianType.Big);
return result.Content;
}
}

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Modbus;
/// <inheritdoc/>
@@ -26,6 +24,34 @@ public partial class ModbusMaster : ProtocolBase, IDtu
client.WaitHandlePool.MaxSign = ushort.MaxValue;
}
/// <summary>
/// 客户端连接滑动过期时间(TCP服务通道时)
/// </summary>
public int CheckClearTime { get; set; } = 120;
/// <summary>
/// 默认Dtu注册包,utf-8字符串
/// </summary>
public string DtuId { get; set; } = "DtuId";
/// <summary>
/// 心跳检测(大写16进制字符串)
/// </summary>
public string HeartbeatHexString { get; set; } = "FFFF8080";
/// <inheritdoc/>
public override bool IsSingleThread
{
get
{
switch (ModbusType)
{
case ModbusTypeEnum.ModbusTcp: return false;
default: return true;
}
}
}
/// <summary>
/// Modbus类型
/// </summary>
@@ -46,45 +72,11 @@ public partial class ModbusMaster : ProtocolBase, IDtu
}
}
/// <inheritdoc/>
public override bool IsSingleThread
{
get
{
switch (ModbusType)
{
case ModbusTypeEnum.ModbusTcp: return false;
default: return true;
}
}
}
/// <summary>
/// 站号
/// </summary>
public byte Station { get; set; } = 1;
/// <summary>
/// 默认Dtu注册包,utf-8字符串
/// </summary>
public string DtuId { get; set; } = "TEST";
/// <summary>
/// 客户端连接滑动过期时间(TCP服务通道时)
/// </summary>
public int CheckClearTime { get; set; } = 120;
/// <summary>
/// 心跳检测(大写16进制字符串)
/// </summary>
public string HeartbeatHexString { get; set; } = "FFFF8080";
/// <inheritdoc/>
public override string GetAddressDescription()
{
return $"{base.GetAddressDescription()}{Environment.NewLine}{ModbusHelper.GetAddressDescription()}";
}
/// <inheritdoc/>
public override Action<IPluginManager> ConfigurePlugins()
{
@@ -96,6 +88,12 @@ public partial class ModbusMaster : ProtocolBase, IDtu
return base.ConfigurePlugins();
}
/// <inheritdoc/>
public override string GetAddressDescription()
{
return $"{base.GetAddressDescription()}{Environment.NewLine}{ModbusHelper.GetAddressDescription()}";
}
/// <inheritdoc/>
public override DataHandlingAdapter GetDataAdapter()
{
@@ -107,13 +105,13 @@ public partial class ModbusMaster : ProtocolBase, IDtu
case ChannelTypeEnum.TcpClient:
case ChannelTypeEnum.TcpService:
case ChannelTypeEnum.SerialPort:
return new ModbusTcpDataHandleAdapter()
return new ProtocolSingleStreamDataHandleAdapter<ModbusTcpMessage>()
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
case ChannelTypeEnum.UdpSession:
return new ModbusUdpDataHandleAdapter()
return new ProtocolUdpDataHandleAdapter<ModbusTcpMessage>()
{
};
}
@@ -125,19 +123,19 @@ public partial class ModbusMaster : ProtocolBase, IDtu
case ChannelTypeEnum.TcpClient:
case ChannelTypeEnum.TcpService:
case ChannelTypeEnum.SerialPort:
return new ModbusRtuDataHandleAdapter()
return new ProtocolSingleStreamDataHandleAdapter<ModbusRtuMessage>()
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
case ChannelTypeEnum.UdpSession:
return new ModbusRtuOverUdpDataHandleAdapter()
return new ProtocolUdpDataHandleAdapter<ModbusRtuMessage>()
{
};
}
break;
}
return new ModbusTcpDataHandleAdapter()
return new ProtocolSingleStreamDataHandleAdapter<ModbusTcpMessage>()
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
@@ -149,25 +147,16 @@ public partial class ModbusMaster : ProtocolBase, IDtu
return PackHelper.LoadSourceRead<T>(this, deviceVariables, maxPack, defaultIntervalTime, Station, DtuId);
}
/// <inheritdoc/>
public override async ValueTask<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
public async ValueTask<OperResult<byte[]>> ModbusRequestAsync(ModbusAddress mAddress, bool read, CancellationToken cancellationToken = default)
{
try
{
var mAddress = ModbusAddress.ParseFrom(address, Station, DtuId);
var channelResult = GetChannel(mAddress.SocketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var waitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var sign);
ModbusHelper.GetReadModbusCommand(ref valueByteBlock, mAddress, (ushort)length, ModbusType, (ushort)sign, 0);
return await this.SendThenReturnAsync(new SendMessage(valueByteBlock.Memory) { Sign = sign }, waitData, cancellationToken, channelResult.Content).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
var waitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var sign);
return await this.SendThenReturnAsync(
GetSendMessage(mAddress, (ushort)sign, read),
channelResult.Content, waitData, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -175,31 +164,29 @@ public partial class ModbusMaster : ProtocolBase, IDtu
}
}
/// <inheritdoc/>
public override ValueTask<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
var mAddress = ModbusAddress.ParseFrom(address, Station, DtuId);
mAddress.Length = (ushort)length;
return ModbusRequestAsync(mAddress, true, cancellationToken);
}
catch (Exception ex)
{
return EasyValueTask.FromResult(new OperResult<byte[]>(ex));
}
}
/// <inheritdoc/>
public override async ValueTask<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
var mAddress = ModbusAddress.ParseFrom(address, Station, DtuId);
value = value.ArrayExpandToLengthEven();
var channelResult = GetChannel(mAddress.SocketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
{
var waitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var sign);
if (value.Length > 2 || mAddress.WriteFunction == 16)
ModbusHelper.GetWriteModbusCommand(ref valueByteBlock, mAddress, value, (ushort)(value.Length / RegisterByteLength), ModbusType, (ushort)sign, 0);
else
ModbusHelper.GetWriteOneModbusCommand(ref valueByteBlock, mAddress, value, ModbusType, (ushort)sign, 0);
return await this.SendThenReturnAsync(new SendMessage(valueByteBlock.Memory) { Sign = sign }, waitData, cancellationToken, channelResult.Content).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
mAddress.Data = value;
return await ModbusRequestAsync(mAddress, false, cancellationToken);
}
catch (Exception ex)
{
@@ -213,53 +200,32 @@ public partial class ModbusMaster : ProtocolBase, IDtu
try
{
var mAddress = ModbusAddress.ParseFrom(address, Station, DtuId);
var channelResult = GetChannel(mAddress.SocketId);
if (!channelResult.IsSuccess) return new OperResult<byte[]>(channelResult);
ValueByteBlock valueByteBlock = new ValueByteBlock(1024);
try
if (value.Length > 1 || mAddress.WriteFunctionCode == 15)
{
var waitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var sign);
if (value.Length > 1 || mAddress.WriteFunction == 15)
mAddress.Data = value.BoolArrayToByte();
return await ModbusRequestAsync(mAddress, false, cancellationToken);
}
else if (mAddress.BitIndex == null)
{
mAddress.Data = value[0] ? new byte[2] { 255, 0 } : [0, 0];
return await ModbusRequestAsync(mAddress, false, cancellationToken);
}
else
{
if (mAddress.BitIndex < 16)
{
ModbusHelper.GetWriteBoolModbusCommand(ref valueByteBlock, mAddress, value, (ushort)value.Length, ModbusType, (ushort)sign, 0);
var readData = await ModbusRequestAsync(mAddress, true, cancellationToken);
if (!readData.IsSuccess) return readData;
var writeData = ThingsGatewayBitConverter.ToUInt16(readData.Content, 0);
ushort mask = (ushort)(1 << mAddress.BitIndex);
ushort result = (ushort)(value[0] ? (writeData | mask) : (writeData & ~mask));
mAddress.Data = ThingsGatewayBitConverter.GetBytes(result);
return await ModbusRequestAsync(mAddress, false, cancellationToken);
}
else
{
if (mAddress.BitIndex == null)
{
ModbusHelper.GetWriteBoolModbusCommand(ref valueByteBlock, mAddress, value[0], ModbusType, (ushort)sign, 0);
}
else if (mAddress.BitIndex < 16)
{
ValueByteBlock readValueByteBlock = new ValueByteBlock(1024);
try
{
var readWaitData = channelResult.Content.WaitHandlePool.GetWaitDataAsync(out var readSign);
//比如40001.1
ModbusHelper.GetReadModbusCommand(ref readValueByteBlock, mAddress, 1, ModbusType, (ushort)readSign, 0);
var readData = await this.SendThenReturnAsync(new SendMessage(readValueByteBlock.Memory) { Sign = readSign }, readWaitData, cancellationToken, channelResult.Content).ConfigureAwait(false);
if (!readData.IsSuccess) return readData;
var writeData = ThingsGatewayBitConverter.ToUInt16(readData.Content, 0);
ushort mask = (ushort)(1 << mAddress.BitIndex);
ushort result = (ushort)(value[0] ? (writeData | mask) : (writeData & ~mask));
ModbusHelper.GetWriteOneModbusCommand(ref valueByteBlock, mAddress, ThingsGatewayBitConverter.GetBytes(result), ModbusType, (ushort)sign, 0);
}
finally
{
readValueByteBlock.SafeDispose();
}
}
else
{
return new OperResult(ModbusResource.Localizer["ValueOverlimit", nameof(mAddress.BitIndex), 16]);
}
return new OperResult(ModbusResource.Localizer["ValueOverlimit", nameof(mAddress.BitIndex), 16]);
}
return await this.SendThenReturnAsync(new SendMessage(valueByteBlock.Memory) { Sign = sign }, waitData, cancellationToken, channelResult.Content).ConfigureAwait(false);
}
finally
{
valueByteBlock.SafeDispose();
}
}
catch (Exception ex)
@@ -267,4 +233,16 @@ public partial class ModbusMaster : ProtocolBase, IDtu
return new OperResult(ex);
}
}
private ISendMessage GetSendMessage(ModbusAddress modbusAddress, ushort sign, bool read)
{
if (ModbusType == ModbusTypeEnum.ModbusRtu)
{
return new ModbusRtuSend(modbusAddress, sign, read);
}
else
{
return new ModbusTcpSend(modbusAddress, sign, read);
}
}
}

View File

@@ -1,37 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusRtuOverUdpServerDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuServerMessage>
{
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusRtuServerMessage request, IByteBlock byteBlock)
{
var pos = byteBlock.Position;
var result = ModbusHelper.CheckCrc(byteBlock);
request.OperCode = result.OperCode;
request.ErrorMessage = result.ErrorMessage;
if (result.IsSuccess)
{
byteBlock.Position = pos;
var bytes = ModbusHelper.ModbusServerAnalysisAddressValue(request, byteBlock);
request.Bytes = byteBlock.AsSegment(pos, request.HeadBytesLength + request.BodyLength);
return new AdapterResult() { FilterResult = FilterResult.Success, Content = bytes };
}
else
{
return new AdapterResult() { FilterResult = FilterResult.Success };
}
}
}

View File

@@ -1,35 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <inheritdoc/>
internal class ModbusRtuServerDataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<ModbusRtuServerMessage>
{
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusRtuServerMessage request, IByteBlock byteBlock)
{
var pos = byteBlock.Position;
var result = ModbusHelper.CheckCrc(byteBlock);
request.OperCode = result.OperCode;
request.ErrorMessage = result.ErrorMessage;
if (result.IsSuccess)
{
byteBlock.Position = pos;
var bytes = ModbusHelper.ModbusServerAnalysisAddressValue(request, byteBlock);
request.Bytes = byteBlock.AsSegment(pos, request.HeadBytesLength + request.BodyLength);
return new AdapterResult() { FilterResult = FilterResult.Success, Content = bytes };
}
else
{
return new AdapterResult() { FilterResult = FilterResult.Success };
}
}
}

View File

@@ -1,60 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusRtuServerMessage : MessageBase, IResultMessage, IModbusServerMessage
{
/// <inheritdoc/>
public ModbusAddress ModbusAddress { get; set; }
/// <summary>
/// 当前关联的字节数组
/// </summary>
public ReadOnlyMemory<byte> Bytes { get; set; }
/// <inheritdoc/>
public int Length { get; set; }
/// <inheritdoc/>
public override int HeadBytesLength => 7; //主站发送的报文最低也有8个字节
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[]? headBytes)
{
if (headBytes == null || headBytes.Length <= 0) return false;
//01 03 00 00 00 01 xx xx
//01 04 00 00 00 01 xx xx
//01 01 00 00 00 01 xx xx
//01 02 00 00 00 01 xx xx
//01 05 00 00 00 00 xx xx
//01 06 00 00 00 00 xx xx
//01 0f 00 00 00 01 01 00 xx xx
//01 10 00 00 00 01 02 00 00 xx xx
//modbusRtu 读取/单写
if (headBytes[1] <= 0x06)
{
BodyLength = 1; //crc l
return true;
}
else if (headBytes[1] <= 0x10)
{
//modbusRtu 多写
BodyLength = headBytes[6] + 2; //数据区+crc
return true;
}
return false;
}
}

View File

@@ -0,0 +1,101 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusRtuSlaveMessage : MessageBase, IResultMessage
{
public ModbusRequest Request { get; set; } = new();
/// <summary>
/// 当前关联的字节数组
/// </summary>
public ReadOnlyMemory<byte> Bytes { get; set; }
/// <inheritdoc/>
public override int HeaderLength => 7; //主站发送的报文最低也有8个字节
/// <inheritdoc/>
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
{
Request.Station = byteBlock.ReadByte();
Request.FunctionCode = byteBlock.ReadByte();
Request.StartAddress = byteBlock.ReadUInt16(EndianType.Big);
if (Request.FunctionCode == 3 || Request.FunctionCode == 4)
{
Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2);
BodyLength = 1;
return true;
}
else if (Request.FunctionCode == 1 || Request.FunctionCode == 2)
{
Request.Length = (ushort)Math.Ceiling(byteBlock.ReadUInt16(EndianType.Big) / 8.0);
BodyLength = 1;
return true;
}
else if (Request.FunctionCode == 5)
{
Request.Data = byteBlock.AsSegmentTake(2);
BodyLength = 1;
return true;
}
else if (Request.FunctionCode == 6)
{
Request.Data = byteBlock.AsSegmentTake(2);
BodyLength = 1;
return true;
}
else if (Request.FunctionCode == 15)
{
Request.Length = (ushort)Math.Ceiling(byteBlock.ReadUInt16(EndianType.Big) / 8.0);
BodyLength = Request.Length + 2;
return true;
}
else if (Request.FunctionCode == 16)
{
Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2);
BodyLength = Request.Length + 2;
return true;
}
return false;
}
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
var pos = byteBlock.Position - HeaderLength;
var crcLen = 0;
this.Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength);
if (Request.FunctionCode == 15)
{
Request.Data = byteBlock.AsSegmentTake(Request.Length);
}
else if (Request.FunctionCode == 16)
{
Request.Data = byteBlock.AsSegmentTake(Request.Length);
}
crcLen = HeaderLength + BodyLength - 2;
var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen));
//Crc
var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2).ToArray();
if (crc.SequenceEqual(checkCrc))
{
return FilterResult.Success;
}
return FilterResult.GoOn;
}
}

View File

@@ -1,30 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <inheritdoc/>
internal class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<ModbusTcpServerMessage>
{
public ModbusTcpServerDataHandleAdapter()
{
this.MaxPackageSize = 1024 * 1024 * 1024;
}
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusTcpServerMessage request, IByteBlock byteBlock)
{
var pos = byteBlock.Position;
byteBlock.Position = byteBlock.Position + 6;
var bytes = ModbusHelper.ModbusServerAnalysisAddressValue(request, byteBlock);
request.Bytes = byteBlock.AsSegment(pos, request.HeadBytesLength + request.BodyLength);
return new AdapterResult() { FilterResult = FilterResult.Success, Content = bytes };
}
}

View File

@@ -1,47 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusTcpServerMessage : MessageBase, IResultMessage, IModbusServerMessage
{
/// <summary>
/// 当前关联的地址
/// </summary>
public ModbusAddress ModbusAddress { get; set; }
/// <summary>
/// 当前关联的字节数组
/// </summary>
public ReadOnlyMemory<byte> Bytes { get; set; }
/// <summary>
/// 当前读写的数据长度
/// </summary>
public int Length { get; set; }
/// <inheritdoc/>
public override int HeadBytesLength => 6;
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[]? headBytes)
{
if (headBytes == null || headBytes.Length <= 0) return false;
int num = (headBytes[4] * 256) + headBytes[5];
if (num > 0xff + 3) return false;
BodyLength = num;
return true;
}
}

View File

@@ -0,0 +1,78 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusTcpSlaveMessage : MessageBase, IResultMessage
{
public ModbusRequest Request { get; set; } = new();
/// <summary>
/// 当前关联的字节数组
/// </summary>
public ReadOnlyMemory<byte> Bytes { get; set; }
/// <inheritdoc/>
public override int HeaderLength => 12;
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
{
this.Sign = byteBlock.ReadUInt16(EndianType.Big);
byteBlock.Position += 2;
this.BodyLength = byteBlock.ReadUInt16(EndianType.Big) - 6;
Request.Station = byteBlock.ReadByte();
Request.FunctionCode = byteBlock.ReadByte();
Request.StartAddress = byteBlock.ReadUInt16(EndianType.Big);
if (Request.FunctionCode == 3 || Request.FunctionCode == 4)
{
Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2);
return true;
}
else if (Request.FunctionCode == 1 || Request.FunctionCode == 2)
{
Request.Length = (ushort)Math.Ceiling(byteBlock.ReadUInt16(EndianType.Big) / 8.0);
return true;
}
else if (Request.FunctionCode == 5 || Request.FunctionCode == 6)
{
Request.Data = byteBlock.AsSegmentTake(2);
return true;
}
else if (Request.FunctionCode == 15)
{
Request.Length = (ushort)Math.Ceiling(byteBlock.ReadUInt16(EndianType.Big) / 8.0);
return true;
}
else if (Request.FunctionCode == 16)
{
Request.Length = (ushort)(byteBlock.ReadUInt16(EndianType.Big) * 2);
return true;
}
return false;
}
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
var pos = byteBlock.Position - HeaderLength;
this.Bytes = byteBlock.AsSegment(pos, HeaderLength + BodyLength);
if (Request.FunctionCode == 15 || Request.FunctionCode == 16)
{
byteBlock.Position += 1;
Request.Data = byteBlock.AsSegmentTake(Request.Length);
}
return FilterResult.Success;
}
}

View File

@@ -1,32 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class ModbusUdpServerDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusTcpServerMessage>
{
public ModbusUdpServerDataHandleAdapter()
{
this.MaxPackageSize = 1024 * 1024 * 1024;
}
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(ModbusTcpServerMessage request, IByteBlock byteBlock)
{
var pos = byteBlock.Position;
byteBlock.Position = byteBlock.Position + 6;
var bytes = ModbusHelper.ModbusServerAnalysisAddressValue(request, byteBlock);
request.Bytes = byteBlock.AsSegment(pos, request.HeadBytesLength + request.BodyLength);
return new AdapterResult() { FilterResult = FilterResult.Success, Content = bytes };
}
}

View File

@@ -10,8 +10,6 @@
using System.Collections.Concurrent;
using ThingsGateway.Foundation.Extension.Generic;
using TouchSocket.Sockets;
namespace ThingsGateway.Foundation.Modbus;
@@ -19,7 +17,7 @@ namespace ThingsGateway.Foundation.Modbus;
/// <summary>
/// ChannelEventHandler
/// </summary>
public delegate ValueTask<OperResult> ModbusServerWriteEventHandler(ModbusAddress modbusAddress, byte[] writeValue, IThingsGatewayBitConverter bitConverter, IClientChannel channel);
public delegate ValueTask<OperResult> ModbusServerWriteEventHandler(ModbusAddress modbusAddress, IThingsGatewayBitConverter bitConverter, IClientChannel channel);
/// <inheritdoc/>
public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
@@ -71,7 +69,7 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
/// </summary>
public int MaxClientCount { get; set; } = 10000;
public string DtuId { get; set; } = "TEST";
public string DtuId { get; set; } = "DtuId";
public int HeartbeatTime { get; set; } = 5;
public string HeartbeatHexString { get; set; } = "FFFF8080";
@@ -134,13 +132,13 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
case ChannelTypeEnum.TcpClient:
case ChannelTypeEnum.TcpService:
case ChannelTypeEnum.SerialPort:
return new ModbusTcpServerDataHandleAdapter()
return new ProtocolSingleStreamDataHandleAdapter<ModbusTcpSlaveMessage>()
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
case ChannelTypeEnum.UdpSession:
return new ModbusUdpServerDataHandleAdapter()
return new ProtocolUdpDataHandleAdapter<ModbusTcpSlaveMessage>()
{
};
}
@@ -152,19 +150,19 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
case ChannelTypeEnum.TcpClient:
case ChannelTypeEnum.TcpService:
case ChannelTypeEnum.SerialPort:
return new ModbusRtuServerDataHandleAdapter()
return new ProtocolSingleStreamDataHandleAdapter<ModbusRtuSlaveMessage>()
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
case ChannelTypeEnum.UdpSession:
return new ModbusRtuOverUdpServerDataHandleAdapter()
return new ProtocolUdpDataHandleAdapter<ModbusRtuSlaveMessage>()
{
};
}
break;
}
return new ModbusTcpDataHandleAdapter()
return new ProtocolSingleStreamDataHandleAdapter<ModbusTcpSlaveMessage>()
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
@@ -177,7 +175,7 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
}
/// <inheritdoc/>
private void Init(ModbusAddress mAddress)
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]));
@@ -219,194 +217,197 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
_ = Task.Run(async () =>
{
var requestInfo = e.RequestInfo;
bool modbusRtu = false;
ModbusRequest modbusRequest = default;
ReadOnlyMemory<byte> Bytes = default;
//接收外部报文
if (requestInfo is IModbusServerMessage modbusServerMessage)
if (requestInfo is ModbusRtuSlaveMessage modbusRtuSlaveMessage)
{
if (modbusServerMessage.ModbusAddress == null)
if (!modbusRtuSlaveMessage.IsSuccess)
{
return;//无法解析直接返回
return;
}
if (!modbusServerMessage.IsSuccess)
modbusRequest = modbusRtuSlaveMessage.Request;
Bytes = modbusRtuSlaveMessage.Bytes;
modbusRtu = true;
}
else if (requestInfo is ModbusTcpSlaveMessage modbusTcpSlaveMessage)
{
if (!modbusTcpSlaveMessage.IsSuccess)
{
return;//无法解析直接返回
return;
}
modbusRequest = modbusTcpSlaveMessage.Request;
Bytes = modbusTcpSlaveMessage.Bytes;
modbusRtu = false;
}
if (modbusServerMessage.ModbusAddress.WriteFunction == null)//读取
if (modbusRequest.FunctionCode <= 4)
{
var data = this.ModbusRequest(modbusRequest, true);
if (data.IsSuccess)
{
var data = this.Read(modbusServerMessage.ModbusAddress.ToString(), modbusServerMessage.Length);
if (data.IsSuccess)
ValueByteBlock valueByteBlock = new(1024);
try
{
ValueByteBlock valueByteBlock = new(1024);
try
if (modbusRtu)
{
//rtu返回头
if (ModbusType == ModbusTypeEnum.ModbusRtu)
valueByteBlock.Write(Bytes.Slice(0, 2).Span);
if (modbusRequest.FunctionCode == 1 || modbusRequest.FunctionCode == 2)
{
valueByteBlock.Write(modbusServerMessage.Bytes.Slice(0, 2).Span);
if (modbusServerMessage.ModbusAddress.ReadFunction == 1 || modbusServerMessage.ModbusAddress.ReadFunction == 2)
{
valueByteBlock.WriteByte((byte)(Math.Ceiling(modbusServerMessage.Length / 8.0)));
valueByteBlock.Write(data.Content.ToArray().Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0)));
}
else
{
valueByteBlock.WriteByte((byte)data.Content.Length);
valueByteBlock.Write(data.Content.Span);
}
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
await ReturnData(client, e, valueByteBlock.Memory);
valueByteBlock.WriteByte((byte)modbusRequest.Length);
valueByteBlock.Write(data.Content.Span);
}
else
{
valueByteBlock.Write(modbusServerMessage.Bytes.Slice(0, 8).Span);
if (modbusServerMessage.ModbusAddress.ReadFunction == 1 || modbusServerMessage.ModbusAddress.ReadFunction == 2)
{
valueByteBlock.WriteByte((byte)(Math.Ceiling(modbusServerMessage.Length / 8.0)));
valueByteBlock.Write(data.Content.ToArray().Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0)));
}
else
{
valueByteBlock.WriteByte((byte)data.Content.Length);
valueByteBlock.Write(data.Content.Span);
}
valueByteBlock[5] = (byte)(valueByteBlock.Length - 6);
await ReturnData(client, e, valueByteBlock.Memory);
valueByteBlock.WriteByte((byte)data.Content.Length);
valueByteBlock.Write(data.Content.Span);
}
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory.Span));
await ReturnData(client, valueByteBlock.Memory, e);
}
finally
else
{
valueByteBlock.SafeDispose();
valueByteBlock.Write(Bytes.Slice(0, 8).Span);
if (modbusRequest.FunctionCode == 1 || modbusRequest.FunctionCode == 2)
{
valueByteBlock.WriteByte((byte)modbusRequest.Length);
valueByteBlock.Write(data.Content.Span);
}
else
{
valueByteBlock.WriteByte((byte)data.Content.Length);
valueByteBlock.Write(data.Content.Span);
}
valueByteBlock[5] = (byte)(valueByteBlock.Length - 6);
await ReturnData(client, valueByteBlock.Memory, e);
}
}
else
finally
{
await WriteError(this.ModbusType, client, modbusServerMessage, e);//返回错误码
valueByteBlock.SafeDispose();
}
}
else//写入
else
{
var coreData = modbusServerMessage.Content;
if (modbusServerMessage.ModbusAddress.ReadFunction == 1 || modbusServerMessage.ModbusAddress.ReadFunction == 2)
await WriteError(modbusRtu, client, Bytes, e);//返回错误码
}
}
else//写入
{
if (modbusRequest.FunctionCode == 5)
{
//写入继电器
if (this.WriteData != null)
{
//写入继电器
if (this.WriteData != null)
var modbusAddress = new ModbusAddress(modbusRequest) { WriteFunctionCode = modbusRequest.FunctionCode, FunctionCode = 1 };
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
if ((await this.WriteData(modbusAddress, this.ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
{
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
if ((await this.WriteData(modbusServerMessage.ModbusAddress, modbusServerMessage.Content, this.ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
await WriteSuccess(modbusRtu, client, Bytes, e);
if (this.IsWriteMemory)
{
await WriteSuccess(this.ModbusType, client, modbusServerMessage, e);
if (this.IsWriteMemory)
{
var result = this.Write(modbusServerMessage.ModbusAddress.ToString(), coreData, modbusServerMessage.Length);
if (result.IsSuccess)
await WriteSuccess(this.ModbusType, client, modbusServerMessage, e);
else
await WriteError(this.ModbusType, client, modbusServerMessage, e);
}
var result = this.ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
await WriteSuccess(modbusRtu, client, Bytes, e);
else
await WriteSuccess(this.ModbusType, client, modbusServerMessage, e);
await WriteError(modbusRtu, client, Bytes, e);
}
else
{
await WriteError(this.ModbusType, client, modbusServerMessage, e);
}
await WriteSuccess(modbusRtu, client, Bytes, e);
}
else
{
//写入内存区
var result = this.Write(modbusServerMessage.ModbusAddress.ToString(), coreData, modbusServerMessage.Length);
if (result.IsSuccess)
{
await WriteSuccess(this.ModbusType, client, modbusServerMessage, e);
}
else
{
await WriteError(this.ModbusType, client, modbusServerMessage, e);
}
await WriteError(modbusRtu, client, Bytes, e);
}
}
else
{
//写入寄存器
if (this.WriteData != null)
//写入内存区
var result = this.ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
{
if ((await this.WriteData(modbusServerMessage.ModbusAddress, modbusServerMessage.Content, this.ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
{
if (this.IsWriteMemory)
{
var result = this.Write(modbusServerMessage.ModbusAddress.ToString(), coreData);
if (result.IsSuccess)
await WriteSuccess(this.ModbusType, client, modbusServerMessage, e);
else
await WriteError(this.ModbusType, client, modbusServerMessage, e);
}
else
await WriteSuccess(this.ModbusType, client, modbusServerMessage, e);
}
else
{
await WriteError(this.ModbusType, client, modbusServerMessage, e);
}
await WriteSuccess(modbusRtu, client, Bytes, e);
}
else
{
var result = this.Write(modbusServerMessage.ModbusAddress.ToString(), coreData);
if (result.IsSuccess)
await WriteError(modbusRtu, client, Bytes, e);
}
}
}
else
{
//写入寄存器
if (this.WriteData != null)
{
var modbusAddress = new ModbusAddress(modbusRequest) { WriteFunctionCode = modbusRequest.FunctionCode, FunctionCode = 3 };
if ((await this.WriteData(modbusAddress, this.ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
{
if (this.IsWriteMemory)
{
await WriteSuccess(this.ModbusType, client, modbusServerMessage, e);
var result = this.ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
await WriteSuccess(modbusRtu, client, Bytes, e);
else
await WriteError(modbusRtu, client, Bytes, e);
}
else
{
await WriteError(this.ModbusType, client, modbusServerMessage, e);
}
await WriteSuccess(modbusRtu, client, Bytes, e);
}
else
{
await WriteError(modbusRtu, client, Bytes, e);
}
}
else
{
var result = this.ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
{
await WriteSuccess(modbusRtu, client, Bytes, e);
}
else
{
await WriteError(modbusRtu, client, Bytes, e);
}
}
}
}
});
return EasyTask.CompletedTask;
return Task.CompletedTask;
}
private async Task ReturnData(IClientChannel client, ReceivedDataEventArgs e, ReadOnlyMemory<byte> sendData)
private async Task ReturnData(IClientChannel client, ReadOnlyMemory<byte> sendData, ReceivedDataEventArgs e)
{
if (SendDelayTime > 0)
await Task.Delay(SendDelayTime).ConfigureAwait(false);
if (client is IUdpClientSender udpClientSender)
if (ModbusType == ModbusTypeEnum.ModbusRtu)
await udpClientSender.SendAsync(((UdpReceivedDataEventArgs)e).EndPoint, new SendMessage(sendData));
else
await udpClientSender.SendAsync(((UdpReceivedDataEventArgs)e).EndPoint, sendData);
else
if (ModbusType == ModbusTypeEnum.ModbusRtu)
await client.SendAsync(new SendMessage(sendData));
await udpClientSender.SendAsync(((UdpReceivedDataEventArgs)e).EndPoint, sendData);
else
await client.SendAsync(sendData);
}
/// <summary>
/// 返回错误码
/// </summary>
private async Task WriteError(ModbusTypeEnum modbusType, IClientChannel client, IModbusServerMessage modbusServerMessage, ReceivedDataEventArgs e)
private async Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlyMemory<byte> bytes, ReceivedDataEventArgs e)
{
ValueByteBlock valueByteBlock = new(20);
try
{
if (modbusType == ModbusTypeEnum.ModbusRtu)
if (modbusRtu)
{
valueByteBlock.Write(modbusServerMessage.Bytes.Slice(0, 2).Span);
valueByteBlock.Write(bytes.Slice(0, 2).Span);
valueByteBlock.WriteByte((byte)1);
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Span));
valueByteBlock[1] = (byte)(valueByteBlock[1] + 128);
await ReturnData(client, e, valueByteBlock.Memory);
}
else
{
valueByteBlock.Write(modbusServerMessage.Bytes.Slice(0, 8).Span);
valueByteBlock.Write(bytes.Slice(0, 8).Span);
valueByteBlock.WriteByte((byte)1);
valueByteBlock[5] = (byte)(valueByteBlock.Length - 6);
valueByteBlock[7] = (byte)(valueByteBlock[7] + 128);
await ReturnData(client, e, valueByteBlock.Memory);
}
await ReturnData(client, valueByteBlock.Memory, e);
}
finally
{
@@ -414,35 +415,36 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
}
}
/// <summary>
/// 返回成功
/// </summary>
internal async Task WriteSuccess(ModbusTypeEnum modbusType, IClientChannel client, IModbusServerMessage modbusServerMessage, ReceivedDataEventArgs e)
private async Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlyMemory<byte> bytes, ReceivedDataEventArgs e)
{
ValueByteBlock valueByteBlock = new(20);
if (modbusType == ModbusTypeEnum.ModbusRtu)
try
{
valueByteBlock.Write(modbusServerMessage.Bytes.Slice(0, 6).Span);
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Memory));
await ReturnData(client, e, valueByteBlock.Memory);
if (modbusRtu)
{
valueByteBlock.Write(bytes.Slice(0, 6).Span);
valueByteBlock.Write(CRC16Utils.Crc16Only(valueByteBlock.Span));
}
else
{
valueByteBlock.Write(bytes.Slice(0, 12).Span);
valueByteBlock[5] = (byte)(valueByteBlock.Length - 6);
}
await ReturnData(client, valueByteBlock.Memory, e);
}
else
finally
{
valueByteBlock.Write(modbusServerMessage.Bytes.Slice(0, 12).Span);
valueByteBlock[5] = (byte)(valueByteBlock.Length - 6);
await ReturnData(client, e, valueByteBlock.Memory);
valueByteBlock.SafeDispose();
}
}
private readonly ReaderWriterLockSlim _lockSlim = new();
/// <inheritdoc/>
public OperResult<Memory<byte>> Read(string address, int length, CancellationToken cancellationToken = default)
public OperResult<ReadOnlyMemory<byte>> ModbusRequest(ModbusRequest mAddress, bool read, CancellationToken cancellationToken = default)
{
try
{
ModbusAddress mAddress = ModbusAddress.ParseFrom(address, this.Station);
if (this.MulStation)
{
this.Init(mAddress);
@@ -455,29 +457,67 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
}
this.Init(mAddress);
}
using (new ReadLock(this._lockSlim))
var ModbusServer01ByteBlock = this.ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = this.ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = this.ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = this.ModbusServer04ByteBlocks[mAddress.Station];
if (read)
{
var ModbusServer01ByteBlock = this.ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = this.ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = this.ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = this.ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * this.RegisterByteLength;
switch (mAddress.ReadFunction)
using (new ReadLock(this._lockSlim))
{
case 1:
return OperResult.CreateSuccessResult(ModbusServer01ByteBlock.ToMemory(mAddress.AddressStart, len));
int len = mAddress.Length;
case 2:
return OperResult.CreateSuccessResult(ModbusServer02ByteBlock.ToMemory(mAddress.AddressStart, len));
switch (mAddress.FunctionCode)
{
case 1:
return OperResult.CreateSuccessResult(ModbusServer01ByteBlock.Memory.Slice(mAddress.StartAddress, len));
case 3:
case 2:
return OperResult.CreateSuccessResult(ModbusServer02ByteBlock.Memory.Slice(mAddress.StartAddress, len));
return OperResult.CreateSuccessResult(ModbusServer03ByteBlock.ToMemory(mAddress.AddressStart * this.RegisterByteLength, len));
case 3:
case 4:
return OperResult.CreateSuccessResult(ModbusServer04ByteBlock.ToMemory(mAddress.AddressStart * this.RegisterByteLength, len));
return OperResult.CreateSuccessResult(ModbusServer03ByteBlock.Memory.Slice(mAddress.StartAddress * this.RegisterByteLength, len));
case 4:
return OperResult.CreateSuccessResult(ModbusServer04ByteBlock.Memory.Slice(mAddress.StartAddress * this.RegisterByteLength, len));
}
}
}
else
{
if (mAddress.FunctionCode == 0x05)
{
var data = ModbusServer01ByteBlock.Memory.Slice(mAddress.StartAddress, 1);
mAddress.Data = new ReadOnlyMemory<byte>([data.Span[0].SetBit(mAddress.StartAddress % 8, mAddress.Data.Span[0] == 0xff)]);
}
using (new WriteLock(this._lockSlim))
{
switch (mAddress.FunctionCode)
{
case 2:
ModbusServer02ByteBlock.Position = mAddress.StartAddress;
ModbusServer02ByteBlock.Write(mAddress.Data.Span);
return new();
case 1:
case 5:
case 15:
ModbusServer01ByteBlock.Position = mAddress.StartAddress;
ModbusServer01ByteBlock.Write(mAddress.Data.Span);
return new();
case 4:
ModbusServer04ByteBlock.Position = mAddress.StartAddress;
ModbusServer04ByteBlock.Write(mAddress.Data.Span);
return new();
case 6:
case 16:
ModbusServer03ByteBlock.Position = mAddress.StartAddress * this.RegisterByteLength;
ModbusServer03ByteBlock.Write(mAddress.Data.Span);
return new();
}
}
}
@@ -492,7 +532,9 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
/// <inheritdoc/>
public override ValueTask<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
var result = Read(address, length, cancellationToken);
ModbusAddress mAddress = ModbusAddress.ParseFrom(address, this.Station);
mAddress.Length = (ushort)(length * RegisterByteLength);
var result = ModbusRequest(mAddress, true, cancellationToken);
if (result.IsSuccess)
{
return EasyValueTask.FromResult(new OperResult<byte[]>() { Content = result.Content.ToArray() });
@@ -503,74 +545,36 @@ public class ModbusSlave : ProtocolBase, ITcpService, IDtuClient
}
}
/// <inheritdoc/>
public OperResult Write(string address, byte[] value, int? length = null, CancellationToken cancellationToken = default)
{
try
{
length ??= value.Length;
ModbusAddress mAddress = ModbusAddress.ParseFrom(address, this.Station);
if (this.MulStation)
{
this.Init(mAddress);
}
else
{
if (this.Station != mAddress.Station)
{
return new OperResult<byte[]>(ModbusResource.Localizer["StationNotSame", mAddress.Station, this.Station]);
}
this.Init(mAddress);
}
using (new WriteLock(this._lockSlim))
{
var ModbusServer01ByteBlock = this.ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = this.ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = this.ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = this.ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 1:
ModbusServer01ByteBlock.Position = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.ByteToBoolArray(length.Value).BoolArrayToByte());
return OperResult.Success;
case 2:
ModbusServer02ByteBlock.Position = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value);
return OperResult.Success;
case 3:
ModbusServer03ByteBlock.Position = mAddress.AddressStart * this.RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return OperResult.Success;
case 4:
ModbusServer04ByteBlock.Position = mAddress.AddressStart * this.RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return OperResult.Success;
}
}
return new OperResult<byte[]>(ModbusResource.Localizer["FunctionError"]);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override ValueTask<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
return EasyValueTask.FromResult(Write(address, value, cancellationToken: cancellationToken));
ModbusAddress mAddress = ModbusAddress.ParseFrom(address, this.Station);
mAddress.Data = value;
var result = ModbusRequest(mAddress, false, cancellationToken);
if (result.IsSuccess)
{
return EasyValueTask.FromResult(new OperResult());
}
else
{
return EasyValueTask.FromResult(new OperResult(result));
}
}
/// <inheritdoc/>
public override ValueTask<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
return EasyValueTask.FromResult(Write(address, value.BoolArrayToByte(), cancellationToken: cancellationToken));
ModbusAddress mAddress = ModbusAddress.ParseFrom(address, this.Station);
mAddress.Data = value.BoolArrayToByte();
var result = ModbusRequest(mAddress, false, cancellationToken);
if (result.IsSuccess)
{
return EasyValueTask.FromResult(new OperResult());
}
else
{
return EasyValueTask.FromResult(new OperResult(result));
}
}
#endregion

View File

@@ -89,7 +89,7 @@ internal class OpcDiscovery
//2
m_server.EnumClassesOfCategories(
1,
new Guid[] { catid },
[catid],
0,
null,
out enumerator);
@@ -117,7 +117,7 @@ internal class OpcDiscovery
IOPCEnumGUID enumerator = null;
m_server.EnumClassesOfCategories(
1,
new Guid[] { catid },
[catid],
0,
null,
out enumerator);

View File

@@ -19,7 +19,7 @@ public class BrowseElement : ICloneable
private string m_itemName;
private string m_itemPath;
private string m_name;
private ItemProperty[] m_properties = new ItemProperty[0];
private ItemProperty[] m_properties = [];
public bool HasChildren
{

View File

@@ -512,7 +512,7 @@ public class FormUtils
}
// construct the event based on the known event type.
BaseEventState e = (BaseEventState)Activator.CreateInstance(knownType, new object[] { (NodeState)null });
BaseEventState e = (BaseEventState)Activator.CreateInstance(knownType, [(NodeState)null]);
// get the filter which defines the contents of the notification.
EventFilter filter = monitoredItem.Status.Filter as EventFilter;

View File

@@ -48,9 +48,9 @@ public class OPCUAJsonEncoder : IJsonEncoder, IEncoder, IDisposable
private bool m_leaveOpen;
private static readonly char[] m_specialChars = new char[7] { '"', '\\', '\n', '\r', '\t', '\b', '\f' };
private static readonly char[] m_specialChars = ['"', '\\', '\n', '\r', '\t', '\b', '\f'];
private static readonly char[] m_substitution = new char[7] { '"', '\\', 'n', 'r', 't', 'b', 'f' };
private static readonly char[] m_substitution = ['"', '\\', 'n', 'r', 't', 'b', 'f'];
public EncodingType EncodingType => EncodingType.Json;

View File

@@ -22,16 +22,13 @@
"NotString": "Data type in PLC is not String",
"WriteDataLengthMore": "Write length exceeds limit",
"ERROR01": "Address out of range",
"ERROR02": "Invalid return length",
"ERROR03": "Data size does not match",
"ERROR04": "Data block does not exist",
"ERROR05": "Exceeds PDU size",
"ERROR06": "Invalid value",
"ERROR07": "Function not available",
"ERROR08": "Password required",
"ERROR09": "Invalid password",
"ERROR010": "No password set or cleared",
"ERROR5": "Invalid address",
"ERROR1": "Hardware error",
"ERROR3": "Accessing the object not allowed",
"ERROR6": "Data type not supported",
"ERROR7": "Data type inconsistent",
"ERROR10": "Object does not exist",
"MulWriteError": "Multiple write not supported",
"StringLengthReadError": "Consecutive reading of variable-length strings not supported",

View File

@@ -22,16 +22,13 @@
"NotString": "PLC中的数据类型不是String",
"WriteDataLengthMore": "写入长度超限",
"ERROR01": "地址超限",
"ERROR02": "返回长度无效",
"ERROR03": "数据大小不匹配",
"ERROR04": "数据块不存在",
"ERROR05": "超出PDU大小",
"ERROR06": "无效的值",
"ERROR07": "功能不可用",
"ERROR08": "需要密码",
"ERROR09": "无效密码",
"ERROR010": "没有设置密码或已清除",
"ERROR5": "无效地址所需的地址超出此PLC的极限",
"ERROR1": "硬件错误",
"ERROR3": "对象不允许访问",
"ERROR6": "数据类型不支持",
"ERROR7": "日期类型不一致",
"ERROR10": "对象不存在",
"MulWriteError": "不支持多写",
"StringLengthReadError": "不支持变长字符串的连读",

View File

@@ -0,0 +1,186 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using TouchSocket.Core;
namespace ThingsGateway.Foundation.SiemensS7;
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class S7Response : S7Request
{
/// <summary>
/// 错误码
/// </summary>
public byte? ErrorCode { get; set; }
}
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class S7Message : MessageBase, IResultMessage
{
/// <inheritdoc/>
public override int HeaderLength => 4;
public S7Response Response { get; set; } = new();
public SiemensAddress[] Request { get; set; }
public S7Send? S7Send { get; set; }
public override void SendInfo(ISendMessage sendMessage)
{
S7Send = (sendMessage as S7Send);
Request = S7Send.SiemensAddress;
}
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
{
byteBlock.Position += 2;
BodyLength = byteBlock.ReadUInt16(EndianType.Big) - 4;
return true;
}
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
{
if (Response.ErrorCode.HasValue)
{
return FilterResult.Success;
}
var pos = byteBlock.Position;
if (S7Send.Handshake)
{
if (byteBlock[pos + 1] == 0xD0) // 首次握手0XD0连接确认
{
return FilterResult.Success;
}
else
{
// 其余情况判断错误代码
if (byteBlock[pos + 13] + byteBlock[pos + 14] > 0) // 如果错误代码不为0
{
Response.ErrorCode = byteBlock[pos + 14];
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["ReturnError", byteBlock[pos + 13].ToString("X2"), byteBlock[pos + 14].ToString("X2")];
return FilterResult.Success;
}
else
{
Content = byteBlock.ToArray(byteBlock.Length - 2, 2);
this.OperCode = 0;
return FilterResult.Success;
}
}
}
//分bit/byte解析
if (S7Send.Read)
{
int length = 0;
int itemLen = Request.Length;//驱动只会读取一个项
//添加错误代码校验
// 其余情况判断错误代码
if (byteBlock[pos + 13] + byteBlock[pos + 14] > 0) // 如果错误代码不为0
{
Response.ErrorCode = byteBlock[pos + 14];
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["ReturnError", byteBlock[pos + 13].ToString("X2"), byteBlock[pos + 14].ToString("X2")];
return FilterResult.Success;
}
else
{
if (byteBlock[pos + 16] != itemLen)
{
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["DataLengthError"];
return FilterResult.Success;
}
if (byteBlock.Length < pos + 18)
{
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["DataLengthError"];
return FilterResult.Success;
}
if (byteBlock[pos + 17] != byte.MaxValue)
{
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["ValidateDataError", byteBlock[pos + 17], SiemensHelper.GetCpuError(byteBlock[pos + 17])];
return FilterResult.Success;
}
//解析读取字节数组
for (int index = 0; index < itemLen; index++)
{
var address = Request[index];
length += address.Length;
}
using ValueByteBlock data = new(length);
var dataIndex = pos + 17;
for (int index = 0; index < itemLen; index++)
{
if (byteBlock[dataIndex] != byte.MaxValue)
{
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["ValidateDataError", byteBlock[pos + 17], SiemensHelper.GetCpuError(byteBlock[pos + 17])];
return FilterResult.Success;
}
var address = Request[index];
if (byteBlock[dataIndex + 1] == 4)//Bit:3;Byte:4;Counter或者Timer:9
{
data.Write(byteBlock.Span.Slice(dataIndex + 4, address.Length));
dataIndex += address.Length + 3;
}
else if (byteBlock[dataIndex + 1] == 9)//Counter或者Timer:9
{
int num = (byteBlock[dataIndex + 2] * 256) + byteBlock[dataIndex + 3];
if (num % 3 == 0)
{
for (int indexCT = 0; indexCT < num / 3; indexCT++)
{
data.Write(byteBlock.Span.Slice(dataIndex + 5 + (3 * indexCT), 2));
}
}
else
{
for (int indexCT = 0; indexCT < num / 5; indexCT++)
{
data.Write(byteBlock.Span.Slice(dataIndex + 7 + (5 * indexCT), 2));
}
}
dataIndex += num + 4;
}
}
this.OperCode = 0;
Content = data.ToArray();
return FilterResult.Success;
}
}
else
{
if (byteBlock.Length < pos + 18)
{
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["DataLengthError"];
return FilterResult.Success;
}
if (byteBlock[pos + 17] != byte.MaxValue)
{
this.OperCode = 999;
this.ErrorMessage = SiemensS7Resource.Localizer["ValidateDataError", byteBlock[pos + 17], SiemensHelper.GetCpuError(byteBlock[pos + 17])];
return FilterResult.Success;
}
return FilterResult.Success;
}
}
}

View File

@@ -0,0 +1,187 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using TouchSocket.Core;
namespace ThingsGateway.Foundation.SiemensS7;
/// <summary>
/// <inheritdoc/>
/// </summary>
public class S7Request
{
#region Request
/// <summary>
/// bit位偏移
/// </summary>
public byte BitCode { get; set; }
/// <summary>
/// 数据块代码
/// </summary>
public byte DataCode { get; set; }
/// <summary>
/// DB块数据信息
/// </summary>
public ushort DbBlock { get; set; }
/// <summary>
/// IsString默认是true如果是char[],需要填写W=false;
/// </summary>
public bool IsString { get; set; } = true;
/// <summary>
/// Length
/// </summary>
public int Length { get; set; }
public int AddressStart { get; set; }
/// <summary>
/// 写入数据
/// </summary>
public byte[] Data { get; set; }
#endregion Request
}
/// <summary>
/// <inheritdoc/>
/// </summary>
internal class S7Send : ISendMessage
{
public int Sign { get; set; }
public int MaxLength => 2048;
internal SiemensAddress[]? SiemensAddress;
internal bool Read;
internal bool IsBit;
internal bool Handshake;
internal byte[] HandshakeBytes;
public S7Send(SiemensAddress[]? siemensAddress = null, bool? read = null, bool? isBit = null, bool? handshake = null, byte[]? handshakeBytes = default)
{
SiemensAddress = siemensAddress;
Read = read ?? false;
IsBit = isBit ?? false;
Handshake = handshake ?? false;
HandshakeBytes = handshakeBytes;
}
internal void GetReadCommand<TByteBlock>(ref TByteBlock valueByteBlock, SiemensAddress[] siemensAddress) where TByteBlock : IByteBlock
{
byte len = (byte)siemensAddress.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
//COTP信息
valueByteBlock.WriteByte(2);//长度
valueByteBlock.WriteByte(0xf0);//pdu类型
valueByteBlock.WriteByte(0x80);//目标引用
//header
valueByteBlock.WriteByte(0x32);//协议id
valueByteBlock.WriteByte(0x01);//请求
valueByteBlock.WriteUInt16(0x00, EndianType.Big);//冗余识别
valueByteBlock.WriteUInt16(0x01, EndianType.Big);//数据引用
valueByteBlock.WriteUInt16(parameterLen, EndianType.Big);//参数长度item.len*12+2
valueByteBlock.WriteUInt16(0x00, EndianType.Big);//数据长度data.len+4 ,写入时填写读取时为0
valueByteBlock.WriteByte(0x04);//功能码4 Read Var, 5 Write Var
valueByteBlock.WriteByte(len);//Item数量
//通信项构建
for (int index = 0; index < len; index++)
{
valueByteBlock.WriteByte(0x12);//Var 规范
valueByteBlock.WriteByte(0x0a);//剩余的字节长度
valueByteBlock.WriteByte(0x10);//Syntax ID
if (siemensAddress[index].DataCode == (byte)S7WordLength.Counter || siemensAddress[index].DataCode == (byte)S7WordLength.Timer)
{
valueByteBlock.WriteByte(siemensAddress[index].DataCode);//数据类型
}
else
{
valueByteBlock.WriteByte((byte)S7WordLength.Byte);//数据类型
}
valueByteBlock.WriteUInt16((ushort)siemensAddress[index].Length, EndianType.Big);//读取长度
valueByteBlock.WriteUInt16(siemensAddress[index].DbBlock, EndianType.Big);//DB编号
valueByteBlock.WriteByte(siemensAddress[index].DataCode);//数据块类型
valueByteBlock.WriteByte((byte)(siemensAddress[index].AddressStart / 256 / 256 % 256));//数据块偏移量
valueByteBlock.WriteByte((byte)(siemensAddress[index].AddressStart / 256 % 256));//数据块偏移量
valueByteBlock.WriteByte((byte)(siemensAddress[index].AddressStart % 256));//数据块偏移量
}
}
internal static void GetWriteByteCommand<TByteBlock>(ref TByteBlock valueByteBlock, SiemensAddress address, bool isBit) where TByteBlock : IByteBlock
{
var data = address.Data;
byte len = (byte)data.Length;
ushort telegramLen = (ushort)(16 + 19 + len);
ushort parameterLen = 12 + 2;
//TPKT
valueByteBlock.WriteByte(3);//版本
valueByteBlock.WriteByte(0);
valueByteBlock.WriteUInt16(telegramLen, EndianType.Big);//长度item.len*12+19
//COTP信息
valueByteBlock.WriteByte(2);//长度
valueByteBlock.WriteByte(0xf0);//pdu类型
valueByteBlock.WriteByte(0x80);//目标引用
//header
valueByteBlock.WriteByte(0x32);//协议id
valueByteBlock.WriteByte(0x01);//请求
valueByteBlock.WriteUInt16(0x00, EndianType.Big);//冗余识别
valueByteBlock.WriteUInt16(0x01, EndianType.Big);//数据引用
valueByteBlock.WriteUInt16(parameterLen, EndianType.Big);//参数长度item.len*12+2
valueByteBlock.WriteUInt16((ushort)(4 + len), EndianType.Big);//数据长度data.len+4 ,写入时填写读取时为0
valueByteBlock.WriteByte(0x05);//功能码4 Read Var, 5 Write Var
valueByteBlock.WriteByte(1);//Item数量
//写入Item与读取大致相同
valueByteBlock.WriteByte(0x12);//Var 规范
valueByteBlock.WriteByte(0x0a);//剩余的字节长度
valueByteBlock.WriteByte(0x10);//Syntax ID
valueByteBlock.WriteByte(isBit ? (byte)S7WordLength.Bit : (byte)S7WordLength.Byte);//数据类型
valueByteBlock.WriteUInt16((ushort)len, EndianType.Big);//长度
valueByteBlock.WriteUInt16(address.DbBlock, EndianType.Big);//DB编号
valueByteBlock.WriteByte(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));//数据块偏移量
//后面跟的是写入的数据信息
valueByteBlock.WriteByte(0);
valueByteBlock.WriteByte((byte)(isBit ? 3 : 4));//Bit:3;Byte:4;Counter或者Timer:9
valueByteBlock.WriteUInt16((ushort)(isBit ? len : len * 8), EndianType.Big);
valueByteBlock.Write(data);
}
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
{
if (Handshake == true)
{
byteBlock.Write(HandshakeBytes);
return;
}
if (Read == true)
{
GetReadCommand(ref byteBlock, SiemensAddress);
}
else
{
GetWriteByteCommand(ref byteBlock, SiemensAddress[0], IsBit);
}
}
}

View File

@@ -19,34 +19,22 @@ namespace ThingsGateway.Foundation.SiemensS7;
/// <summary>
/// 西门子PLC地址数据信息
/// </summary>
public class SiemensAddress
public class SiemensAddress : S7Request
{
/// <summary>
/// bit位偏移
/// </summary>
public byte BitCode { get; set; }
public SiemensAddress()
{
}
/// <summary>
/// 数据块代码
/// </summary>
public byte DataCode { get; set; }
/// <summary>
/// DB块数据信息
/// </summary>
public ushort DbBlock { get; set; }
/// <summary>
/// IsString默认是true如果是char[],需要填写W=false;
/// </summary>
public bool IsString { get; set; } = true;
/// <summary>
/// Length
/// </summary>
public int Length { get; set; }
public int AddressStart { get; set; }
public SiemensAddress(SiemensAddress siemensAddress)
{
this.AddressStart = siemensAddress.AddressStart;
this.BitCode = siemensAddress.BitCode;
this.DataCode = siemensAddress.DataCode;
this.DbBlock = siemensAddress.DbBlock;
this.Data = siemensAddress.Data;
this.IsString = siemensAddress.IsString;
this.Length = siemensAddress.Length;
}
/// <inheritdoc />
public override string ToString()
@@ -88,7 +76,7 @@ public class SiemensAddress
return DataCode == (byte)S7Area.DB ? $"DB{DbBlock}.{GetStringAddress(AddressStart)}{(IsString ? ";" : ";W=false;")}" : AddressStart.ToString() + (IsString ? ";" : ";W=false;");
}
private static string GetStringAddress(int addressStart)
private string GetStringAddress(int addressStart)
{
return addressStart % 8 == 0 ? (addressStart / 8).ToString() : $"{addressStart / 8}.{addressStart % 8}";
}

View File

@@ -1,37 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.SiemensS7;
/// <inheritdoc/>
internal class SiemensMessage : MessageBase, IResultMessage
{
/// <inheritdoc/>
public override int HeadBytesLength => 4;
public ReadOnlyMemory<byte> SendBytes { get; set; }
public override void SendInfo(ReadOnlyMemory<byte> sendBytes)
{
SendBytes = sendBytes;
}
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[]? headBytes)
{
if (headBytes == null || headBytes.Length < 4)
BodyLength = 0;
int length = (headBytes[2] * 256) + headBytes[3] - 4;
if (length < 0)
length = 0;
BodyLength = length;
return headBytes != null && headBytes[0] == 3 && headBytes[1] == 0;
}
}

View File

@@ -1,69 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using TouchSocket.Core;
namespace ThingsGateway.Foundation.SiemensS7;
/// <summary>
/// SiemensS7DataHandleAdapter西门子S7数据处理适配器
/// </summary>
internal class SiemensS7DataHandleAdapter : ReadWriteDevicesSingleStreamDataHandleAdapter<SiemensMessage>
{
/// <inheritdoc/>
protected override AdapterResult UnpackResponse(SiemensMessage request, IByteBlock byteBlock)
{
var result = new OperResult<AdapterResult>(); // 创建一个操作结果对象
if (byteBlock[2] * 256 + byteBlock[3] == 7 || request.SendBytes.Length == 0) // 判断响应中的状态信息是否为7
{
result = new() { Content = new AdapterResult() { Content = Array.Empty<byte>(), FilterResult = FilterResult.Success } }; // 如果是7则表示成功设置操作结果为成功
}
else
{
var send = request.SendBytes.Span;
// 以请求方为准,分开返回类型校验
switch (send[17]) // 根据请求命令中的类型字节进行分析
{
case 0x04: // 读取类型命令
result = SiemensHelper.AnalysisReadByte(send, byteBlock); // 调用辅助方法进行读取数据的解析
break;
case 0x05: // 写入类型命令
result = SiemensHelper.AnalysisWrite(byteBlock); // 调用辅助方法进行写入数据的解析
break;
default:
// 添加返回代码校验
if (byteBlock[5] == 0xD0) // 首次握手0XD0连接确认
{
result = new() { Content = new AdapterResult() { Content = Array.Empty<byte>(), FilterResult = FilterResult.Success } }; // 如果是7则表示成功设置操作结果为成功
}
else
{
// 其余情况判断错误代码
if (byteBlock[17] + byteBlock[18] > 0) // 如果错误代码不为0
{
result = new(SiemensS7Resource.Localizer["ReturnError", byteBlock[17].ToString("X2"), byteBlock[18].ToString("X2")]) { Content = new AdapterResult() { Content = Array.Empty<byte>(), FilterResult = FilterResult.Success } };
}
else
{
result = new() { Content = new AdapterResult() { Content = byteBlock.ToArray(byteBlock.Length - 2, 2), FilterResult = FilterResult.Success } }; // 如果错误代码为0则设置操作结果为成功
}
}
break;
}
}
request.OperCode = result.OperCode; // 设置请求的操作码
request.ErrorMessage = result.ErrorMessage; // 设置请求的错误信息
return result.Content; // 返回操作结果的第二部分内容
}
}

View File

@@ -49,7 +49,7 @@ internal static class PackHelper
{
// 解析SiemensAddress对象
var s7Address = SiemensAddress.ParseFrom(it.RegisterAddress);
var lastLen = it.DataType.GetByteLength();
int lastLen = it.DataType.GetByteLength();
// 处理特殊情况下的长度
if (lastLen <= 0)

Some files were not shown because too many files have changed in this diff Show More