Compare commits

..

40 Commits

Author SHA1 Message Date
Kimdiego2098
affe9a44e0 优化 ModbusServer 内存占用 2023-10-20 01:53:48 +08:00
Kimdiego2098
43730fa519 3.0.0.21 2023-10-20 01:19:18 +08:00
Kimdiego2098
d39aa22b09 优化OPCUAServer,取消注册,提供多url写入 2023-10-20 01:19:05 +08:00
Kimdiego2098
e232a6b6ea 更新赞助名单 2023-10-19 21:15:35 +08:00
Kimdiego2098
71ebb36fe9 更新文档 2023-10-19 20:36:28 +08:00
Kimdiego2098
78a0b86327 更新版本:3.0.0.20 2023-10-19 20:27:31 +08:00
Kimdiego2098
2636c16a97 优化modbusServer内存管理 2023-10-19 20:24:39 +08:00
Kimdiego2098
fd77c0242d update touchsocket 2023-10-19 20:23:43 +08:00
Kimdiego2098
e74819a900 update toucksocket 2023-10-18 21:51:49 +08:00
Kimdiego2098
9b7f696c9b 更新文档 2023-10-18 20:42:10 +08:00
Kimdiego2098
0230d614e7 发布驱动包 2023-10-18 18:04:37 +08:00
Diego2098
252d99ad78 !12 【轻量级 PR】:修正心跳事件中的参数
Merge pull request !12 from youthalan/N/A
2023-10-18 06:59:17 +00:00
youthalan
1ffc200350 修正心跳事件中的参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 06:58:03 +00:00
Kimdiego2098
807d89b2b2 更新版本号 2023-10-18 13:13:03 +08:00
Kimdiego2098
4013afa1f1 初始化 采集/上传线程时 直接返回线程控制 2023-10-18 13:11:59 +08:00
Kimdiego2098
a580927ceb 更新OPCUAClient类库 2023-10-18 13:09:25 +08:00
Kimdiego2098
bf2cf52034 更新OPCUAClient类库 2023-10-18 12:45:26 +08:00
Kimdiego2098
81bb8b7c31 更新OPCUAClient类库 2023-10-18 12:44:58 +08:00
Kimdiego2098
a825007fb5 更新版本号与nuget发布 2023-10-18 12:39:42 +08:00
Kimdiego2098
988124d96a 更新OPCUAClient类库 2023-10-18 12:38:20 +08:00
Diego2098
f0de815296 !11 补充OPCClient类库事件的缺失文件
Merge pull request !11 from youthalan/N/A
2023-10-18 04:34:34 +00:00
youthalan
0e2d58c887 补充OPCClient类库事件的缺失文件
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 04:32:39 +00:00
Diego2098
b155382626 !10 添加连接或断开事件
Merge pull request !10 from youthalan/N/A
2023-10-18 04:20:22 +00:00
youthalan
f362d740af 修改OPCUAClient类添加连接或断开事件,修改注入时不需要带参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 03:48:05 +00:00
Kimdiego2098
4a85e31a4f update 3.0.0.16 2023-10-18 00:38:36 +08:00
Kimdiego2098
302c270ad5 opcuaClient浏览空间 添加是否显示子变量的选项 2023-10-18 00:36:44 +08:00
Kimdiego2098
3c1517d0f3 更改日志输出内容 2023-10-18 00:15:07 +08:00
Kimdiego2098
f9fb222044 update 3.0.0.15 2023-10-18 00:07:24 +08:00
Kimdiego2098
e8edc02ba3 增加不支持单文件发布的说明 2023-10-17 23:47:24 +08:00
Kimdiego2098
95a44e3053 update tdengineDB plugin 2023-10-17 23:43:48 +08:00
Kimdiego2098
74a9fe9a87 update touchsocket 2023-10-17 23:12:19 +08:00
Kimdiego2098
4d03f9ea1a TD时序库插件,创建时间更改为主键 2023-10-17 23:08:09 +08:00
Kimdiego2098
67c96ca991 update touchsocket 2023-10-17 23:06:21 +08:00
Kimdiego2098
88fb793c68 更新nuget包 2023-10-17 21:00:50 +08:00
Kimdiego2098
d6d02d8cc5 update SQLDB 2023-10-16 20:50:10 +08:00
Kimdiego2098
c5a3f8e2e3 update touchsocket and other 2023-10-16 20:36:51 +08:00
Kimdiego2098
27e8653a1a 3.0.0.13 2023-10-16 17:44:09 +08:00
Kimdiego2098
863beda82c 增加关系库存储插件; 2023-10-16 17:40:17 +08:00
Kimdiego2098
bac84c3ecd 增加时序库存储插件; 2023-10-16 17:40:13 +08:00
Kimdiego2098
2fca2ad9f8 更新pro用户列表 2023-10-16 17:39:14 +08:00
196 changed files with 3616 additions and 1190 deletions

View File

@@ -5,3 +5,99 @@ dotnet_diagnostic.CA1848.severity = none
# CA2254: 模板应为静态表达式
dotnet_diagnostic.CA2254.severity = suggestion
[*.cs]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# 命名样式
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
[*.vb]
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.类型.required_modifiers =
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.非字段成员.required_modifiers =
# 命名样式
dotnet_naming_style.以_i_开始.required_prefix = I
dotnet_naming_style.以_i_开始.required_suffix =
dotnet_naming_style.以_i_开始.word_separator =
dotnet_naming_style.以_i_开始.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotnet_naming_style.帕斯卡拼写法.required_suffix =
dotnet_naming_style.帕斯卡拼写法.word_separator =
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotnet_naming_style.帕斯卡拼写法.required_suffix =
dotnet_naming_style.帕斯卡拼写法.word_separator =
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.12</Version>
<Version>3.0.0.22</Version>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Authors>Diego</Authors>

View File

@@ -101,7 +101,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
catch (Exception ex)
{
Messages.Add((LogLevel.Error,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex.Message}"));
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex}"));
}
}
@@ -126,7 +126,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
catch (Exception ex)
{
Messages.Add((LogLevel.Error,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex.Message}"));
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex}"));
}
}

View File

@@ -49,7 +49,7 @@ public partial class DriverDebugUIPage : DriverDebugUIBase
}
catch (Exception ex)
{
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex.Message));
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex));
}
}

View File

@@ -116,16 +116,16 @@
<ItemGroup >
<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" />
<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />
<!--<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />-->
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />-->
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
</ItemGroup>
<ItemGroup >

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.12</Version>
<Version>3.0.0.22</Version>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>

View File

@@ -444,7 +444,7 @@ public class DLT645_2007 : ReadWriteDevicesSerialSessionBase
if (Station.IsNullOrEmpty()) Station = string.Empty;
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
string str = $"04000C{(level + 1):D2}";
string str = $"04000C{level + 1:D2}";
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword,
str.ByHexStringToBytes().Reverse().ToArray()

View File

@@ -92,15 +92,15 @@ public class DLT645_2007Address : DeviceAddressBase
StringBuilder stringGeter = new();
if (Station.Length > 0)
{
stringGeter.Append("s=" + Station.Reverse().ToArray().ToHexString() + ";");
stringGeter.Append($"s={Station.Reverse().ToArray().ToHexString()};");
}
if (DataId.Length > 0)
{
stringGeter.Append(DataId.Reverse().ToArray().ToHexString() + ";");
stringGeter.Append($"{DataId.Reverse().ToArray().ToHexString()};");
}
if (!Reverse)
{
stringGeter.Append("s=" + Reverse.ToString() + ";");
stringGeter.Append($"s={Reverse.ToString()};");
}
return stringGeter.ToString();
}

View File

@@ -134,7 +134,7 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误
{
request.Message = "返回控制码:" + $"0x{response[headCodeIndex + 8]:X2},请求控制码:" + $"0x{send[sendHeadCodeIndex + 8]:X2},不符合规则";
request.Message = $"返回控制码0x{response[headCodeIndex + 8]:X2}请求控制码0x{send[sendHeadCodeIndex + 8]:X2},不符合规则";
request.ErrorCode = 999;
return FilterResult.Success;
}
@@ -146,7 +146,7 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
{
byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33);
var error = DLT645Helper.Get2007ErrorMessage(byte1);
request.Message = "异常控制码:" + $"0x{response[headCodeIndex + 8]:X2},错误信息:{error}";
request.Message = $"异常控制码0x{response[headCodeIndex + 8]:X2},错误信息:{error}";
request.ErrorCode = 999;
return FilterResult.Success;
}

View File

@@ -147,15 +147,15 @@ public class ModbusAddress : DeviceAddressBase
StringBuilder stringGeter = new();
if (Station > 0)
{
stringGeter.Append("s=" + Station.ToString() + ";");
stringGeter.Append($"s={Station.ToString()};");
}
if (WriteFunction > 0)
{
stringGeter.Append("w=" + WriteFunction.ToString() + ";");
stringGeter.Append($"w={WriteFunction.ToString()};");
}
if (!string.IsNullOrEmpty(SocketId))
{
stringGeter.Append("id=" + SocketId + ";");
stringGeter.Append($"id={SocketId};");
}
stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString());
return stringGeter.ToString();

View File

@@ -117,7 +117,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[], FilterResult>(ex.Message) { Content2 = FilterResult.Success };
return new OperResult<byte[], FilterResult>(ex) { Content2 = FilterResult.Success };
}
}
/// <summary>
@@ -165,7 +165,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
/// <summary>
@@ -185,7 +185,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
@@ -207,7 +207,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
/// <summary>
@@ -244,7 +244,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
@@ -296,7 +296,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}

View File

@@ -101,62 +101,76 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
EasyLock easyLock = new();
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
easyLock.Wait();
ModbusAddress mAddress;
try
{
return new OperResult<byte[]>("地址错误");
mAddress = ModbusAddress.ParseFrom(address, Station);
}
Init(mAddress);
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult<byte[]>("地址错误");
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return OperResult.CreateSuccessResult(bytes0);
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return OperResult.CreateSuccessResult(bytes1);
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return OperResult.CreateSuccessResult(bytes3);
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return OperResult.CreateSuccessResult(bytes4);
}
return new OperResult<byte[]>("功能码错误");
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
finally
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return OperResult.CreateSuccessResult(bytes0);
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return OperResult.CreateSuccessResult(bytes1);
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return OperResult.CreateSuccessResult(bytes3);
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return OperResult.CreateSuccessResult(bytes4);
easyLock.Release();
}
return new OperResult<byte[]>("功能码错误");
}
/// <inheritdoc/>
@@ -173,87 +187,108 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
easyLock.Wait();
ModbusAddress mAddress;
try
{
return new OperResult("地址错误");
mAddress = ModbusAddress.ParseFrom(address, Station);
}
Init(mAddress);
catch (Exception ex)
{
return new OperResult(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult("地址错误");
}
Init(mAddress);
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
}
return new OperResult("功能码错误");
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
finally
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
easyLock.Release();
}
return new OperResult("功能码错误");
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return (new OperResult(ex));
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
easyLock.Wait();
ModbusAddress mAddress;
try
{
return (new OperResult("地址错误"));
mAddress = ModbusAddress.ParseFrom(address, Station);
}
Init(mAddress);
catch (Exception ex)
{
return (new OperResult(ex));
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return (new OperResult("地址错误"));
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
}
return new OperResult("功能码错误");
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
finally
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
easyLock.Release();
}
return new OperResult("功能码错误");
}
/// <inheritdoc/>
@@ -409,13 +444,9 @@ public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
private void Init(ModbusAddress mAddress)
{
if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
}
}

View File

@@ -57,7 +57,7 @@ public class ModbusSerialServerDataHandleAdapter : ReadWriteDevicesTcpDataHandle
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}

View File

@@ -186,7 +186,7 @@ public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase
var item = commandResult.Content;
if (FrameTime != 0)
Thread.Sleep(FrameTime);
var WaitingClientEx = client.GetWaitingClient(new() { ThrowBreakException = true });
var WaitingClientEx = client.CreateWaitingClient(new() { ThrowBreakException = true });
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
@@ -213,7 +213,7 @@ public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase
var item = commandResult.Content;
await Task.Delay(FrameTime, cancellationToken);
var WaitingClientEx = client.GetWaitingClient(new() { ThrowBreakException = true });
var WaitingClientEx = client.CreateWaitingClient(new() { ThrowBreakException = true });
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}

View File

@@ -104,62 +104,75 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
EasyLock easyLock = new();
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
easyLock.Wait();
ModbusAddress mAddress;
try
{
return new OperResult<byte[]>("地址错误");
mAddress = ModbusAddress.ParseFrom(address, Station);
}
Init(mAddress);
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult<byte[]>("地址错误");
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return OperResult.CreateSuccessResult(bytes0);
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return OperResult.CreateSuccessResult(bytes1);
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return OperResult.CreateSuccessResult(bytes3);
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return OperResult.CreateSuccessResult(bytes4);
}
return new OperResult<byte[]>("功能码错误");
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
finally
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return OperResult.CreateSuccessResult(bytes0);
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return OperResult.CreateSuccessResult(bytes1);
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return OperResult.CreateSuccessResult(bytes3);
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return OperResult.CreateSuccessResult(bytes4);
easyLock.Release();
}
return new OperResult<byte[]>("功能码错误");
}
/// <inheritdoc/>
@@ -193,84 +206,104 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
easyLock.Wait();
ModbusAddress mAddress;
try
{
return new OperResult("地址错误");
mAddress = ModbusAddress.ParseFrom(address, Station);
}
Init(mAddress);
catch (Exception ex)
{
return new OperResult(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult("地址错误");
}
Init(mAddress);
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
}
return new OperResult("功能码错误");
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
finally
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
easyLock.Release();
}
return new OperResult("功能码错误");
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return (new OperResult(ex));
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
easyLock.Wait();
ModbusAddress mAddress;
try
{
return (new OperResult("地址错误"));
mAddress = ModbusAddress.ParseFrom(address, Station);
}
Init(mAddress);
catch (Exception ex)
{
return (new OperResult(ex));
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return (new OperResult("地址错误"));
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
}
return new OperResult("功能码错误");
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
finally
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
easyLock.Release();
}
return new OperResult("功能码错误");
}
/// <inheritdoc/>
@@ -429,13 +462,9 @@ public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
private void Init(ModbusAddress mAddress)
{
if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
}
}

View File

@@ -57,7 +57,7 @@ public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAda
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}

View File

@@ -176,7 +176,7 @@ internal class OpcServer : IDisposable
{
status = (OPCSERVERSTATUS)o;
serverStatus = new();
serverStatus.Version = status.wMajorVersion.ToString() + "." + status.wMinorVersion.ToString() + "." + status.wBuildNumber.ToString();
serverStatus.Version = $"{status.wMajorVersion.ToString()}.{status.wMinorVersion.ToString()}.{status.wBuildNumber.ToString()}";
serverStatus.ServerState = status.dwServerState;
serverStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime);
serverStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime);

View File

@@ -1,4 +1,5 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
@@ -8,6 +9,7 @@
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Newtonsoft.Json.Linq;
@@ -17,22 +19,23 @@ using Opc.Ua.Client;
using Opc.Ua.Client.ComplexTypes;
using Opc.Ua.Configuration;
//修改自https://github.com/dathlin/OpcUaHelper 与OPC基金会net库
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
/// 订阅委托
/// </summary>
/// <param name="value"></param>
public delegate void DataChangedEventHandler((VariableNode variableNode, DataValue dataValue, JToken jToken) value);
/// <summary>
/// OPCUAClient
/// </summary>
public class OPCUAClient : IDisposable
{
#region
/// <summary>
/// 当前配置
/// </summary>
@@ -48,30 +51,35 @@ public class OPCUAClient : IDisposable
/// </summary>
public List<string> Variables = new();
private readonly Action<byte, object, string, Exception> _logAction;
/// <summary>
/// 当前的变量名称/OPC变量节点
/// </summary>
private readonly Dictionary<string, VariableNode> _variableDicts = new();
private readonly object checkLock = new();
/// <summary>
/// 当前的订阅组,组名称/组
/// </summary>
private readonly Dictionary<string, Subscription> dic_subscriptions = new();
private readonly ApplicationInstance m_application = new();
private readonly ApplicationConfiguration m_configuration;
private SessionReconnectHandler m_reConnectHandler;
private EventHandler m_ReconnectComplete;
private EventHandler m_ReconnectStarting;
private EventHandler<KeepAliveEventArgs> m_KeepAliveComplete;
private EventHandler<bool> m_ConnectComplete;
private EventHandler<OpcUaStatusEventArgs> m_OpcStatusChange;
private ISession m_session;
/// <summary>
/// 默认的构造函数实例化一个新的OPC UA类
/// </summary>
public OPCUAClient(Action<byte, object, string, Exception> log)
public OPCUAClient()
{
_logAction = log;
var certificateValidator = new CertificateValidator();
certificateValidator.CertificateValidation += CertificateValidation;
@@ -90,7 +98,6 @@ public class OPCUAClient : IDisposable
MaxMessageQueueSize = 1000000,
MaxNotificationQueueSize = 1000000,
MaxPublishRequestCount = 10000000,
},
SecurityConfiguration = new SecurityConfiguration
@@ -133,8 +140,6 @@ public class OPCUAClient : IDisposable
StoreType = CertificateStoreType.Directory,
StorePath = AppContext.BaseDirectory + @"OPCUAClientCertificate\pki\trustedUser",
}
},
TransportQuotas = new TransportQuotas
@@ -160,8 +165,6 @@ public class OPCUAClient : IDisposable
m_configuration.Validate(ApplicationType.Client);
m_application.ApplicationConfiguration = m_configuration;
}
/// <summary>
@@ -188,6 +191,52 @@ public class OPCUAClient : IDisposable
/// SessionReconnectHandler
/// </summary>
public SessionReconnectHandler ReConnectHandler => m_reConnectHandler;
/// <summary>
/// Raised when a good keep alive from the server arrives.
/// </summary>
public event EventHandler<KeepAliveEventArgs> KeepAliveComplete
{
add { m_KeepAliveComplete += value; }
remove { m_KeepAliveComplete -= value; }
}
/// <summary>
/// Raised when a reconnect operation starts.
/// </summary>
public event EventHandler ReconnectStarting
{
add { m_ReconnectStarting += value; }
remove { m_ReconnectStarting -= value; }
}
/// <summary>
/// Raised when a reconnect operation completes.
/// </summary>
public event EventHandler ReconnectComplete
{
add { m_ReconnectComplete += value; }
remove { m_ReconnectComplete -= value; }
}
/// <summary>
/// Raised after successfully connecting to or disconnecing from a server.
/// </summary>
public event EventHandler<bool> ConnectComplete
{
add { m_ConnectComplete += value; }
remove { m_ConnectComplete -= value; }
}
/// <summary>
/// Raised after the client status change
/// </summary>
public event EventHandler<OpcUaStatusEventArgs> OpcStatusChange
{
add { m_OpcStatusChange += value; }
remove { m_OpcStatusChange -= value; }
}
/// <summary>
/// 当前活动会话。
/// </summary>
@@ -231,7 +280,7 @@ public class OPCUAClient : IDisposable
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"初始化{items[i]}变量订阅失败", ex);
UpdateStatus(3, DateTime.Now, $"初始化{items[i]}变量订阅失败,错误原因:{ex}");
}
}
m_subscription.AddItems(monitoredItems);
@@ -247,9 +296,9 @@ public class OPCUAClient : IDisposable
var isError = m_subscription.MonitoredItems.Any(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode));
if (isError)
{
_logAction?.Invoke(3, this, $"创建以下变量订阅失败:{Environment.NewLine}{m_subscription.MonitoredItems.Where(
UpdateStatus(3, DateTime.Now, $"创建以下变量订阅失败:{Environment.NewLine}{m_subscription.MonitoredItems.Where(
a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode))
.Select(a => a.StartNodeId.ToString() + "" + a.Status.Error.ToString()).ToJsonString()}", null);
.Select(a => $"{a.StartNodeId.ToString()}{a.Status.Error.ToString()}").ToJsonString()}");
}
lock (dic_subscriptions)
@@ -281,7 +330,6 @@ public class OPCUAClient : IDisposable
item.Value.Delete(true);
m_session.RemoveSubscription(item.Value);
try { item.Value.Dispose(); } catch { }
}
dic_subscriptions.Clear();
}
@@ -304,39 +352,40 @@ public class OPCUAClient : IDisposable
dic_subscriptions.RemoveWhere(a => a.Key == subscriptionName);
}
}
}
private void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
{
try
{
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues())
if (m_session != null)
{
if (value.Value != null)
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues())
{
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
if (data == null && value.Value != null)
if (value.Value != null)
{
_logAction?.Invoke(3, this, $"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}", null);
var data1 = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
if (data == null && value.Value != null)
{
UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}");
var data1 = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
}
DataChangedHandler?.Invoke((variableNode, value, data));
}
else
{
var data = JValue.CreateNull();
DataChangedHandler?.Invoke((variableNode, value, data));
}
DataChangedHandler?.Invoke((variableNode, value, data));
}
else
{
var data = JValue.CreateNull();
DataChangedHandler?.Invoke((variableNode, value, data));
}
}
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"{monitoreditem.StartNodeId}订阅处理错误", ex);
UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}订阅处理错误,错误原因:" + ex);
}
}
#endregion
@@ -462,8 +511,8 @@ public class OPCUAClient : IDisposable
#endregion
#region
private ComplexTypeSystem typeSystem;
/// <summary>
@@ -472,7 +521,6 @@ public class OPCUAClient : IDisposable
public async Task ConnectAsync()
{
await ConnectAsync(OPCNode.OPCUrl);
_logAction?.Invoke(1, this, $"连接成功", null);
}
/// <summary>
@@ -484,10 +532,10 @@ public class OPCUAClient : IDisposable
// disconnect any existing session.
if (m_session != null)
{
_logAction?.Invoke(1, this, $"主动断开连接", null);
m_session = null;
}
}
/// <summary>
/// Creates a new session.
/// </summary>
@@ -516,20 +564,24 @@ public class OPCUAClient : IDisposable
//创建本地证书
await m_application.CheckApplicationInstanceCertificate(true, 0, 1200);
m_session = await Opc.Ua.Client.Session.Create(
m_configuration,
endpoint,
false,
OPCNode.CheckDomain,
(string.IsNullOrEmpty(OPCUAName)) ? m_configuration.ApplicationName : OPCUAName,
60000,
userIdentity,
Array.Empty<string>());
m_configuration,
endpoint,
false,
OPCNode.CheckDomain,
(string.IsNullOrEmpty(OPCUAName)) ? m_configuration.ApplicationName : OPCUAName,
60000,
userIdentity,
Array.Empty<string>());
typeSystem = new ComplexTypeSystem(m_session);
m_session.KeepAliveInterval = OPCNode.KeepAliveInterval == 0 ? 60000 : OPCNode.KeepAliveInterval;
m_session.KeepAlive += new KeepAliveEventHandler(Session_KeepAlive);
// raise an event.
DoConnectComplete(true);
UpdateStatus(2, DateTime.UtcNow, "Connected");
//如果是订阅模式,连接时添加订阅组
if (OPCNode.ActiveSubscribe)
await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray(), OPCNode.LoadType);
@@ -538,6 +590,9 @@ public class OPCUAClient : IDisposable
private void PrivateDisconnect()
{
bool state = m_session?.Connected == true;
if (m_reConnectHandler != null)
{
try { m_reConnectHandler.Dispose(); } catch { }
@@ -549,8 +604,14 @@ public class OPCUAClient : IDisposable
m_session.Close(10000);
}
if (state)
{
UpdateStatus(2, DateTime.UtcNow, "Disconnected");
DoConnectComplete(false);
}
}
#endregion
#region /
@@ -593,7 +654,6 @@ public class OPCUAClient : IDisposable
valuesToWrite.Add(valueToWrite);
}
var result = await m_session.WriteAsync(
requestHeader: null,
nodesToWrite: valuesToWrite, cancellationToken);
@@ -601,7 +661,6 @@ public class OPCUAClient : IDisposable
ClientBase.ValidateResponse(result.Results, valuesToWrite);
ClientBase.ValidateDiagnosticInfos(result.DiagnosticInfos, valuesToWrite);
var keys = writeInfoLists.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
@@ -624,7 +683,6 @@ public class OPCUAClient : IDisposable
}
return results;
}
}
/// <summary>
@@ -706,7 +764,6 @@ public class OPCUAClient : IDisposable
return node;
}
/// <summary>
/// 从服务器读取节点
/// </summary>
@@ -729,18 +786,14 @@ public class OPCUAClient : IDisposable
}
else
{
_logAction?.Invoke(3, this, $"获取服务器节点信息失败{nodes.Item2[i]}", null);
UpdateStatus(3, DateTime.Now, $"获取服务器节点信息失败{nodes.Item2[i]}");
}
}
return nodes.Item1.ToList();
}
#endregion
#region
/// <summary>
@@ -805,7 +858,6 @@ public class OPCUAClient : IDisposable
ResultMask = (uint)BrowseResultMask.All
};
nodesToBrowse.Add(nodeToBrowse);
}
return await ReadNoteAttributeAsync(nodesToBrowse, nodesToRead, cancellationToken);
@@ -968,11 +1020,9 @@ public class OPCUAClient : IDisposable
return nodeAttribute.ToArray();
}
#endregion
/// <inheritdoc/>
public void Dispose()
{
@@ -991,7 +1041,6 @@ public class OPCUAClient : IDisposable
throw new Exception(string.Format("验证证书失败,错误代码:{0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));
}
private async Task<Dictionary<string, List<OPCNodeAttribute>>> ReadNoteAttributeAsync(BrowseDescriptionCollection nodesToBrowse, ReadValueIdCollection nodesToRead, CancellationToken cancellationToken)
{
int startOfProperties = nodesToRead.Count;
@@ -1068,7 +1117,6 @@ public class OPCUAClient : IDisposable
}
}
if (nodeAttributes.ContainsKey(nodeToRead.NodeId.ToString()))
{
nodeAttributes[nodeToRead.NodeId.ToString()].Add(item);
@@ -1086,21 +1134,47 @@ public class OPCUAClient : IDisposable
/// </summary>
private void Server_ReconnectComplete(object sender, EventArgs e)
{
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
try
{
return;
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
{
return;
}
m_session = m_reConnectHandler.Session;
m_reConnectHandler.Dispose();
m_reConnectHandler = null;
// raise any additional notifications.
m_ReconnectComplete?.Invoke(this, e);
}
catch (Exception)
{
throw;
}
}
m_session = m_reConnectHandler.Session;
m_reConnectHandler = null;
/// <summary>
/// Report the client status
/// </summary>
/// <param name="logLevel">Whether the status represents an error. </param>
/// <param name="time">The time associated with the status.</param>
/// <param name="status">The status message.</param>
/// <param name="args">Arguments used to format the status message.</param>
private void UpdateStatus(int logLevel, DateTime time, string status, params object[] args)
{
m_OpcStatusChange?.Invoke(this, new OpcUaStatusEventArgs()
{
LogLevel = logLevel,
Time = time.ToLocalTime(),
Text = String.Format(status, args),
});
}
private void Session_KeepAlive(ISession session, KeepAliveEventArgs e)
{
lock (checkLock)
{
if (!Object.ReferenceEquals(session, m_session))
{
return;
@@ -1108,22 +1182,39 @@ public class OPCUAClient : IDisposable
if (ServiceResult.IsBad(e.Status))
{
_logAction?.Invoke(3, this, $"心跳检测错误:{e.Status}", null);
if (m_session.KeepAliveInterval <= 0)
{
UpdateStatus(3, e.CurrentTime, "Communication Error ({0})", e.Status);
return;
}
UpdateStatus(3, e.CurrentTime, "Reconnecting in {0}s", m_session.KeepAliveInterval / 1000);
if (m_reConnectHandler == null)
{
m_ReconnectStarting?.Invoke(this, e);
m_reConnectHandler = new SessionReconnectHandler();
m_reConnectHandler.BeginReconnect(m_session, m_session.KeepAliveInterval, Server_ReconnectComplete);
}
return;
}
// update status.
UpdateStatus(0, e.CurrentTime, "Session_KeepAlive Connected [{0}]", session.Endpoint.EndpointUrl);
// raise any additional notifications.
m_KeepAliveComplete?.Invoke(this, e);
}
}
/// <summary>
/// Raises the connect complete event on the main GUI thread.
/// </summary>
private void DoConnectComplete(bool state)
{
m_ConnectComplete?.Invoke(this, state);
}
#endregion
}

View File

@@ -1,4 +1,5 @@
#region copyright
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
@@ -8,11 +9,39 @@
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Opc.Ua;
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
/// OPC UA的状态更新消息
/// </summary>
public class OpcUaStatusEventArgs
{
/// <summary>
/// 日志等级,<br></br>
/// 更为详细的步骤型日志输出 Trace = 0,<br></br>
/// 调试信息日志Debug = 1,<br></br>
/// 消息类日志输出 Info = 2,<br></br>
/// 警告类日志输出 Warning = 3,<br></br>
/// 错误类日志输出 Error = 4,<br></br>
/// 不可控中断类日输出Critical = 5,
/// </summary>
public int LogLevel { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime Time { get; set; }
/// <summary>
/// 文本
/// </summary>
public string Text { get; set; }
}
/// <summary>
/// 读取属性过程中用于描述的
/// </summary>
@@ -22,6 +51,7 @@ public class OPCNodeAttribute
/// 属性的名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 操作结果状态描述
/// </summary>
@@ -31,10 +61,9 @@ public class OPCNodeAttribute
/// 属性的类型描述
/// </summary>
public string Type { get; set; }
/// <summary>
/// 属性的值,如果读取错误,返回文本描述
/// </summary>
public object Value { get; set; }
}
}

View File

@@ -11,5 +11,4 @@
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.4.372.56" />
</ItemGroup>
</Project>

View File

@@ -250,44 +250,44 @@ public class SiemensAddress : DeviceAddressBase
{
if (DataCode == (byte)S7Area.TM)
{
return "T" + Address.ToString() + (IsWString ? ";W=true;" : ";W=false;");
return $"T{Address.ToString()}{(IsWString ? ";W=true;" : ";W=false;")}";
}
if (DataCode == (byte)S7Area.CT)
{
return "C" + Address.ToString() + (IsWString ? ";W=true;" : ";W=false;");
return $"C{Address.ToString()}{(IsWString ? ";W=true;" : ";W=false;")}";
}
if (DataCode == (byte)S7Area.AI)
{
return "AI" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;");
return $"AI{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
}
if (DataCode == (byte)S7Area.AQ)
{
return "AQ" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;");
return $"AQ{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
}
if (DataCode == (byte)S7Area.PE)
{
return "I" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;");
return $"I{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
}
if (DataCode == (byte)S7Area.PA)
{
return "Q" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;");
return $"Q{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
}
if (DataCode == (byte)S7Area.MK)
{
return "M" + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;");
return $"M{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
}
return DataCode == (byte)S7Area.DB ? "DB" + DbBlock.ToString() + "." + GetStringAddress(AddressStart) + (IsWString ? ";W=true;" : ";W=false;") : Address.ToString() + (IsWString ? ";W=true;" : ";W=false;");
return DataCode == (byte)S7Area.DB ? $"DB{DbBlock.ToString()}.{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}" : Address.ToString() + (IsWString ? ";W=true;" : ";W=false;");
}
private static string GetStringAddress(int addressStart)
{
return addressStart % 8 == 0 ? (addressStart / 8).ToString() : string.Format("{0}.{1}", addressStart / 8, addressStart % 8);
return addressStart % 8 == 0 ? (addressStart / 8).ToString() : $"{addressStart / 8}.{addressStart % 8}";
}
}

View File

@@ -24,7 +24,7 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase
public ReadWriteDevicesSerialSessionBase(SerialSession serialSession)
{
SerialSession = serialSession;
WaitingClientEx = SerialSession.GetWaitingClient(new() { ThrowBreakException = true });
WaitingClientEx = SerialSession.CreateWaitingClient(new() { ThrowBreakException = true });
SerialSession.Received -= Received;
SerialSession.Connecting -= Connecting;
SerialSession.Connected -= Connected;
@@ -97,7 +97,7 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase
try
{
waitingOptions ??= new WaitingOptions { ThrowBreakException = true };
ResponsedData result = SerialSession.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken);
ResponsedData result = SerialSession.CreateWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken);
return OperResult.CreateSuccessResult(result.Data);
}
catch (Exception ex)
@@ -112,7 +112,7 @@ public abstract class ReadWriteDevicesSerialSessionBase : ReadWriteDevicesBase
try
{
waitingOptions ??= new WaitingOptions { ThrowBreakException = true };
ResponsedData result = await SerialSession.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken);
ResponsedData result = await SerialSession.CreateWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken);
return OperResult.CreateSuccessResult(result.Data);
}
catch (Exception ex)

View File

@@ -23,7 +23,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase
public ReadWriteDevicesTcpClientBase(TcpClient tcpClient)
{
TcpClient = tcpClient;
WaitingClientEx = TcpClient.GetWaitingClient(new() { ThrowBreakException = true });
WaitingClientEx = TcpClient.CreateWaitingClient(new() { ThrowBreakException = true });
TcpClient.Connecting -= Connecting;
TcpClient.Connected -= Connected;
TcpClient.Disconnecting -= Disconnecting;
@@ -87,7 +87,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase
try
{
waitingOptions ??= new WaitingOptions { ThrowBreakException = true, };
ResponsedData result = TcpClient.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken);
ResponsedData result = TcpClient.CreateWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken);
return OperResult.CreateSuccessResult(result.Data);
}
catch (Exception ex)
@@ -102,7 +102,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase
try
{
waitingOptions ??= new WaitingOptions { ThrowBreakException = true };
ResponsedData result = await TcpClient.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken);
ResponsedData result = await TcpClient.CreateWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken);
return OperResult.CreateSuccessResult(result.Data);
}
catch (Exception ex)
@@ -137,13 +137,13 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesBase
private Task Disconnected(ITcpClientBase client, DisconnectEventArgs e)
{
Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message);
Logger?.Debug($"{client.IP}:{client.Port}断开连接-{e.Message}");
return EasyTask.CompletedTask;
}
private Task Disconnecting(ITcpClientBase client, DisconnectEventArgs e)
{
Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message);
Logger?.Debug($"{client.IP}:{client.Port}正在主动断开连接-{e.Message}");
return EasyTask.CompletedTask;
}
}

View File

@@ -101,14 +101,14 @@ public abstract class ReadWriteDevicesTcpServerBase : ReadWriteDevicesBase
/// <param name="e"></param>
protected virtual Task Connected(SocketClient client, ConnectedEventArgs e)
{
Logger?.Debug(client.IP + ":" + client.Port + "连接成功");
Logger?.Debug($"{client.IP}:{client.Port}连接成功");
return EasyTask.CompletedTask;
}
/// <inheritdoc/>
protected virtual Task Connecting(SocketClient client, ConnectingEventArgs e)
{
Logger?.Debug(client.IP + ":" + client.Port + "正在连接");
Logger?.Debug($"{client.IP}:{client.Port}正在连接");
SetDataAdapter(client);
return EasyTask.CompletedTask;
}
@@ -116,14 +116,14 @@ public abstract class ReadWriteDevicesTcpServerBase : ReadWriteDevicesBase
/// <inheritdoc/>
protected virtual Task Disconnected(ITcpClientBase client, DisconnectEventArgs e)
{
Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message);
Logger?.Debug($"{client.IP}:{client.Port}断开连接-{e.Message}");
return EasyTask.CompletedTask;
}
/// <inheritdoc/>
protected virtual Task Disconnecting(ITcpClientBase client, DisconnectEventArgs e)
{
Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message);
Logger?.Debug($"{client.IP}:{client.Port}正在主动断开连接-{e.Message}");
return EasyTask.CompletedTask;
}

View File

@@ -22,7 +22,7 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase
{
UdpSession = udpSession;
SetDataAdapter();
WaitingClientEx = UdpSession.GetWaitingClient(new() { ThrowBreakException = true });
WaitingClientEx = UdpSession.CreateWaitingClient(new() { ThrowBreakException = true });
}
/// <summary>
@@ -65,7 +65,7 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase
try
{
waitingOptions ??= new WaitingOptions { ThrowBreakException = true };
ResponsedData result = UdpSession.GetWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken);
ResponsedData result = UdpSession.CreateWaitingClient(waitingOptions).SendThenResponse(data, TimeOut, cancellationToken);
return OperResult.CreateSuccessResult(result.Data);
}
catch (Exception ex)
@@ -80,7 +80,7 @@ public abstract class ReadWriteDevicesUdpSessionBase : ReadWriteDevicesBase
try
{
waitingOptions ??= new WaitingOptions { ThrowBreakException = true };
ResponsedData result = await UdpSession.GetWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken);
ResponsedData result = await UdpSession.CreateWaitingClient(waitingOptions).SendThenResponseAsync(data, TimeOut, cancellationToken);
return OperResult.CreateSuccessResult(result.Data);
}
catch (Exception ex)

View File

@@ -20,6 +20,12 @@ public sealed class EasyLock
private static long lockWaitCount;
private readonly SemaphoreSlim m_waiterLock = new SemaphoreSlim(1);
/// <inheritdoc/>
public EasyLock(bool initialState = true)
{
if (!initialState)
m_waiterLock.Wait();
}
/// <inheritdoc/>
~EasyLock()
{
m_waiterLock.SafeDispose();
@@ -54,11 +60,12 @@ public sealed class EasyLock
/// <summary>
/// 进入锁
/// </summary>
public void Wait(TimeSpan timeSpan, CancellationToken cancellationToken)
public bool Wait(TimeSpan timeSpan, CancellationToken cancellationToken)
{
Interlocked.Increment(ref lockWaitCount);
m_waiterLock.Wait(timeSpan, cancellationToken);
var data = m_waiterLock.Wait(timeSpan, cancellationToken);
Interlocked.Decrement(ref lockWaitCount);
return data;
}
/// <summary>
@@ -73,11 +80,12 @@ public sealed class EasyLock
/// <summary>
/// 进入锁
/// </summary>
public async Task WaitAsync(TimeSpan timeSpan, CancellationToken cancellationToken)
public async Task<bool> WaitAsync(TimeSpan timeSpan, CancellationToken cancellationToken)
{
Interlocked.Increment(ref lockWaitCount);
await m_waiterLock.WaitAsync(timeSpan, cancellationToken);
var data = await m_waiterLock.WaitAsync(timeSpan, cancellationToken);
Interlocked.Decrement(ref lockWaitCount);
return data;
}
}

View File

@@ -28,7 +28,7 @@ public class ByteTransformUtil
}
catch (Exception ex)
{
return new OperResult<TResult>(string.Format("{0} {1} : Length({2}) {3}", "转换失败", result.Content.ToHexString(), result.Content.Length, ex.Message));
return new OperResult<TResult>(string.Format("{0} {1} : Length({2}) {3}", "转换失败", result.Content.ToHexString(), result.Content.Length, ex));
}
}

View File

@@ -32,7 +32,7 @@ public static class JTokenUtil
}
catch (Exception)
{
tagValue = JToken.Parse("\"" + item + "\"");
tagValue = JToken.Parse($"\"{item}\"");
}
return tagValue;

View File

@@ -27,8 +27,7 @@ using System.Runtime.CompilerServices;
namespace ThingsGateway.Foundation.Core
{
/// <summary>
/// 具有释放的对象。
/// 并未实现析构函数相关。
/// 具有释放的对象。内部实现了GC.SuppressFinalize但不包括析构函数相关。
/// </summary>
public partial class DisposableObject : IDisposable
{
@@ -65,11 +64,12 @@ namespace ThingsGateway.Foundation.Core
}
/// <summary>
/// 释放资源。
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
/// </summary>
public void Dispose()
{
this.Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -72,7 +72,7 @@ namespace ThingsGateway.Foundation.Core
}
catch (Exception ex)
{
this.OnError(ex.Message, false, true);
this.OnError(ex.ToString(), false, true);
}
}
return FilterResult.GoOn;
@@ -121,7 +121,7 @@ namespace ThingsGateway.Foundation.Core
}
catch (Exception ex)
{
this.OnError(ex.Message, false, true);
this.OnError(ex.ToString(), false, true);
}
}
return FilterResult.GoOn;

View File

@@ -87,7 +87,7 @@ namespace ThingsGateway.Foundation.Core
}
catch (Exception ex)
{
this.OnError(ex.Message);
this.OnError(ex.ToString());
}
}

View File

@@ -182,7 +182,7 @@ namespace ThingsGateway.Foundation.Core
}
if (!flag && !converter.CanConvertTo(destinationType))
{
throw new InvalidOperationException("无法转换成类型:" + value.ToString() + "==>" + destinationType);
throw new InvalidOperationException($"无法转换成类型:{value.ToString()}==>{destinationType}");
}
try
{
@@ -190,7 +190,7 @@ namespace ThingsGateway.Foundation.Core
}
catch (Exception e)
{
throw new InvalidOperationException(" 类型转换出错:" + value.ToString() + "==>" + destinationType, e);
throw new InvalidOperationException($" 类型转换出错:{value.ToString()}==>{destinationType}", e);
}
return returnValue;
}

View File

@@ -70,6 +70,15 @@ namespace ThingsGateway.Foundation.Core
return isPeriod;
}
/// <summary>
/// 累计增加一个计数
/// </summary>
/// <returns></returns>
public bool Increment()
{
return this.Increment(1);
}
/// <summary>
/// 重置<see cref="Count"/>和<see cref="LastIncrement"/>
/// </summary>

View File

@@ -47,14 +47,7 @@ namespace ThingsGateway.Foundation.Core
/// </summary>
public TimeSpan Period { get; set; }
/// <summary>
/// 重置<see cref="Count"/>和<see cref="LastIncrement"/>
/// </summary>
public void Reset()
{
this.m_count = 0;
this.m_lastIncrement = default;
}
/// <summary>
/// 累计增加计数
@@ -78,5 +71,26 @@ namespace ThingsGateway.Foundation.Core
Interlocked.Add(ref this.m_count, value);
return isPeriod;
}
/// <summary>
/// 累计增加一个计数
/// </summary>
/// <returns></returns>
public bool Increment()
{
return this.Increment(1);
}
/// <summary>
/// 重置<see cref="Count"/>和<see cref="LastIncrement"/>
/// </summary>
public void Reset()
{
this.m_count = 0;
this.m_lastIncrement = default;
}
}
}

View File

@@ -133,7 +133,7 @@ namespace ThingsGateway.Foundation.Core
catch (Exception ex)
{
fileStorage = null;
msg = ex.Message;
msg = ex.ToString();
return false;
}
}

View File

@@ -134,7 +134,7 @@ namespace ThingsGateway.Foundation.Core
string path = null;
while (true)
{
path = Path.Combine(dir, $"{count:0000}" + ".log");
path = Path.Combine(dir, $"{count:0000}.log");
if (!File.Exists(path))
{
this.m_writer = FilePool.GetWriter(path);

View File

@@ -292,7 +292,13 @@ public static class LoggerExtensions
{
logger.Log(ThingsGateway.Foundation.Core.LogLevel.Error, null, msg, ex);
}
/// <summary>
/// 输出错误日志
/// </summary>
public static void LogError(this ILog logger, Exception ex)
{
logger.Log(ThingsGateway.Foundation.Core.LogLevel.Error, null, ex.Message, ex);
}
/// <summary>
/// 输出警示日志
@@ -301,7 +307,13 @@ public static class LoggerExtensions
{
logger.Log(ThingsGateway.Foundation.Core.LogLevel.Warning, null, msg, ex);
}
/// <summary>
/// 输出警示日志
/// </summary>
public static void LogWarning(this ILog logger, Exception ex)
{
logger.Log(ThingsGateway.Foundation.Core.LogLevel.Warning, null, ex.Message, ex);
}
/// <summary>
/// 输出警示日志

View File

@@ -50,13 +50,16 @@ namespace ThingsGateway.Foundation.Dmtp
#region
private ClientWebSocket m_client;
private Func<string, IDmtpActor> m_findDmtpActor;
private ValueCounter m_receiveCounter;
private ValueCounter m_sendCounter;
private SealedDmtpActor m_dmtpActor;
private TcpDmtpAdapter m_smtpAdapter;
private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
private ClientWebSocket m_client;
private SealedDmtpActor m_dmtpActor;
private Func<string, IDmtpActor> m_findDmtpActor;
private int m_receiveBufferSize = 1024 * 10;
private ValueCounter m_receiveCounter;
private int m_sendBufferSize = 1024 * 10;
private ValueCounter m_sendCounter;
private TcpDmtpAdapter m_smtpAdapter;
#endregion
/// <inheritdoc/>
@@ -90,25 +93,21 @@ namespace ThingsGateway.Foundation.Dmtp
/// <inheritdoc/>
public DateTime LastSendTime => this.m_sendCounter.LastIncrement;
/// <summary>
/// 未实现
/// </summary>
public Func<ByteBlock, bool> OnHandleRawBuffer { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <summary>
/// 未实现
/// </summary>
public Func<ByteBlock, IRequestInfo, bool> OnHandleReceivedData { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <inheritdoc/>
public IPluginsManager PluginsManager { get; private set; }
/// <inheritdoc/>
public Protocol Protocol { get; set; } = DmtpUtility.DmtpProtocol;
/// <inheritdoc/>
public override int ReceiveBufferSize => this.m_receiveBufferSize;
/// <inheritdoc/>
public IPHost RemoteIPHost { get; private set; }
/// <inheritdoc/>
public override int SendBufferSize => this.m_sendBufferSize;
/// <summary>
/// 发送<see cref="IDmtpActor"/>关闭消息。
/// </summary>
@@ -388,12 +387,12 @@ namespace ThingsGateway.Foundation.Dmtp
private void OnReceivePeriod(long value)
{
this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value);
this.m_receiveBufferSize = TouchSocketUtility.HitBufferLength(value);
}
private void OnSendPeriod(long value)
{
this.SendBufferSize = TouchSocketUtility.HitBufferLength(value);
this.m_sendBufferSize = TouchSocketUtility.HitBufferLength(value);
}
private void PrivateClose(string msg)
@@ -473,8 +472,7 @@ namespace ThingsGateway.Foundation.Dmtp
{
task = this.m_client.SendAsync(transferBytes[i], WebSocketMessageType.Binary, false, CancellationToken.None);
}
task.ConfigureAwait(false);
task.GetAwaiter().GetResult();
task.GetFalseAwaitResult();
this.m_sendCounter.Increment(transferBytes[i].Count);
}
}
@@ -522,9 +520,8 @@ namespace ThingsGateway.Foundation.Dmtp
/// <summary>
/// 不支持该功能
/// </summary>
/// <returns></returns>
/// <exception cref="NotSupportedException"></exception>
public IReceiver CreateReceiver()
public void ClearReceiver()
{
throw new NotSupportedException("不支持该功能");
}
@@ -532,12 +529,13 @@ namespace ThingsGateway.Foundation.Dmtp
/// <summary>
/// 不支持该功能
/// </summary>
/// <returns></returns>
/// <exception cref="NotSupportedException"></exception>
public void ClearReceiver()
public IReceiver CreateReceiver()
{
throw new NotSupportedException("不支持该功能");
}
#endregion
#endregion Receiver
}
}

View File

@@ -374,7 +374,7 @@ namespace ThingsGateway.Foundation.Http
/// <returns></returns>
public static TResponse SetContentTypeFromFileName<TResponse>(this TResponse response, string fileName) where TResponse : HttpResponse
{
var contentDisposition = "attachment;" + "filename=" + System.Web.HttpUtility.UrlEncode(fileName);
var contentDisposition = $"attachment;filename={System.Web.HttpUtility.UrlEncode(fileName)}";
response.Headers.Add(HttpHeaders.ContentDisposition, contentDisposition);
return response;
}
@@ -429,7 +429,7 @@ namespace ThingsGateway.Foundation.Http
using (var reader = FilePool.GetReader(filePath))
{
response.SetContentTypeByExtension(Path.GetExtension(filePath));
var contentDisposition = "attachment;" + "filename=" + System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath));
var contentDisposition = $"attachment;filename={System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath))}";
response.Headers.Add(HttpHeaders.ContentDisposition, contentDisposition);
response.Headers.Add(HttpHeaders.AcceptRanges, "bytes");
@@ -526,7 +526,7 @@ namespace ThingsGateway.Foundation.Http
using (var reader = FilePool.GetReader(filePath))
{
context.Response.SetContentTypeByExtension(Path.GetExtension(filePath));
var contentDisposition = "attachment;" + "filename=" + System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath));
var contentDisposition = $"attachment;filename={System.Web.HttpUtility.UrlEncode(fileName ?? Path.GetFileName(filePath))}";
context.Response.Headers.Add(HttpHeaders.ContentDisposition, contentDisposition);
context.Response.Headers.Add(HttpHeaders.AcceptRanges, "bytes");

View File

@@ -66,7 +66,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets
args.Context.Response.Build(byteBlock);
await client.DefaultSendAsync(byteBlock).ConfigureAwait(false);
}
await client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakedPlugin.OnWebSocketHandshaked), client, new HttpContextEventArgs(httpContext))
_ = client.PluginsManager.RaiseAsync(nameof(IWebSocketHandshakedPlugin.OnWebSocketHandshaked), client, new HttpContextEventArgs(httpContext))
.ConfigureAwait(false);
return true;
}

View File

@@ -101,7 +101,7 @@ namespace ThingsGateway.Foundation.Http.WebSockets
if (await this.VerifyConnection.Invoke(client, e.Context))
{
e.Handled = true;
_ = client.SwitchProtocolToWebSocket(e.Context);
await client.SwitchProtocolToWebSocket(e.Context);
return;
}
}

View File

@@ -130,7 +130,7 @@ namespace ThingsGateway.Foundation.Rpc
catch (TargetInvocationException ex)
{
invokeResult.Status = InvokeStatus.InvocationException;
invokeResult.Message = ex.InnerException != null ? "函数内部发生异常,信息:" + ex.InnerException.Message : "函数内部发生异常,信息:未知";
invokeResult.Message = ex.InnerException != null ? $"函数内部发生异常,信息:{ex.InnerException.Message}" : "函数内部发生异常,信息:未知";
if (callContext.MethodInstance.Filters != null)
{
for (var i = 0; i < callContext.MethodInstance.Filters.Length; i++)
@@ -223,7 +223,7 @@ namespace ThingsGateway.Foundation.Rpc
catch (TargetInvocationException ex)
{
invokeResult.Status = InvokeStatus.InvocationException;
invokeResult.Message = ex.InnerException != null ? "函数内部发生异常,信息:" + ex.InnerException.Message : "函数内部发生异常,信息:未知";
invokeResult.Message = ex.InnerException != null ? $"函数内部发生异常,信息:{ex.InnerException.Message}" : "函数内部发生异常,信息:未知";
if (callContext.MethodInstance.Filters != null)
{
for (var i = 0; i < callContext.MethodInstance.Filters.Length; i++)

View File

@@ -21,22 +21,12 @@ public abstract class BaseSerial : DependencyObject, ISerial
/// 同步根。
/// </summary>
protected readonly object SyncRoot = new object();
private int m_receiveBufferSize = 1024 * 10;
private int m_sendBufferSize = 1024 * 10;
/// <inheritdoc/>
public virtual int SendBufferSize
{
get => this.m_sendBufferSize;
set => this.m_sendBufferSize = value < 1024 ? 1024 : value;
}
public abstract int SendBufferSize { get; }
/// <inheritdoc/>
public virtual int ReceiveBufferSize
{
get => this.m_receiveBufferSize;
set => this.m_receiveBufferSize = value < 1024 ? 1024 : value;
}
public abstract int ReceiveBufferSize { get; }
/// <inheritdoc/>
public ILog Logger { get; set; }

View File

@@ -24,9 +24,14 @@ internal sealed class InternalSerialCore : SerialCore
public class SerialCore : IDisposable, ISender
{
/// <summary>
/// 初始缓存大小
/// 最小缓存尺寸
/// </summary>
public const int BufferSize = 1024 * 10;
public int MinBufferSize { get; set; } = 1024 * 10;
/// <summary>
/// 最大缓存尺寸
/// </summary>
public int MaxBufferSize { get; set; } = 1024 * 1024 * 10;
#region
/// <summary>
@@ -35,14 +40,15 @@ public class SerialCore : IDisposable, ISender
public readonly object SyncRoot = new object();
private long m_bufferRate;
private bool m_disposedValue;
private SpinLock m_lock;
private bool m_online => MainSerialPort?.IsOpen == true;
private int m_receiveBufferSize = BufferSize;
private bool m_online => m_serialPort?.IsOpen == true;
private int m_receiveBufferSize = 1024 * 10;
private ValueCounter m_receiveCounter;
private int m_sendBufferSize = BufferSize;
private int m_sendBufferSize = 1024 * 10;
private ValueCounter m_sendCounter;
private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
private readonly EasyLock m_semaphore = new EasyLock();
private SerialPort m_serialPort;
#endregion
/// <summary>
@@ -69,7 +75,7 @@ public class SerialCore : IDisposable, ISender
/// </summary>
~SerialCore()
{
this.Dispose(disposing: false);
this.SafeDispose();
}
/// <inheritdoc/>
@@ -100,19 +106,11 @@ public class SerialCore : IDisposable, ISender
public Action<SerialCore, ByteBlock> OnReceived { get; set; }
/// <summary>
/// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整
/// 接收缓存池,运行时的值会根据流速自动调整
/// </summary>
public int ReceiveBufferSize
{
get => this.m_receiveBufferSize;
set
{
this.m_receiveBufferSize = value;
if (this.MainSerialPort != null && !MainSerialPort.IsOpen)
{
this.MainSerialPort.ReadBufferSize = value;
}
}
}
/// <summary>
@@ -121,19 +119,11 @@ public class SerialCore : IDisposable, ISender
public ValueCounter ReceiveCounter { get => this.m_receiveCounter; }
/// <summary>
/// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整
/// 发送缓存池,运行时的值会根据流速自动调整
/// </summary>
public int SendBufferSize
{
get => this.m_sendBufferSize;
set
{
this.m_sendBufferSize = value;
if (this.MainSerialPort != null && !MainSerialPort.IsOpen)
{
this.MainSerialPort.WriteBufferSize = value;
}
}
}
/// <summary>
@@ -144,7 +134,7 @@ public class SerialCore : IDisposable, ISender
/// <summary>
/// SerialPort
/// </summary>
public SerialPort MainSerialPort { get; private set; }
public SerialPort MainSerialPort { get => this.m_serialPort; }
/// <summary>
@@ -155,11 +145,12 @@ public class SerialCore : IDisposable, ISender
var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize);
this.UserToken = byteBlock;
byteBlock.SetLength(0);
if (this.MainSerialPort.BytesToRead > 0)
if (this.m_serialPort.BytesToRead > 0)
{
this.m_bufferRate += 2;
this.ProcessReceived();
}
MainSerialPort.DataReceived += MainSerialPort_DataReceived;
m_serialPort.DataReceived += MainSerialPort_DataReceived;
}
private void MainSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
@@ -171,7 +162,7 @@ public class SerialCore : IDisposable, ISender
}
catch (Exception ex)
{
this.PrivateBreakOut(false, ex.Message);
this.PrivateBreakOut(false, ex.ToString());
}
}
@@ -189,12 +180,12 @@ public class SerialCore : IDisposable, ISender
/// </summary>
public void Dispose()
{
this.Dispose(disposing: true);
GC.SuppressFinalize(this);
UserToken.SafeDispose();
}
/// <summary>
/// 重置环境,并设置新的<see cref="MainSerialPort"/>。
/// 重置环境,并设置新的<see cref="m_serialPort"/>。
/// </summary>
/// <param name="socket"></param>
public virtual void Reset(SerialPort socket)
@@ -209,7 +200,7 @@ public class SerialCore : IDisposable, ISender
throw new Exception("新的SerialPort必须在连接状态。");
}
this.Reset();
this.MainSerialPort = socket;
this.m_serialPort = socket;
}
/// <summary>
@@ -219,14 +210,14 @@ public class SerialCore : IDisposable, ISender
{
this.m_receiveCounter.Reset();
this.m_sendCounter.Reset();
this.MainSerialPort = null;
this.m_serialPort = null;
this.OnReceived = null;
this.OnBreakOut = null;
this.UserToken = null;
this.m_bufferRate = 1;
this.m_lock = new SpinLock();
this.m_receiveBufferSize = BufferSize;
this.m_sendBufferSize = BufferSize;
this.m_receiveBufferSize = this.MinBufferSize;
this.m_sendBufferSize = this.MinBufferSize;
}
/// <summary>
@@ -244,7 +235,7 @@ public class SerialCore : IDisposable, ISender
try
{
this.m_lock.Enter(ref lockTaken);
this.MainSerialPort.Write(buffer, offset, length);
this.m_serialPort.Write(buffer, offset, length);
}
finally
{
@@ -267,7 +258,7 @@ public class SerialCore : IDisposable, ISender
{
await this.m_semaphore.WaitAsync();
this.MainSerialPort.Write(buffer, offset, length);
this.m_serialPort.Write(buffer, offset, length);
}
finally
{
@@ -287,22 +278,7 @@ public class SerialCore : IDisposable, ISender
this.OnBreakOut?.Invoke(this, manual, msg);
}
/// <summary>
/// 释放对象
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!this.m_disposedValue)
{
if (disposing)
{
}
this.m_disposedValue = true;
}
UserToken.SafeDispose();
}
/// <summary>
/// 当发生异常的时候
@@ -341,12 +317,20 @@ public class SerialCore : IDisposable, ISender
private void OnReceivePeriod(long value)
{
this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value);
this.m_receiveBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize);
if (this.MainSerialPort != null && !MainSerialPort.IsOpen)
{
this.MainSerialPort.ReadBufferSize = this.m_receiveBufferSize;
}
}
private void OnSendPeriod(long value)
{
this.SendBufferSize = TouchSocketUtility.HitBufferLength(value);
this.m_sendBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize);
if (this.MainSerialPort != null && !MainSerialPort.IsOpen)
{
this.MainSerialPort.WriteBufferSize = this.m_sendBufferSize;
}
}
private void PrivateBreakOut(bool manual, string msg)
@@ -367,21 +351,21 @@ public class SerialCore : IDisposable, ISender
UserToken?.SafeDispose();
return;
}
if (MainSerialPort.BytesToRead > 0)
if (m_serialPort.BytesToRead > 0)
{
var byteBlock = UserToken;
byte[] buffer = BytePool.Default.Rent(MainSerialPort.BytesToRead);
int num = MainSerialPort.Read(buffer, 0, MainSerialPort.BytesToRead);
byte[] buffer = BytePool.Default.Rent(m_serialPort.BytesToRead);
int num = m_serialPort.Read(buffer, 0, m_serialPort.BytesToRead);
byteBlock.Write(buffer, 0, num);
byteBlock.SetLength(num);
this.HandleBuffer(byteBlock);
try
{
var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength));
var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, this.MaxBufferSize));
newByteBlock.SetLength(0);
UserToken = newByteBlock;
if (MainSerialPort.BytesToRead > 0)
if (m_serialPort.BytesToRead > 0)
{
this.m_bufferRate += 2;
this.ProcessReceived();
@@ -389,7 +373,7 @@ public class SerialCore : IDisposable, ISender
}
catch (Exception ex)
{
this.PrivateBreakOut(false, ex.Message);
this.PrivateBreakOut(false, ex.ToString());
}
}
}

View File

@@ -57,7 +57,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession
private DelaySender m_delaySender;
private bool m_online => MainSerialPort?.IsOpen == true;
private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
private readonly EasyLock m_semaphore = new();
private readonly InternalSerialCore m_serialCore;
#endregion
@@ -204,10 +204,10 @@ public class SerialSessionBase : BaseSerial, ISerialSession
#region
/// <inheritdoc/>
public DateTime LastReceivedTime => this.GetTcpCore().ReceiveCounter.LastIncrement;
public DateTime LastReceivedTime => this.GetSerialCore().ReceiveCounter.LastIncrement;
/// <inheritdoc/>
public DateTime LastSendTime => this.GetTcpCore().SendCounter.LastIncrement;
public DateTime LastSendTime => this.GetSerialCore().SendCounter.LastIncrement;
/// <inheritdoc/>
public IContainer Container { get; private set; }
@@ -321,7 +321,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession
private void BeginReceive()
{
this.GetTcpCore().BeginIocpReceive();
this.GetSerialCore().BeginIocpReceive();
}
@@ -375,32 +375,22 @@ public class SerialSessionBase : BaseSerial, ISerialSession
}
}
private SerialCore GetTcpCore()
private SerialCore GetSerialCore()
{
this.ThrowIfDisposed();
return this.m_serialCore ?? throw new ObjectDisposedException(this.GetType().Name);
}
/// <inheritdoc/>
public override int ReceiveBufferSize
{
get => this.GetTcpCore().ReceiveBufferSize;
set
{
this.GetTcpCore().ReceiveBufferSize = value;
}
get => this.GetSerialCore().ReceiveBufferSize;
}
/// <inheritdoc/>
public override int SendBufferSize
{
get => this.GetTcpCore().SendBufferSize;
set
{
this.GetTcpCore().SendBufferSize = value;
}
get => this.GetSerialCore().SendBufferSize;
}
/// <inheritdoc/>
@@ -728,7 +718,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession
{
if (this.SendingData(buffer, offset, length).GetFalseAwaitResult())
{
this.GetTcpCore().Send(buffer, offset, length);
this.GetSerialCore().Send(buffer, offset, length);
}
}
@@ -737,7 +727,7 @@ public class SerialSessionBase : BaseSerial, ISerialSession
{
if (await this.SendingData(buffer, offset, length))
{
await this.GetTcpCore().SendAsync(buffer, offset, length);
await this.GetSerialCore().SendAsync(buffer, offset, length);
}
}
@@ -770,7 +760,15 @@ public class SerialSessionBase : BaseSerial, ISerialSession
this.m_serialCore.Reset(serialPort);
this.m_serialCore.OnReceived = this.HandleReceived;
this.m_serialCore.OnBreakOut = this.BreakOut;
if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue)
{
this.m_serialCore.MinBufferSize = minValue;
}
if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue)
{
this.m_serialCore.MaxBufferSize = maxValue;
}
}
private void HandleReceived(SerialCore core, ByteBlock byteBlock)

View File

@@ -35,22 +35,11 @@ namespace ThingsGateway.Foundation.Sockets
/// </summary>
protected readonly object SyncRoot = new object();
private int m_receiveBufferSize = 1024 * 10;
private int m_sendBufferSize = 1024 * 10;
/// <inheritdoc/>
public abstract int SendBufferSize { get; }
/// <inheritdoc/>
public virtual int SendBufferSize
{
get => this.m_sendBufferSize;
set => this.m_sendBufferSize = value < 1024 ? 1024 : value;
}
/// <inheritdoc/>
public virtual int ReceiveBufferSize
{
get => this.m_receiveBufferSize;
set => this.m_receiveBufferSize = value < 1024 ? 1024 : value;
}
public abstract int ReceiveBufferSize { get; }
/// <inheritdoc/>
public ILog Logger { get; set; }

View File

@@ -108,10 +108,6 @@ namespace ThingsGateway.Foundation.Sockets
}
}
/// <summary>
/// 最大BufferLength
/// </summary>
public static int MaxBufferLength { get; set; } = 1024 * 1024 * 10;
/// <summary>
/// 命中BufferLength

View File

@@ -24,9 +24,15 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
private const string m_msg1 = "远程终端主动关闭";
/// <summary>
/// 初始缓存大小
/// 最小缓存尺寸
/// </summary>
public const int BufferSize = 1024 * 10;
public int MinBufferSize { get; set; } = 1024 * 10;
/// <summary>
/// 最大缓存尺寸
/// </summary>
public int MaxBufferSize { get; set; } = 1024 * 1024 * 10;
#region
/// <summary>
@@ -35,14 +41,14 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
public readonly object SyncRoot = new object();
private long m_bufferRate;
private bool m_disposedValue;
private SpinLock m_lock;
private volatile bool m_online;
private int m_receiveBufferSize = BufferSize;
private int m_receiveBufferSize = 1024 * 10;
private ValueCounter m_receiveCounter;
private int m_sendBufferSize = BufferSize;
private int m_sendBufferSize = 1024 * 10;
private ValueCounter m_sendCounter;
private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
private Socket m_socket;
private readonly EasyLock m_semaphore = new();
#endregion
/// <summary>
@@ -64,14 +70,6 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
};
}
/// <summary>
/// 析构函数
/// </summary>
~TcpCore()
{
this.Dispose(disposing: false);
}
/// <inheritdoc/>
public bool CanSend => this.m_online;
@@ -96,16 +94,11 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
public Action<TcpCore, ByteBlock> OnReceived { get; set; }
/// <summary>
/// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整
/// 接收缓存池,运行时的值会根据流速自动调整
/// </summary>
public int ReceiveBufferSize
{
get => this.m_receiveBufferSize;
set
{
this.m_receiveBufferSize = value;
this.Socket.ReceiveBufferSize = value;
}
}
/// <summary>
@@ -114,16 +107,11 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
public ValueCounter ReceiveCounter { get => this.m_receiveCounter; }
/// <summary>
/// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整
/// 发送缓存池,运行时的值会根据流速自动调整
/// </summary>
public int SendBufferSize
{
get => this.m_sendBufferSize;
set
{
this.m_sendBufferSize = value;
this.Socket.SendBufferSize = value;
}
}
/// <summary>
@@ -134,7 +122,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
/// <summary>
/// Socket
/// </summary>
public Socket Socket { get; private set; }
public Socket Socket { get => this.m_socket; }
/// <summary>
/// 提供一个用于客户端-服务器通信的流,该流使用安全套接字层 (SSL) 安全协议对服务器和(可选)客户端进行身份验证。
@@ -152,7 +140,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
/// <param name="sslOption"></param>
public virtual void Authenticate(ServiceSslOption sslOption)
{
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false);
sslStream.AuthenticateAsServer(sslOption.Certificate);
this.SslStream = sslStream;
@@ -165,7 +153,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
/// <param name="sslOption"></param>
public virtual void Authenticate(ClientSslOption sslOption)
{
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false);
if (sslOption.ClientCertificates == null)
{
sslStream.AuthenticateAsClient(sslOption.TargetHost);
@@ -185,7 +173,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
/// <returns></returns>
public virtual async Task AuthenticateAsync(ServiceSslOption sslOption)
{
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false);
await sslStream.AuthenticateAsServerAsync(sslOption.Certificate);
this.SslStream = sslStream;
@@ -199,7 +187,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
/// <returns></returns>
public virtual async Task AuthenticateAsync(ClientSslOption sslOption)
{
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.m_socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.m_socket, false), false);
if (sslOption.ClientCertificates == null)
{
await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost);
@@ -220,8 +208,9 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize);
this.UserToken = byteBlock;
this.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity);
if (!this.Socket.ReceiveAsync(this))
if (!this.m_socket.ReceiveAsync(this))
{
this.m_bufferRate += 2;
this.ProcessReceived(this);
}
}
@@ -257,7 +246,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
catch (Exception ex)
{
byteBlock.Dispose();
this.PrivateBreakOut(false, ex.Message);
this.PrivateBreakOut(false, ex.ToString());
}
}
}
@@ -271,15 +260,6 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
this.PrivateBreakOut(true, msg);
}
/// <summary>
/// 释放对象
/// </summary>
public new void Dispose()
{
// 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
this.Dispose(disposing: true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 重置环境,并设置新的<see cref="Socket"/>。
@@ -298,7 +278,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
}
this.Reset();
this.m_online = true;
this.Socket = socket;
this.m_socket = socket;
}
/// <summary>
@@ -310,14 +290,14 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
this.m_sendCounter.Reset();
this.SslStream?.Dispose();
this.SslStream = null;
this.Socket = null;
this.m_socket = null;
this.OnReceived = null;
this.OnBreakOut = null;
this.UserToken = null;
this.m_bufferRate = 1;
this.m_lock = new SpinLock();
this.m_receiveBufferSize = BufferSize;
this.m_sendBufferSize = BufferSize;
this.m_receiveBufferSize = this.MinBufferSize;
this.m_sendBufferSize = this.MinBufferSize;
this.m_online = false;
}
@@ -344,7 +324,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
this.m_lock.Enter(ref lockTaken);
while (length > 0)
{
var r = this.Socket.Send(buffer, offset, length, SocketFlags.None);
var r = this.m_socket.Send(buffer, offset, length, SocketFlags.None);
if (r == 0 && length > 0)
{
throw new Exception("发送数据不完全");
@@ -387,7 +367,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
while (length > 0)
{
var r = await this.Socket.SendAsync(new ArraySegment<byte>(buffer, offset, length), SocketFlags.None, CancellationToken.None);
var r = await this.m_socket.SendAsync(new ArraySegment<byte>(buffer, offset, length), SocketFlags.None, CancellationToken.None);
if (r == 0 && length > 0)
{
throw new Exception("发送数据不完全");
@@ -414,7 +394,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
while (length > 0)
{
var r = this.Socket.Send(buffer, offset, length, SocketFlags.None);
var r = this.m_socket.Send(buffer, offset, length, SocketFlags.None);
if (r == 0 && length > 0)
{
throw new Exception("发送数据不完全");
@@ -443,23 +423,6 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
this.OnBreakOut?.Invoke(this, manual, msg);
}
/// <summary>
/// 释放对象
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!this.m_disposedValue)
{
if (disposing)
{
}
this.m_disposedValue = true;
}
base.Dispose();
}
/// <summary>
/// 当发生异常的时候
/// </summary>
@@ -481,7 +444,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
}
catch (Exception ex)
{
this.PrivateBreakOut(false, ex.Message);
this.PrivateBreakOut(false, ex.ToString());
}
}
}
@@ -514,12 +477,20 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
private void OnReceivePeriod(long value)
{
this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value);
this.m_receiveBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize);
if (this.m_socket != null)
{
this.m_socket.ReceiveBufferSize = this.m_receiveBufferSize;
}
}
private void OnSendPeriod(long value)
{
this.SendBufferSize = TouchSocketUtility.HitBufferLength(value);
this.m_sendBufferSize = Math.Max(TouchSocketUtility.HitBufferLength(value), this.MinBufferSize);
if (this.m_socket != null)
{
this.m_socket.SendBufferSize = this.m_sendBufferSize;
}
}
private void PrivateBreakOut(bool manual, string msg)
@@ -548,11 +519,11 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
this.HandleBuffer(byteBlock);
try
{
var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength));
var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, this.MaxBufferSize));
e.UserToken = newByteBlock;
e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity);
if (!this.Socket.ReceiveAsync(e))
if (!this.m_socket.ReceiveAsync(e))
{
this.m_bufferRate += 2;
this.ProcessReceived(e);
@@ -560,7 +531,7 @@ public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
}
catch (Exception ex)
{
this.PrivateBreakOut(false, ex.Message);
this.PrivateBreakOut(false, ex.ToString());
}
}
else

View File

@@ -149,7 +149,7 @@ namespace ThingsGateway.Foundation.Sockets
}
catch (Exception ex)
{
this.BreakOut(default, false, ex.Message);
this.BreakOut(default, false, ex.ToString());
}
}
@@ -227,6 +227,15 @@ namespace ThingsGateway.Foundation.Sockets
tcpCore.Reset(socket);
tcpCore.OnReceived = this.HandleReceived;
tcpCore.OnBreakOut = this.BreakOut;
if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue)
{
tcpCore.MinBufferSize = minValue;
}
if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue)
{
tcpCore.MaxBufferSize = maxValue;
}
this.m_tcpCore = tcpCore;
}
@@ -398,20 +407,12 @@ namespace ThingsGateway.Foundation.Sockets
public override int ReceiveBufferSize
{
get => this.GetTcpCore().ReceiveBufferSize;
set
{
this.GetTcpCore().ReceiveBufferSize = value;
}
}
/// <inheritdoc/>
public override int SendBufferSize
{
get => this.GetTcpCore().SendBufferSize;
set
{
this.GetTcpCore().SendBufferSize = value;
}
}
/// <inheritdoc/>

View File

@@ -71,7 +71,7 @@ namespace ThingsGateway.Foundation.Sockets
private DelaySender m_delaySender;
private volatile bool m_online;
private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
private readonly EasyLock m_semaphore = new();
private readonly InternalTcpCore m_tcpCore;
#endregion
@@ -334,10 +334,7 @@ namespace ThingsGateway.Foundation.Sockets
var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。");
this.MainSocket.SafeDispose();
var socket = this.CreateSocket(iPHost);
this.PrivateOnConnecting(new ConnectingEventArgs(socket))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
this.PrivateOnConnecting(new ConnectingEventArgs(socket)).GetFalseAwaitResult();
if (timeout == 5000)
{
socket.Connect(iPHost.Host, iPHost.Port);
@@ -500,6 +497,11 @@ namespace ThingsGateway.Foundation.Sockets
#endregion
/// <inheritdoc/>
public override string ToString()
{
return this.GetIPPort();
}
private void BreakOut(TcpCore core, bool manual, string msg)
{
lock (this.SyncRoot)
@@ -526,20 +528,12 @@ namespace ThingsGateway.Foundation.Sockets
public override int ReceiveBufferSize
{
get => this.GetTcpCore().ReceiveBufferSize;
set
{
this.GetTcpCore().ReceiveBufferSize = value;
}
}
/// <inheritdoc/>
public override int SendBufferSize
{
get => this.GetTcpCore().SendBufferSize;
set
{
this.GetTcpCore().SendBufferSize = value;
}
}
/// <inheritdoc/>
@@ -942,7 +936,15 @@ namespace ThingsGateway.Foundation.Sockets
this.m_tcpCore.Reset(socket);
this.m_tcpCore.OnReceived = this.HandleReceived;
this.m_tcpCore.OnBreakOut = this.BreakOut;
if (this.Config.GetValue(TouchSocketConfigExtension.MinBufferSizeProperty) is int minValue)
{
this.m_tcpCore.MinBufferSize = minValue;
}
if (this.Config.GetValue(TouchSocketConfigExtension.MaxBufferSizeProperty) is int maxValue)
{
this.m_tcpCore.MaxBufferSize = maxValue;
}
}
private void HandleReceived(TcpCore core, ByteBlock byteBlock)

View File

@@ -31,6 +31,12 @@ namespace ThingsGateway.Foundation.Sockets
/// </summary>
public class TcpService<TClient> : TcpServiceBase, ITcpService<TClient> where TClient : SocketClient, new()
{
/// <inheritdoc/>
public override int SendBufferSize => 1024 * 1024 * 10;
/// <inheritdoc/>
public override int ReceiveBufferSize => 1024 * 1024 * 10;
/// <summary>
/// 构造函数
/// </summary>
@@ -203,7 +209,7 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="e"></param>
protected virtual Task OnDisconnecting(TClient socketClient, DisconnectEventArgs e)
{
if (this.Disconnected != null)
if (this.Disconnecting != null)
{
return this.Disconnecting.Invoke(socketClient, e);
}
@@ -477,7 +483,7 @@ namespace ThingsGateway.Foundation.Sockets
{
this.m_serverState = ServerState.Exception;
this.m_pluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.m_serverState, ex) { Message = ex.Message });
this.m_pluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.m_serverState, ex) { Message = ex.ToString() });
throw;
}
}

View File

@@ -155,6 +155,11 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="tcpCore"></param>
public void ReturnTcpCore(TcpCore tcpCore)
{
if (this.DisposedValue)
{
tcpCore.SafeDispose();
return;
}
this.m_tcpCores.Push(tcpCore);
}

View File

@@ -69,10 +69,14 @@ namespace ThingsGateway.Foundation.Sockets
this.Protocol = Protocol.Udp;
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
this.Monitor = new UdpNetworkMonitor(null, socket);
this.ReceiveBufferSize = 1024 * 64;
this.SendBufferSize = 1024 * 64;
}
/// <inheritdoc/>
public override int ReceiveBufferSize => 64 * 1024;
/// <inheritdoc/>
public override int SendBufferSize => 64 * 1024;
/// <summary>
/// <inheritdoc/>
/// </summary>
@@ -332,7 +336,7 @@ namespace ThingsGateway.Foundation.Sockets
catch (Exception ex)
{
this.ServerState = ServerState.Exception;
this.PluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.ServerState, ex) { Message = ex.Message });
this.PluginsManager.Raise(nameof(IServerStartedPlugin.OnServerStarted), this, new ServiceStateEventArgs(this.ServerState, ex) { Message = ex.ToString() });
throw;
}
}

View File

@@ -55,7 +55,7 @@ namespace ThingsGateway.Foundation.Sockets
}
catch (Exception ex)
{
this.OnError(ex.Message);
this.OnError(ex.ToString());
}
}

View File

@@ -173,7 +173,7 @@ namespace ThingsGateway.Foundation.Sockets
}
catch (Exception ex)
{
return new Result(ResultCode.Exception, ex.Message);
return new Result(ResultCode.Exception, ex.ToString());
}
}
@@ -193,7 +193,7 @@ namespace ThingsGateway.Foundation.Sockets
}
catch (Exception ex)
{
return new Result(ResultCode.Exception, ex.Message);
return new Result(ResultCode.Exception, ex.ToString());
}
}

View File

@@ -24,7 +24,6 @@
//------------------------------------------------------------------------------
using System.Net;
using System.Text;
namespace ThingsGateway.Foundation.Sockets
{
/// <summary>
@@ -358,5 +357,145 @@ namespace ThingsGateway.Foundation.Sockets
}
#endregion IUdpClientSender
#region IWaitSender
/// <summary>
/// 发送字节流
/// </summary>
/// <param name="client"></param>
/// <param name="msg"></param>
/// <param name="timeout"></param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static byte[] SendThenReturn(this IWaitSender client, string msg, int timeout = 5000)
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenReturn(Encoding.UTF8.GetBytes(msg), tokenSource.Token);
}
}
/// <summary>
/// 发送字节流
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout"></param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static byte[] SendThenReturn(this IWaitSender client, byte[] buffer, int timeout = 5000)
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenReturn(buffer, 0, buffer.Length, tokenSource.Token);
}
}
/// <summary>
/// 发送流中的有效数据
/// </summary>
/// <param name="client"></param>
/// <param name="byteBlock">数据块载体</param>
/// <param name="timeout"></param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static byte[] SendThenReturn(this IWaitSender client, ByteBlock byteBlock, int timeout = 5000)
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, tokenSource.Token);
}
}
/// <summary>
/// 异步发送
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout"></param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<byte[]> SendThenReturnAsync(this IWaitSender client, byte[] buffer, int timeout = 5000)
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenReturnAsync(buffer, 0, buffer.Length, tokenSource.Token);
}
}
/// <summary>
/// 异步发送
/// </summary>
/// <param name="client"></param>
/// <param name="msg"></param>
/// <param name="timeout"></param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<byte[]> SendThenReturnAsync(this IWaitSender client, string msg, int timeout = 5000)
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenReturnAsync(Encoding.UTF8.GetBytes(msg), tokenSource.Token);
}
}
/// <summary>
/// 发送字节流
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static byte[] SendThenReturn(this IWaitSender client, byte[] buffer, CancellationToken token)
{
return client.SendThenReturn(buffer, 0, buffer.Length, token);
}
/// <summary>
/// 发送流中的有效数据
/// </summary>
/// <param name="client"></param>
/// <param name="byteBlock">数据块载体</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static byte[] SendThenReturn(this IWaitSender client, ByteBlock byteBlock, CancellationToken token)
{
return client.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, token);
}
/// <summary>
/// 异步发送
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<byte[]> SendThenReturnAsync(this IWaitSender client, byte[] buffer, CancellationToken token)
{
return client.SendThenReturnAsync(buffer, 0, buffer.Length, token);
}
#endregion
}
}

View File

@@ -51,6 +51,54 @@ namespace ThingsGateway.Foundation.Sockets
public static readonly DependencyProperty<Func<UdpDataHandlingAdapter>> UdpDataHandlingAdapterProperty = DependencyProperty<Func<UdpDataHandlingAdapter>>.Register("UdpDataHandlingAdapter", () => { return new NormalUdpDataHandlingAdapter(); });
/// <summary>
/// 最小缓存池尺寸
/// 所需类型<see cref="int"/>
/// </summary>
public static readonly DependencyProperty<int?> MinBufferSizeProperty = DependencyProperty<int?>.Register("MinBufferSize", default);
/// <summary>
/// 最大缓存池尺寸
/// 所需类型<see cref="int"/>
/// </summary>
public static readonly DependencyProperty<int?> MaxBufferSizeProperty = DependencyProperty<int?>.Register("MinBufferSize", default);
/// <summary>
/// 最小缓存容量,默认缺省。
/// <list type="number">
/// </list>
/// </summary>
/// <param name="config"></param>
/// <param name="value"></param>
/// <returns></returns>
public static TouchSocketConfig MinBufferSize(this TouchSocketConfig config, int value)
{
if (value < 1024)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "数值不能小于1024");
}
config.SetValue(MinBufferSizeProperty, value);
return config;
}
/// <summary>
/// 最大缓存容量,默认缺省。
/// <list type="number">
/// </list>
/// </summary>
/// <param name="config"></param>
/// <param name="value"></param>
/// <returns></returns>
public static TouchSocketConfig MaxBufferSize(this TouchSocketConfig config, int value)
{
if (value < 1024)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "数值不能小于1024");
}
config.SetValue(MaxBufferSizeProperty, value);
return config;
}
/// <summary>
/// 发送超时设定单位毫秒默认为0。意为禁用该配置。
/// </summary>

View File

@@ -36,37 +36,14 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="buffer">数据缓存区</param>
/// <param name="offset">偏移</param>
/// <param name="length">长度</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
byte[] SendThenReturn(byte[] buffer, int offset, int length, CancellationToken token);
/// <summary>
/// 发送字节流
/// </summary>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
/// <summary>
/// 发送流中的有效数据
/// </summary>
/// <param name="byteBlock">数据块载体</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
/// <summary>
/// 异步发送
@@ -74,36 +51,13 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="buffer">数据缓存区</param>
/// <param name="offset">偏移</param>
/// <param name="length">长度</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, CancellationToken token);
/// <summary>
/// 异步发送
/// </summary>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
Task<byte[]> SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
/// <summary>
/// 异步发送
/// </summary>
/// <param name="byteBlock">数据块载体</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
Task<byte[]> SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
}
}

View File

@@ -33,12 +33,12 @@ namespace ThingsGateway.Foundation.Sockets
/// <summary>
/// 发送缓存区大小。最小值=1024。
/// </summary>
int SendBufferSize { get; set; }
int SendBufferSize { get; }
/// <summary>
/// 接收缓存区大小。最小值=1024。
/// </summary>
int ReceiveBufferSize { get; set; }
int ReceiveBufferSize { get; }
/// <summary>
/// 日志记录器

View File

@@ -31,6 +31,24 @@ namespace ThingsGateway.Foundation.Sockets
[PluginOption(Singleton = true, NotRegister = true)]
public sealed class ReconnectionPlugin<TClient> : PluginBase where TClient : class, ITcpClient
{
/// <summary>
/// 重连插件
/// </summary>
public ReconnectionPlugin()
{
this.ActionForConnect = async (c) =>
{
try
{
await c.ConnectAsync();
return true;
}
catch
{
return false;
}
};
}
/// <inheritdoc/>
protected override void Loaded(IPluginsManager pluginsManager)
{
@@ -134,6 +152,21 @@ namespace ThingsGateway.Foundation.Sockets
return this;
}
/// <summary>
/// 每个周期可执行的委托。返回值为True标识客户端存活。返回False表示失活立即重连。返回null时表示跳过此次检验。
/// </summary>
/// <param name="actionForCheck"></param>
/// <returns></returns>
public ReconnectionPlugin<TClient> SetActionForCheck(Func<TClient, int, bool?> actionForCheck)
{
this.ActionForCheck = async (c, i) =>
{
await EasyTask.CompletedTask;
return actionForCheck.Invoke(c, i);
};
return this;
}
/// <summary>
/// 设置连接动作
/// </summary>
@@ -145,6 +178,21 @@ namespace ThingsGateway.Foundation.Sockets
return this;
}
/// <summary>
/// 设置连接动作
/// </summary>
/// <param name="tryConnect"></param>
/// <returns>无论如何只要返回True则结束本轮尝试</returns>
public ReconnectionPlugin<TClient> SetConnectAction(Func<TClient, bool> tryConnect)
{
this.ActionForConnect = async (c) =>
{
await EasyTask.CompletedTask;
return tryConnect.Invoke(c);
};
return this;
}
/// <summary>
/// 检验时间间隔
/// </summary>

View File

@@ -121,7 +121,7 @@ namespace ThingsGateway.Foundation.Sockets
{
if (this.ReturnException)
{
client.Send(ex.Message);
client.Send(ex.ToString());
}
}
}

View File

@@ -18,11 +18,17 @@ namespace ThingsGateway.Foundation.Sockets
public class Receiver : DisposableObject, IReceiver
{
private readonly IClient m_client;
private readonly AutoResetEvent m_resetEventForComplateRead = new AutoResetEvent(false);
private readonly EasyLock m_resetEventForComplateRead = new EasyLock(false);
private readonly AsyncAutoResetEvent m_resetEventForRead = new AsyncAutoResetEvent(false);
private ByteBlock m_byteBlock;
private IRequestInfo m_requestInfo;
/// <summary>
/// Receiver
/// </summary>
~Receiver()
{
this.Dispose(false);
}
/// <summary>
/// Receiver
/// </summary>
@@ -64,7 +70,7 @@ namespace ThingsGateway.Foundation.Sockets
{
return true;
}
if (this.m_resetEventForComplateRead.WaitOne(TimeSpan.FromSeconds(10)))
if (this.m_resetEventForComplateRead.Wait(TimeSpan.FromSeconds(10), CancellationToken.None))
{
return true;
}
@@ -74,9 +80,15 @@ namespace ThingsGateway.Foundation.Sockets
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
this.m_client.ClearReceiver();
this.m_resetEventForComplateRead.SafeDispose();
this.m_resetEventForRead.SafeDispose();
if (disposing)
{
this.m_client.ClearReceiver();
}
else
{
this.m_resetEventForRead.SafeDispose();
}
this.m_byteBlock = null;
base.Dispose(disposing);
}
@@ -85,7 +97,7 @@ namespace ThingsGateway.Foundation.Sockets
{
this.m_byteBlock = default;
this.m_requestInfo = default;
this.m_resetEventForComplateRead.Set();
this.m_resetEventForComplateRead.Release();
}
}
}

View File

@@ -46,37 +46,13 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="buffer">数据缓存区</param>
/// <param name="offset">偏移</param>
/// <param name="length">长度</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
ResponsedData SendThenResponse(byte[] buffer, int offset, int length, CancellationToken token);
/// <summary>
/// 发送字节流
/// </summary>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
/// <summary>
/// 发送流中的有效数据
/// </summary>
/// <param name="byteBlock">数据块载体</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
/// <summary>
/// 异步发送
@@ -84,36 +60,12 @@ namespace ThingsGateway.Foundation.Sockets
/// <param name="buffer">数据缓存区</param>
/// <param name="offset">偏移</param>
/// <param name="length">长度</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
/// <summary>
/// 异步发送
/// </summary>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
/// <summary>
/// 异步发送
/// </summary>
/// <param name="byteBlock">数据块载体</param>
/// <param name="timeout">超时时间</param>
/// <param name="cancellationToken">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
Task<ResponsedData> SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, CancellationToken token);
}
}

View File

@@ -27,17 +27,11 @@ namespace ThingsGateway.Foundation.Sockets
{
internal class WaitingClient<TClient> : DisposableObject, IWaitingClient<TClient> where TClient : IClient, ISender
{
private readonly Func<ResponsedData, bool> m_func;
private readonly SemaphoreSlim m_semaphoreSlim = new SemaphoreSlim(1, 1);
private readonly EasyLock m_semaphoreSlim = new();
private volatile bool m_breaked;
private CancellationTokenSource m_cancellation;
private CancellationTokenSource m_cancellationTokenSource;
public WaitingClient(TClient client, WaitingOptions waitingOptions, Func<ResponsedData, bool> func)
{
this.Client = client ?? throw new ArgumentNullException(nameof(client));
this.WaitingOptions = waitingOptions;
this.m_func = func;
}
public WaitingClient(TClient client, WaitingOptions waitingOptions)
{
@@ -59,19 +53,25 @@ namespace ThingsGateway.Foundation.Sockets
protected override void Dispose(bool disposing)
{
this.Client = default;
this.Cancel();
this.Client = default;
base.Dispose(disposing);
}
private void Cancel()
{
this.m_cancellation.Cancel();
try
{
this.m_cancellationTokenSource?.Cancel();
}
catch
{
}
}
#region Response
public ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default)
public ResponsedData SendThenResponse(byte[] buffer, int offset, int length, CancellationToken token = default)
{
try
{
@@ -79,14 +79,13 @@ namespace ThingsGateway.Foundation.Sockets
this.m_breaked = false;
if (token.CanBeCanceled)
{
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(timeout);
m_cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, token);
this.m_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
}
else
{
m_cancellation = new CancellationTokenSource(timeout);
this.m_cancellationTokenSource = new CancellationTokenSource(5000);
}
using (m_cancellation)
using (m_cancellationTokenSource)
{
if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session)
{
@@ -96,7 +95,7 @@ namespace ThingsGateway.Foundation.Sockets
while (true)
{
using (var receiverResult = receiver.ReadAsync(m_cancellation.Token).GetFalseAwaitResult())
using (var receiverResult = receiver.ReadAsync(this.m_cancellationTokenSource.Token).GetFalseAwaitResult())
{
var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo);
}
@@ -110,7 +109,7 @@ namespace ThingsGateway.Foundation.Sockets
this.Client.Send(buffer, offset, length);
while (true)
{
using (var receiverResult = receiver.ReadAsync(m_cancellation.Token).GetFalseAwaitResult())
using (var receiverResult = receiver.ReadAsync(this.m_cancellationTokenSource.Token).GetFalseAwaitResult())
{
if (receiverResult.IsClosed)
{
@@ -119,13 +118,13 @@ namespace ThingsGateway.Foundation.Sockets
}
var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo);
if (this.m_func == null)
if (this.WaitingOptions.FilterFunc == null)
{
return response;
}
else
{
if (this.m_func.Invoke(response))
if (this.WaitingOptions.FilterFunc.Invoke(response))
{
return response;
}
@@ -142,25 +141,18 @@ namespace ThingsGateway.Foundation.Sockets
}
finally
{
this.m_cancellationTokenSource = null;
this.m_semaphoreSlim.Release();
}
}
public ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default)
{
return this.SendThenResponse(buffer, 0, buffer.Length, timeout, token);
}
public ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default)
{
return this.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, timeout, token);
}
#endregion Response
#region Response异步
public async Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default)
public async Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, CancellationToken token = default)
{
try
{
@@ -168,14 +160,13 @@ namespace ThingsGateway.Foundation.Sockets
this.m_breaked = false;
if (token.CanBeCanceled)
{
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(timeout);
m_cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, token);
this.m_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
}
else
{
m_cancellation = new CancellationTokenSource(timeout);
this.m_cancellationTokenSource = new CancellationTokenSource(5000);
}
using (m_cancellation)
using (m_cancellationTokenSource)
{
if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session)
{
@@ -185,7 +176,7 @@ namespace ThingsGateway.Foundation.Sockets
while (true)
{
using (var receiverResult = await receiver.ReadAsync(m_cancellation.Token))
using (var receiverResult = await receiver.ReadAsync(m_cancellationTokenSource.Token))
{
var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo);
}
@@ -199,7 +190,7 @@ namespace ThingsGateway.Foundation.Sockets
await this.Client.SendAsync(buffer, offset, length);
while (true)
{
using (var receiverResult = await receiver.ReadAsync(m_cancellation.Token))
using (var receiverResult = await receiver.ReadAsync(this.m_cancellationTokenSource.Token))
{
if (receiverResult.IsClosed)
{
@@ -208,13 +199,13 @@ namespace ThingsGateway.Foundation.Sockets
}
var response = new ResponsedData(receiverResult.ByteBlock?.ToArray(), receiverResult.RequestInfo);
if (this.m_func == null)
if (this.WaitingOptions.FilterFunc == null)
{
return response;
}
else
{
if (this.m_func.Invoke(response))
if (this.WaitingOptions.FilterFunc.Invoke(response))
{
return response;
}
@@ -231,58 +222,27 @@ namespace ThingsGateway.Foundation.Sockets
}
finally
{
this.m_cancellationTokenSource = null;
this.m_semaphoreSlim.Release();
}
}
public Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default)
{
return this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, token);
}
public Task<ResponsedData> SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default)
{
return this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, token);
}
#endregion Response异步
#region
public byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default)
public byte[] SendThenReturn(byte[] buffer, int offset, int length, CancellationToken token = default)
{
return this.SendThenResponse(buffer, offset, length, timeout, token).Data;
return this.SendThenResponse(buffer, offset, length, token).Data;
}
public byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default)
public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, CancellationToken token = default)
{
return this.SendThenReturn(buffer, 0, buffer.Length, timeout, token);
return (await this.SendThenResponseAsync(buffer, offset, length, token)).Data;
}
public byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default)
{
return this.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, timeout, token);
}
#endregion
#region
public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken token = default)
{
return (await this.SendThenResponseAsync(buffer, offset, length, timeout, token)).Data;
}
public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default)
{
return (await this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, token)).Data;
}
public async Task<byte[]> SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken token = default)
{
return (await this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, token)).Data;
}
#endregion
}
}

View File

@@ -23,6 +23,8 @@
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using System.Text;
namespace ThingsGateway.Foundation.Sockets
{
/// <summary>
@@ -31,28 +33,224 @@ namespace ThingsGateway.Foundation.Sockets
public static class WaitingClientExtension
{
/// <summary>
/// 获取筛选条件的可等待的客户端。
/// </summary>
/// <typeparam name="TClient"></typeparam>
/// <param name="client"></param>
/// <param name="waitingOptions"></param>
/// <param name="func">当条件成立时返回</param>
/// <returns></returns>
public static IWaitingClient<TClient> GetWaitingClient<TClient>(this TClient client, WaitingOptions waitingOptions, Func<ResponsedData, bool> func) where TClient : IClient, ISender
{
return new WaitingClient<TClient>(client, waitingOptions, func);
}
/// <summary>
/// 获取可等待的客户端。
/// 创建可等待的客户端。
/// </summary>
/// <typeparam name="TClient"></typeparam>
/// <param name="client"></param>
/// <param name="waitingOptions"></param>
/// <returns></returns>
public static IWaitingClient<TClient> GetWaitingClient<TClient>(this TClient client, WaitingOptions waitingOptions) where TClient : IClient, ISender
public static IWaitingClient<TClient> CreateWaitingClient<TClient>(this TClient client, WaitingOptions waitingOptions) where TClient : IClient, ISender
{
return new WaitingClient<TClient>(client, waitingOptions);
}
#region
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, byte[] buffer, CancellationToken token)
where TClient : IClient, ISender
{
return client.SendThenResponse(buffer, 0, buffer.Length, token);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="msg"></param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, string msg, CancellationToken token)
where TClient : IClient, ISender
{
return client.SendThenResponse(Encoding.UTF8.GetBytes(msg), token);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout">超时时间</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout, CancellationToken token)
where TClient : IClient, ISender
{
using CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(timeout).Token, token);
return client.SendThenResponse(buffer, 0, buffer.Length, cancellationTokenSource.Token);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout">超时时间</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout, CancellationToken token)
where TClient : IClient, ISender
{
using CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(timeout).Token, token);
return client.SendThenResponseAsync(buffer, 0, buffer.Length, cancellationTokenSource.Token);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="msg"></param>
/// <param name="timeout"></param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, string msg, int timeout = 5000)
where TClient : IClient, ISender
{
return client.SendThenResponse(Encoding.UTF8.GetBytes(msg), timeout);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="byteBlock">数据块载体</param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, ByteBlock byteBlock, CancellationToken token)
where TClient : IClient, ISender
{
return client.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, token);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="buffer">数据缓存区</param>
/// <param name="timeout">超时时间</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout = 5000)
where TClient : IClient, ISender
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenResponse(buffer, 0, buffer.Length, tokenSource.Token);
}
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="byteBlock">数据块载体</param>
/// <param name="timeout">超时时间</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static ResponsedData SendThenResponse<TClient>(this IWaitingClient<TClient> client, ByteBlock byteBlock, int timeout = 5000)
where TClient : IClient, ISender
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, tokenSource.Token);
}
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, byte[] buffer, CancellationToken token)
where TClient : IClient, ISender
{
return client.SendThenResponseAsync(buffer, 0, buffer.Length, token);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="msg"></param>
/// <param name="token">取消令箭</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, string msg, CancellationToken token)
where TClient : IClient, ISender
{
return client.SendThenResponseAsync(Encoding.UTF8.GetBytes(msg), token);
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
/// <param name="timeout">超时时间</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, byte[] buffer, int timeout = 5000)
where TClient : IClient, ISender
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenResponseAsync(buffer, 0, buffer.Length, tokenSource.Token);
}
}
/// <summary>
/// 发送数据并等待
/// </summary>
/// <param name="client"></param>
/// <param name="msg"></param>
/// <param name="timeout">超时时间</param>
/// <exception cref="NotConnectedException">客户端没有连接</exception>
/// <exception cref="OverlengthException">发送数据超长</exception>
/// <exception cref="Exception">其他异常</exception>
/// <returns>返回的数据</returns>
public static Task<ResponsedData> SendThenResponseAsync<TClient>(this IWaitingClient<TClient> client, string msg, int timeout = 5000)
where TClient : IClient, ISender
{
using (var tokenSource = new CancellationTokenSource(timeout))
{
return client.SendThenResponseAsync(Encoding.UTF8.GetBytes(msg), tokenSource.Token);
}
}
#endregion
}
}

View File

@@ -45,5 +45,12 @@ namespace ThingsGateway.Foundation.Sockets
/// 当Client为Tcp系时。是否在断开连接时以异常返回结果。
/// </summary>
public bool ThrowBreakException { get; set; } = true;
/// <summary>
/// 筛选函数
/// </summary>
public Func<ResponsedData, bool> FilterFunc { get; set; }
}
}

View File

@@ -142,7 +142,7 @@ namespace ThingsGateway.Foundation.WebApi
catch (Exception ex)
{
invokeResult.Status = InvokeStatus.Exception;
invokeResult.Message = ex.Message;
invokeResult.Message = ex.ToString();
}
}
else
@@ -273,7 +273,7 @@ namespace ThingsGateway.Foundation.WebApi
catch (Exception ex)
{
invokeResult.Status = InvokeStatus.Exception;
invokeResult.Message = ex.Message;
invokeResult.Message = ex.ToString();
}
}
else

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.0.0.12</Version>
<Version>3.0.0.22</Version>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>

View File

@@ -98,7 +98,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage.LogWarning(ex, ToString());
LogMessage.LogWarning(ex);
}
}
@@ -133,7 +133,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage.LogWarning(ex, ToString());
LogMessage.LogWarning(ex);
}
}
@@ -144,7 +144,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -153,7 +153,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
{
@@ -180,7 +180,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
if (isSuccess)
@@ -212,7 +212,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
if (isSuccess)
@@ -226,7 +226,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
@@ -401,7 +401,7 @@ public class KafkaProducer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount);
}
}

View File

@@ -162,7 +162,7 @@ public class ModbusSerialServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
tags.ForEach(a =>
{
a.VariableValueChange -= VariableValueChange;
@@ -234,7 +234,7 @@ public class ModbusSerialServer : UpLoadBase
}
catch (Exception ex)
{
return new OperResult(ex.Message);
return new OperResult(ex);
}
}

View File

@@ -158,7 +158,7 @@ public class ModbusTcpServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
tags.ForEach(a =>
{
a.VariableValueChange -= VariableValueChange;
@@ -201,7 +201,7 @@ public class ModbusTcpServer : UpLoadBase
var addressStr = GetPropertyValue(tag.Value, nameof(ModbusTcpServerVariableProperty.ServiceAddress));
if (Enum.TryParse<DataTypeEnum>(type, out DataTypeEnum result))
{
var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{client.IP + ":" + client.Port}",
var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{$"{client.IP}:{client.Port}"}",
new Dictionary<string, string>
{
{
@@ -216,7 +216,7 @@ public class ModbusTcpServer : UpLoadBase
}
else
{
var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{client.IP + ":" + client.Port}",
var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{$"{client.IP}:{client.Port}"}",
new Dictionary<string, string>
{
{
@@ -230,7 +230,7 @@ public class ModbusTcpServer : UpLoadBase
}
catch (Exception ex)
{
return new OperResult(ex.Message);
return new OperResult(ex);
}
}

View File

@@ -124,7 +124,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -163,7 +163,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -171,7 +171,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -180,7 +180,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
{
@@ -207,7 +207,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -237,7 +237,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -250,7 +250,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
@@ -301,7 +301,7 @@ public class IotSharpClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogError(ex, ToString());
LogMessage?.LogError(ex);
}
}

View File

@@ -114,7 +114,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -149,7 +149,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -157,7 +157,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -167,7 +167,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
{
@@ -194,7 +194,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -226,7 +226,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -239,7 +239,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
@@ -293,7 +293,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogError(ex, ToString());
LogMessage?.LogError(ex);
}
}
@@ -438,7 +438,7 @@ public class MqttClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
{

View File

@@ -108,7 +108,7 @@ public class MqttServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -117,7 +117,7 @@ public class MqttServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
@@ -147,7 +147,7 @@ public class MqttServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -157,7 +157,7 @@ public class MqttServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
@@ -170,7 +170,7 @@ public class MqttServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
{
@@ -302,7 +302,7 @@ public class MqttServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
{

View File

@@ -88,12 +88,12 @@ f =>
.Build();
await mqttClientPage.MqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + $"订阅{driverDebugUIPage.Address}成功"));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 订阅{driverDebugUIPage.Address}成功"));
}
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}"));
}
}
@@ -106,12 +106,12 @@ f =>
.Build();
await mqttClientPage.MqttClient.UnsubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + $"取消订阅{driverDebugUIPage.Address}成功"));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 取消订阅{driverDebugUIPage.Address}成功"));
}
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}"));
}
}
@@ -125,11 +125,11 @@ f =>
.WithTopic($"{PublishTopic}")
.WithPayload(PublishValue).Build();
await mqttClientPage.MqttClient.PublishAsync(devMessage, CancellationToken.None);
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + $"发布{PublishTopic}成功"));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 发布{PublishTopic}成功"));
}
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}"));
}
}
@@ -141,11 +141,11 @@ f =>
using MqttRpcClient mqttRpcClient = new(mqttClientPage.MqttClient);
var data = await mqttRpcClient.ExecuteAsync(MqttRpcTopicPair, driverDebugUIPage.WriteValue, MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce, TimeSpan.FromSeconds(10));
var str = Encoding.UTF8.GetString(data);
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + str));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {str}"));
}
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset) + " - " + ex.Message));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}"));
}
}

View File

@@ -230,7 +230,7 @@ public class OPCDAClient : CollectBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
}

View File

@@ -86,7 +86,7 @@ public partial class OPCDAClientDebugPage : IDisposable
}
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex.Message));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
}
}
@@ -182,7 +182,7 @@ public partial class OPCDAClientDebugPage : IDisposable
}
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex.Message));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
}
return Task.CompletedTask;
@@ -228,7 +228,7 @@ public partial class OPCDAClientDebugPage : IDisposable
}
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + "写入失败:" + ex.Message));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入失败:{ex}"));
}
return Task.CompletedTask;

View File

@@ -177,6 +177,8 @@ public class OPCUAClient : CollectBase
if (_plc != null)
{
_plc.DataChangedHandler -= DataChangedHandler;
_plc.OpcStatusChange -= _plc_OpcStatusChange;
_plc.Disconnect();
_plc.SafeDispose();
_plc = null;
@@ -204,13 +206,19 @@ public class OPCUAClient : CollectBase
};
if (_plc == null)
{
_plc = new((arg1, arg2, arg3, arg4) => Log_Out((LogLevel)arg1, arg2, arg3, arg4));
_plc = new();
_plc.OpcStatusChange += _plc_OpcStatusChange;
_plc.DataChangedHandler += DataChangedHandler;
}
_plc.OPCNode = opcNode;
}
private void _plc_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
{
Log_Out((LogLevel)e.LogLevel, null, e.Text, null);
}
/// <inheritdoc/>
protected override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken)
{
@@ -283,7 +291,7 @@ public class OPCUAClient : CollectBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}

View File

@@ -0,0 +1,39 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Opc.Ua.Configuration;
namespace ThingsGateway.Plugin.OPCUA;
public partial class OPCUAServer
{
public class ApplicationMessageDlg : IApplicationMessageDlg
{
private string message = string.Empty;
private ILog _log;
public ApplicationMessageDlg(ILog log)
{
_log = log;
}
public override void Message(string text, bool ask)
{
message = text;
}
public override async Task<bool> ShowAsync()
{
_log.Warning(message);
return await Task.FromResult(true);
}
}
}

View File

@@ -0,0 +1,59 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Microsoft.Extensions.Logging;
namespace ThingsGateway.Plugin.OPCUA;
public partial class OPCUAServer
{
private class OPCUALogger : ILogger
{
private ILog _log;
public OPCUALogger(ILog log)
{
_log = log;
}
/// <summary>
/// Set the log level
/// </summary>
public Microsoft.Extensions.Logging.LogLevel LogLevel { get; set; } = Microsoft.Extensions.Logging.LogLevel.Trace;
/// <inheritdoc/>
public IDisposable BeginScope<TState>(TState state) => default;
/// <inheritdoc/>
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => logLevel >= LogLevel;
/// <inheritdoc/>
public void Log<TState>(
Microsoft.Extensions.Logging.LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
else
{
var message = formatter(state, exception);
_log.Log((Foundation.Core.LogLevel)(byte)logLevel, state, message, exception);
if (logLevel > Microsoft.Extensions.Logging.LogLevel.Information)
{
}
}
}
}
}

View File

@@ -63,7 +63,7 @@ public partial class OPCUAServer : UpLoadBase
public override async Task BeforStartAsync(CancellationToken cancellationToken)
{
// 启动服务器。
await m_application.CheckApplicationInstanceCertificate(true, 0, 1200);
await m_application.CheckApplicationInstanceCertificate(false, 0, 1200);
await m_application.Start(m_server);
}
/// <inheritdoc/>
@@ -90,7 +90,7 @@ public partial class OPCUAServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage.LogWarning(ex, ToString());
LogMessage.LogWarning(ex);
}
}
@@ -98,7 +98,7 @@ public partial class OPCUAServer : UpLoadBase
}
catch (Exception ex)
{
LogMessage.LogWarning(ex, ToString());
LogMessage.LogWarning(ex);
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
{
@@ -138,9 +138,13 @@ public partial class OPCUAServer : UpLoadBase
_uploadVariables = null;
CollectVariableRunTimes.Clear();
}
/// <inheritdoc/>
protected override void Init(UploadDeviceRunTime device)
{
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(LogMessage);//默认返回true
//Utils.SetLogger(new OPCUALogger(LogMessage)); //调试用途
m_application = new ApplicationInstance();
m_configuration = GetDefaultConfiguration();
m_configuration.Validate(ApplicationType.Server).GetAwaiter().GetResult();
@@ -169,7 +173,7 @@ public partial class OPCUAServer : UpLoadBase
private ApplicationConfiguration GetDefaultConfiguration()
{
ApplicationConfiguration config = new();
string url = driverPropertys.OpcUaStringUrl;
var urls = driverPropertys.OpcUaStringUrl.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
// 签名及加密验证
ServerSecurityPolicyCollection policies = new();
var userTokens = new UserTokenPolicyCollection();
@@ -242,13 +246,14 @@ public partial class OPCUAServer : UpLoadBase
config.ApplicationName = "ThingsGateway OPCUAServer";
config.ApplicationType = ApplicationType.Server;
config.ApplicationUri = Utils.Format(@"urn:{0}:thingsgatewayopcuaserver", System.Net.Dns.GetHostName());
config.ApplicationUri = driverPropertys.ApplicationUri;
config.ServerConfiguration = new ServerConfiguration()
{
// 配置登录的地址
BaseAddresses = new string[] { url },
BaseAddresses = urls,
SecurityPolicies = policies,
UserTokenPolicies = userTokens,
ShutdownDelay = 1,
@@ -269,7 +274,7 @@ public partial class OPCUAServer : UpLoadBase
MaxNotificationQueueSize = 100, // 为每个被监视项目保存在队列中的最大证书数
MaxNotificationsPerPublish = 1000, // 每次发布的最大通知数
MinMetadataSamplingInterval = 1000, // 元数据的最小采样间隔
MaxRegistrationInterval = 30000, // 两次注册尝试之间的最大时间(以毫秒为单位)
MaxRegistrationInterval = -1, // 两次注册尝试之间的最大时间(以毫秒为单位)//不提供注册
};
config.SecurityConfiguration = new SecurityConfiguration()
@@ -283,7 +288,8 @@ public partial class OPCUAServer : UpLoadBase
{
StoreType = CertificateStoreType.X509Store,
StorePath = "CurrentUser\\UAServer_ThingsGateway",
SubjectName = "CN=ThingsGateway OPCUAServer, C=CN, S=GUANGZHOU, O=ThingsGateway, DC=" + System.Net.Dns.GetHostName(),
SubjectName = driverPropertys.SubjectName,
//ValidationOptions = CertificateValidationOptions.SuppressHostNameInvalid,
},
TrustedPeerCertificates = new CertificateTrustList()
@@ -323,7 +329,7 @@ public partial class OPCUAServer : UpLoadBase
config.CertificateValidator = new CertificateValidator();
config.CertificateValidator.Update(config);
config.CertificateValidator.Update(config).GetAwaiter().GetResult();
config.Extensions = new XmlElementCollection();
return config;

View File

@@ -10,6 +10,8 @@
//------------------------------------------------------------------------------
#endregion
using Opc.Ua;
namespace ThingsGateway.Plugin.OPCUA;
/// <inheritdoc/>
@@ -18,8 +20,21 @@ public class OPCUAServerProperty : UpDriverPropertyBase
/// <summary>
/// 服务地址
/// </summary>
[DeviceProperty("服务地址", "")]
[DeviceProperty("服务地址", "分号分割数组可设置多个url")]
public string OpcUaStringUrl { get; set; } = "opc.tcp://127.0.0.1:49321";
/// <summary>
/// SubjectName
/// </summary>
[DeviceProperty("SubjectName", "")]
public string SubjectName { get; set; } = "CN=ThingsGateway OPCUAServer, C=CN, S=GUANGZHOU, O=ThingsGateway, DC=" + System.Net.Dns.GetHostName();
/// <summary>
/// ApplicationUri
/// </summary>
[DeviceProperty("ApplicationUri", "")]
public string ApplicationUri { get; set; } = Utils.Format(@"urn:{0}:thingsgatewayopcuaserver", System.Net.Dns.GetHostName());
/// <summary>
/// 安全策略
/// </summary>

View File

@@ -53,16 +53,23 @@
写入
</MButton>
<MButton Class="mx-1 my-3" Color="accent" OnClick="()=>IsShowImportVariableList=!IsShowImportVariableList">
<span>查看OPC节点空间</span>
</MButton>
</div>
<div class="my-1 ml-2">
<MRow Align="AlignTypes.Center" >
<MCheckbox HideDetails="@("auto")" @bind-Value=IsShowSubvariable Dense Label="是否显示子变量"/>
<MButton Class="mx-1" Color="accent" OnClick="()=>IsShowImportVariableList=!IsShowImportVariableList">
<span>查看OPC节点空间</span>
</MButton>
</MRow>
</div>
<MCol Class="my-1 py-1">
<MRow NoGutters>
<MRow Align="AlignTypes.Center">
@@ -115,7 +122,7 @@
<ChildContent>
@if (IsShowImportVariableList)
{
<OPCUAImportVariable @ref=ImportVariable PLC="_plc"></OPCUAImportVariable>
<OPCUAImportVariable @ref=ImportVariable PLC="_plc" IsShowSubvariable=IsShowSubvariable></OPCUAImportVariable>
}
</ChildContent>
@@ -127,5 +134,5 @@
@code {
private readonly List<(string Code, string Language)> _sections = new();
private bool IsShowSubvariable;
}

View File

@@ -80,7 +80,7 @@ public partial class OPCUAClientDebugPage
await _plc.AddSubscriptionAsync(Guid.NewGuid().ToString(), new[] { driverDebugUIPage.Address });
else
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + "未连接"));
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 未连接"));
}
}
@@ -186,7 +186,7 @@ public partial class OPCUAClientDebugPage
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex.Message}"));
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
}
}
else
@@ -234,7 +234,7 @@ public partial class OPCUAClientDebugPage
catch (Exception ex)
{
driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex.Message}"));
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {ex}"));
}
}
}

View File

@@ -37,7 +37,7 @@
<MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.UserName) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UserName />
<MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.Password) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.Password />
<MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.ActiveSubscribe) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe />
<MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.ActiveSubscribe) Dense HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe />
<MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.IsUseSecurity) Dense HideDetails="@("auto")" @bind-Value=@node.IsUseSecurity />
<MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.CheckDomain) Dense HideDetails="@("auto")" @bind-Value=@node.CheckDomain />
<MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.LoadType) Dense HideDetails="@("auto")" @bind-Value=@node.LoadType />

View File

@@ -36,16 +36,24 @@ public partial class OPCUAClientPage
/// </summary>
public void Dispose()
{
OPC.OpcStatusChange -= OPC_OpcStatusChange;
OPC.SafeDispose();
}
/// <inheritdoc/>
protected override void OnInitialized()
{
OPC = new ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient(LogOut);
OPC = new ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient();
OPC.OpcStatusChange += OPC_OpcStatusChange;
base.OnInitialized();
}
private void OPC_OpcStatusChange(object sender, OpcUaStatusEventArgs e)
{
if (e.LogLevel > 0)
LogAction?.Invoke((LogLevel)e.LogLevel, null, e.Text, null);
}
private async Task ConnectAsync()
{
try
@@ -75,6 +83,4 @@ public partial class OPCUAClientPage
return OPC;
}
private void LogOut(byte logLevel, object source, string message, Exception exception) => LogAction?.Invoke((LogLevel)logLevel, source, message, exception);
}

View File

@@ -43,6 +43,11 @@ public partial class OPCUAImportVariable
/// </summary>
[Parameter]
public ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient PLC { get; set; }
/// <summary>
/// 是否显示子变量
/// </summary>
[Parameter]
public bool IsShowSubvariable { get; set; }
private List<ReferenceDescription> Actived
{
@@ -73,7 +78,7 @@ public partial class OPCUAImportVariable
//动态加载子项时,导出内容需要添加手动加载代码
foreach (var node in Selected.ToList())
{
List<OPCUATagModel> nodes = await PopulateBranchAsync((NodeId)node.NodeId, true);
List<OPCUATagModel> nodes = await PopulateBranchAsync((NodeId)node.NodeId, true, IsShowSubvariable);
if (nodes.Count > 0)
{
Selected.AddRange(nodes.SelectMany(a => a.GetAllTags()).Select(a => a.Tag).Where(a => a != null).ToList());
@@ -165,7 +170,7 @@ public partial class OPCUAImportVariable
{
Task.Run(async () =>
{
Nodes = await PopulateBranchAsync(ObjectIds.ObjectsFolder);
Nodes = await PopulateBranchAsync(ObjectIds.ObjectsFolder, isShowSubvariable: IsShowSubvariable);
overlay = false;
await InvokeAsync(StateHasChanged);
});
@@ -206,10 +211,10 @@ public partial class OPCUAImportVariable
private async Task PopulateBranchAsync(OPCUATagModel model)
{
var sourceId = (NodeId)model.Tag.NodeId;
model.Nodes = await PopulateBranchAsync(sourceId);
model.Nodes = await PopulateBranchAsync(sourceId, isShowSubvariable: IsShowSubvariable);
}
private async Task<List<OPCUATagModel>> PopulateBranchAsync(NodeId sourceId, bool isAll = false)
private async Task<List<OPCUATagModel>> PopulateBranchAsync(NodeId sourceId, bool isAll = false, bool isShowSubvariable = false)
{
if (!PLC.Connected)
{
@@ -232,13 +237,13 @@ public partial class OPCUAImportVariable
Name = Utils.Format("{0}", target),
Tag = target
};
//if (target.NodeClass != NodeClass.Variable)
if (isShowSubvariable || target.NodeClass != NodeClass.Variable)
{
var data = await GetReferenceDescriptionCollectionAsync((NodeId)target.NodeId);
if (data != null && data.Count > 0)
{
if (isAll)
child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId);
child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId, isShowSubvariable: IsShowSubvariable);
else
child.Nodes = new();
}
@@ -247,10 +252,32 @@ public partial class OPCUAImportVariable
child.Nodes = null;
}
}
//else
else
{
child.Nodes = null;
}
////if (target.NodeClass != NodeClass.Variable) //这个判断注释后会让子节点也是变量的情况下无法加载
//{
// child.Nodes = null;
// var data = await GetReferenceDescriptionCollectionAsync((NodeId)target.NodeId);
// if (data != null && data.Count > 0)
// {
// if (isAll)
// child.Nodes = await PopulateBranchAsync((NodeId)target.NodeId);
// else
// child.Nodes = new();
// }
// else
// {
// child.Nodes = null;
// }
//}
////else
////{
//// child.Nodes = null;
////}
list.Add(child);
}
}

View File

@@ -0,0 +1,15 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
global using ThingsGateway.Foundation.Core;
global using ThingsGateway.Gateway.Application;
global using ThingsGateway.Gateway.Core;

View File

@@ -0,0 +1,325 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion;
using Mapster;
using Microsoft.Extensions.Hosting;
using SqlSugar;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Plugin.QuestDB;
public class QuestDB : UpLoadBase
{
private readonly ConcurrentQueue<QuestDBHistoryValue> DeviceVariableRunTimes = new();
private readonly QuestDBProperty driverPropertys = new();
private readonly QuestDBVariableProperty variablePropertys = new();
private TypeAdapterConfig _config;
private GlobalDeviceData _globalDeviceData;
private List<DeviceVariableRunTime> _uploadVariables = new();
private TimerTick exTimerTick;
public QuestDB()
{
_config = new TypeAdapterConfig();
_config.ForType<DeviceVariableRunTime, HistoryValue>()
.Map(dest => dest.Value, (src) => ValueReturn(src))
.Map(dest => dest.CollectTime, (src) => src.CollectTime.ToUniversalTime())//注意sqlsugar插入时无时区直接utc时间
.Map(dest => dest.CreateTime, (src) => DateTime.UtcNow);//注意sqlsugar插入时无时区直接utc时间
}
private static object ValueReturn(DeviceVariableRunTime src)
{
if (src.Value?.ToString()?.IsBoolValue() == true)
{
if (src.Value.ToBoolean())
{
return 1;
}
else
{
return 0;
}
}
else
{
return src.Value;
}
}
public override Type DriverDebugUIType => null;
public override UpDriverPropertyBase DriverPropertys => driverPropertys;
public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables;
public override VariablePropertyBase VariablePropertys => variablePropertys;
public override Task AfterStopAsync()
{
return Task.CompletedTask;
}
public override async Task BeforStartAsync(CancellationToken cancellationToken)
{
SqlSugarClient db = GetHisDbAsync();
db.DbMaintenance.CreateDatabase();
db.CodeFirst.InitTables(typeof(QuestDBHistoryValue));
await Task.CompletedTask;
}
public override async Task ExecuteAsync(CancellationToken cancellationToken)
{
var db = GetHisDbAsync();
{
if (!driverPropertys.IsInterval)
{
try
{
////变化推送
var varList = DeviceVariableRunTimes.ToListWithDequeue();
if (varList?.Count != 0)
{
await InserableAsync(db, varList, cancellationToken);
}
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex);
}
}
else
{
if (exTimerTick.IsTickHappen())
{
try
{
var varList = _uploadVariables.ToList().Adapt<List<QuestDBHistoryValue>>(_config);
if (varList?.Count != 0)
{
await InserableAsync(db, varList, cancellationToken);
}
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex);
}
}
}
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
{
try
{
await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken);
}
catch
{
}
}
}
public override bool IsConnected() => _uploadVariables?.Count > 0;
protected override void Dispose(bool disposing)
{
try
{
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
_uploadVariables = null;
}
catch (Exception ex)
{
LogMessage?.LogError(ex);
}
base.Dispose(disposing);
}
protected override void Init(UploadDeviceRunTime device)
{
_globalDeviceData = App.GetService<GlobalDeviceData>();
var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id))
.Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean())
.ToList();
_uploadVariables = tags;
if (!driverPropertys.IsInterval)
{
_uploadVariables.ForEach(a =>
{
a.VariableValueChange += VariableValueChange;
});
}
if (_uploadVariables.Count == 0)
{
LogMessage.LogWarning("插件变量数量为0");
}
if (driverPropertys.IntervalTime < 1)
driverPropertys.IntervalTime = 10;
exTimerTick = new(driverPropertys.IntervalTime * 1000);
}
/// <summary>
/// Aop设置
/// </summary>
/// <param name="db"></param>
private static void AopSetting(SqlSugarClient db)
{
var config = db.CurrentConnectionConfig;
// 设置超时时间
db.Ado.CommandTimeOut = 30;
// 打印SQL语句
db.Aop.OnLogExecuting = (sql, pars) =>
{
//如果不是开发环境就打印sql
if (App.HostEnvironment.IsDevelopment())
{
if (sql.StartsWith("SELECT"))
{
Console.ForegroundColor = ConsoleColor.Green;
}
if (sql.StartsWith("UPDATE"))
{
Console.ForegroundColor = ConsoleColor.Yellow;
}
if (sql.StartsWith("INSERT"))
{
Console.ForegroundColor = ConsoleColor.Blue;
}
if (sql.StartsWith("DELETE"))
{
Console.ForegroundColor = ConsoleColor.Red;
}
WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars));
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine();
}
};
//异常
db.Aop.OnError = (ex) =>
{
//如果不是开发环境就打印日志
if (App.WebHostEnvironment.IsDevelopment())
{
if (ex.Parametres == null) return;
Console.ForegroundColor = ConsoleColor.Red;
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres));
Console.ForegroundColor = ConsoleColor.White;
}
};
}
private static void WriteSqlLog(string msg)
{
Console.WriteLine("【Sql执行时间】" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
Console.WriteLine("【Sql语句】" + msg + Environment.NewLine);
}
private static void WriteSqlLogError(string msg)
{
Console.WriteLine("【Sql执行错误时间】" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
Console.WriteLine("【Sql语句】" + msg + Environment.NewLine);
}
/// <summary>
/// 获取数据库链接
/// </summary>
/// <returns></returns>
private SqlSugarClient GetHisDbAsync()
{
var configureExternalServices = new ConfigureExternalServices
{
EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required
{
if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|| (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null))
column.IsNullable = true;
},
};
var sqlSugarClient = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = driverPropertys.ConnectStr,//连接字符串
DbType = DbType.QuestDB,//数据库类型
IsAutoCloseConnection = true, //不设成true要手动close
ConfigureExternalServices = configureExternalServices,
}
);
AopSetting(sqlSugarClient);//aop配置
return sqlSugarClient;
}
private async Task InserableAsync(SqlSugarClient db, List<QuestDBHistoryValue> dbInserts, CancellationToken cancellationToken)
{
try
{
var result = await db.Insertable(dbInserts).ExecuteCommandAsync(cancellationToken);
if (result > 0)
LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString());
//连接成功时补发缓存数据
var cacheData = await CacheDb.GetCacheData();
foreach (var item in cacheData)
{
try
{
var data = item.CacheStr.FromJsonString<List<QuestDBHistoryValue>>();
var cacheresult = await db.Insertable(data).ExecuteCommandAsync(cancellationToken);
if (cacheresult > 0)
{
await CacheDb.DeleteCacheData(item.Id);
LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}");
}
}
catch (Exception ex)
{
LogMessage.LogWarning(ex);
}
}
}
catch (Exception ex)
{
LogMessage.LogWarning(ex);
await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount);
}
}
private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime)
{
if (!driverPropertys.IsInterval)
DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<QuestDBHistoryValue>(_config));
}
}

View File

@@ -0,0 +1,62 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using SqlSugar;
using System.ComponentModel;
namespace ThingsGateway.Gateway.Core;
/// <summary>
/// 历史数据表
/// </summary>
[SugarTable("historyValue", TableDescription = "历史数据表")]
public class QuestDBHistoryValue
{
/// <summary>
/// 采集时间
/// </summary>
[TimeDbSplitField(DateType.Month)]
[Description("采集时间")]
public DateTime CollectTime { get; set; }
/// <summary>
/// 上传时间
/// </summary>
[Description("上传时间")]
public DateTime CreateTime { get; set; }
/// <summary>
/// 设备名称
/// </summary>
[SugarColumn(ColumnDataType = "symbol")]
[Description("设备名称")]
public string DeviceName { get; set; }
/// <summary>
/// 变量名称
/// </summary>
[SugarColumn(ColumnDataType = "symbol")]
[Description("变量名称")]
public string Name { get; set; }
/// <summary>
/// 是否在线
/// </summary>
[Description("是否在线")]
public bool IsOnline { get; set; }
/// <summary>
/// 变量值
/// </summary>
[Description("变量值")]
public double Value { get; set; }
}

View File

@@ -0,0 +1,31 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Plugin.QuestDB;
public class QuestDBProperty : UpDriverPropertyBase
{
[DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "host=localhost;port=8812;username=admin;password=quest;database=qdb;ServerCompatibilityMode=NoTypeLoading;";
[DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true;
[DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10;
[DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000;
/// <summary>
/// 线程循环间隔
/// </summary>
[DeviceProperty("线程循环间隔", "最小10ms")]
public int CycleInterval { get; set; } = 1000;
}

View File

@@ -0,0 +1,22 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Plugin.QuestDB;
public class QuestDBVariableProperty : VariablePropertyBase
{
[VariableProperty("启用", "")]
public bool Enable { get; set; } = true;
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command=" set dir=&quot;$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)&quot;&#xD;&#xA; if not exist %25dir%25 md %25dir%25 &#xD;&#xA;copy &quot;$(TargetDir)*QuestDB*.dll&quot; %25dir%25&#xD;&#xA;&#xD;&#xA;&#xD;&#xA;&#xD;&#xA;" />
</Target>
</Project>

View File

@@ -93,7 +93,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -124,7 +124,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -150,7 +150,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -188,7 +188,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -214,7 +214,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -224,7 +224,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
@@ -233,7 +233,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
try
{
@@ -257,7 +257,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -275,7 +275,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -305,7 +305,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -323,7 +323,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
}
@@ -338,7 +338,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
@@ -482,7 +482,7 @@ public class RabbitMQClient : UpLoadBase
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex, ToString());
LogMessage?.LogWarning(ex);
await CacheDb.AddCacheData(queueName, data, driverPropertys.CacheMaxCount);
}

View File

@@ -0,0 +1,15 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
global using ThingsGateway.Foundation.Core;
global using ThingsGateway.Gateway.Application;
global using ThingsGateway.Gateway.Core;

View File

@@ -0,0 +1,334 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion;
using Mapster;
using Microsoft.Extensions.Hosting;
using SqlSugar;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
using ThingsGateway.Foundation.Extension.String;
using Yitter.IdGenerator;
namespace ThingsGateway.Plugin.SQLDB;
public class SQLDB : UpLoadBase
{
private readonly ConcurrentQueue<SQLHistoryValue> DeviceVariableRunTimes = new();
private readonly SQLDBProperty driverPropertys = new();
private readonly SQLDBVariableProperty variablePropertys = new();
private TypeAdapterConfig _config;
private GlobalDeviceData _globalDeviceData;
private List<DeviceVariableRunTime> _uploadVariables = new();
private TimerTick exTimerTick;
private TimerTick exRealTimerTick;
public SQLDB()
{
_config = new TypeAdapterConfig();
_config.ForType<DeviceVariableRunTime, SQLHistoryValue>()
.Map(dest => dest.Id, (src) => YitIdHelper.NextId())
.Map(dest => dest.CreateTime, (src) => DateTime.Now);
}
public override Type DriverDebugUIType => null;
public override UpDriverPropertyBase DriverPropertys => driverPropertys;
public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables;
public override VariablePropertyBase VariablePropertys => variablePropertys;
public override Task AfterStopAsync()
{
return Task.CompletedTask;
}
public override async Task BeforStartAsync(CancellationToken cancellationToken)
{
var db = GetHisDbAsync();
db.CodeFirst.InitTables(typeof(SQLHistoryValue));
db.MappingTables.Add("SQLRealValue", driverPropertys.ReadDBTableName); // typeof(类).Name 可以拿到类名
db.CodeFirst.InitTables(typeof(SQLRealValue)); //生成的表名是 newTableName
await Task.CompletedTask;
}
public override async Task ExecuteAsync(CancellationToken cancellationToken)
{
var db = GetHisDbAsync();
if (driverPropertys.IsReadDB)
{
if (exRealTimerTick.IsTickHappen())
{
try
{
var varList = _uploadVariables.ToList().Adapt<List<SQLRealValue>>();
if (varList?.Count != 0)
{
//var result = await db.Storageable(varList).As(driverPropertys.ReadDBTableName).ExecuteCommandAsync(cancellationToken);
await db.Fastest<SQLRealValue>().AS(driverPropertys.ReadDBTableName).PageSize(100000).BulkUpdateAsync(varList);
}
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex);
}
}
}
else
{
if (!driverPropertys.IsInterval)
{
try
{
////变化推送
var varList = DeviceVariableRunTimes.ToListWithDequeue();
if (varList?.Count != 0)
{
await InserableAsync(db, varList, cancellationToken);
}
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex);
}
}
else
{
if (exTimerTick.IsTickHappen())
{
try
{
var varList = _uploadVariables.ToList().Adapt<List<SQLHistoryValue>>(_config);
if (varList?.Count != 0)
{
await InserableAsync(db, varList, cancellationToken);
}
}
catch (Exception ex)
{
LogMessage?.LogWarning(ex);
}
}
}
}
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
{
try
{
await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken);
}
catch
{
}
}
}
/// <summary>
/// 获取数据库链接
/// </summary>
/// <returns></returns>
public SqlSugarClient GetHisDbAsync()
{
var configureExternalServices = new ConfigureExternalServices
{
EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required
{
if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|| (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null))
column.IsNullable = true;
},
};
var sqlSugarClient = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = driverPropertys.ConnectStr,//连接字符串
DbType = driverPropertys.DbType,//数据库类型
IsAutoCloseConnection = true, //不设成true要手动close
ConfigureExternalServices = configureExternalServices,
}
);
AopSetting(sqlSugarClient);//aop配置
return sqlSugarClient;
}
public override bool IsConnected() => _uploadVariables?.Count > 0;
protected override void Dispose(bool disposing)
{
try
{
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
_uploadVariables = null;
}
catch (Exception ex)
{
LogMessage?.LogError(ex);
}
base.Dispose(disposing);
}
protected override void Init(UploadDeviceRunTime device)
{
_globalDeviceData = App.GetService<GlobalDeviceData>();
var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id))
.Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean())
.ToList();
_uploadVariables = tags;
if (!driverPropertys.IsReadDB)
if (!driverPropertys.IsInterval)
{
_uploadVariables.ForEach(a =>
{
a.VariableValueChange += VariableValueChange;
});
}
if (_uploadVariables.Count == 0)
{
LogMessage.LogWarning("插件变量数量为0");
}
if (driverPropertys.IntervalTime < 1)
driverPropertys.IntervalTime = 10;
exTimerTick = new(driverPropertys.IntervalTime * 1000);
exRealTimerTick = new(driverPropertys.IntervalTime * 1000);
}
/// <summary>
/// Aop设置
/// </summary>
/// <param name="db"></param>
private static void AopSetting(SqlSugarClient db)
{
var config = db.CurrentConnectionConfig;
// 设置超时时间
db.Ado.CommandTimeOut = 30;
// 打印SQL语句
db.Aop.OnLogExecuting = (sql, pars) =>
{
//如果不是开发环境就打印sql
if (App.HostEnvironment.IsDevelopment())
{
if (sql.StartsWith("SELECT"))
{
Console.ForegroundColor = ConsoleColor.Green;
}
if (sql.StartsWith("UPDATE"))
{
Console.ForegroundColor = ConsoleColor.Yellow;
}
if (sql.StartsWith("INSERT"))
{
Console.ForegroundColor = ConsoleColor.Blue;
}
if (sql.StartsWith("DELETE"))
{
Console.ForegroundColor = ConsoleColor.Red;
}
WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars));
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine();
}
};
//异常
db.Aop.OnError = (ex) =>
{
//如果不是开发环境就打印日志
if (App.WebHostEnvironment.IsDevelopment())
{
if (ex.Parametres == null) return;
Console.ForegroundColor = ConsoleColor.Red;
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres));
Console.ForegroundColor = ConsoleColor.White;
}
};
}
private static void WriteSqlLog(string msg)
{
Console.WriteLine("【Sql执行时间】" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
Console.WriteLine("【Sql语句】" + msg + Environment.NewLine);
}
private static void WriteSqlLogError(string msg)
{
Console.WriteLine("【Sql执行错误时间】" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
Console.WriteLine("【Sql语句】" + msg + Environment.NewLine);
}
private async Task InserableAsync(SqlSugarClient db, List<SQLHistoryValue> dbInserts, CancellationToken cancellationToken)
{
try
{
var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync();
if (result > 0)
LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString());
//连接成功时补发缓存数据
var cacheData = await CacheDb.GetCacheData();
foreach (var item in cacheData)
{
try
{
var data = item.CacheStr.FromJsonString<List<SQLHistoryValue>>();
var cacheresult = await db.Insertable(data).SplitTable().ExecuteCommandAsync();
if (cacheresult > 0)
{
await CacheDb.DeleteCacheData(item.Id);
LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}");
}
}
catch (Exception ex)
{
LogMessage.LogWarning(ex);
}
}
}
catch (Exception ex)
{
LogMessage.LogWarning(ex);
await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount);
}
}
private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime)
{
if (!driverPropertys.IsReadDB)
if (!driverPropertys.IsInterval)
DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<SQLHistoryValue>(_config));
}
}

View File

@@ -0,0 +1,37 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using SqlSugar;
namespace ThingsGateway.Plugin.SQLDB;
public class SQLDBProperty : UpDriverPropertyBase
{
[DeviceProperty("是否实时表", "true=>实时表更新false=>历史存储(按月分表)")] public bool IsReadDB { get; set; } = false;
[DeviceProperty("实时表名称", "")] public string ReadDBTableName { get; set; } = "ReadDBTableName";
[DeviceProperty("数据库类型", "MySql/SqlServer")] public DbType DbType { get; set; } = DbType.MySql;
[DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "server=localhost;Database=test;Uid=root;Pwd=111111;AllowLoadLocalInfile=true;";
[DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true;
[DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10;
[DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000;
/// <summary>
/// 线程循环间隔
/// </summary>
[DeviceProperty("线程循环间隔", "最小10ms")]
public int CycleInterval { get; set; } = 1000;
}

View File

@@ -0,0 +1,22 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Plugin.SQLDB;
public class SQLDBVariableProperty : VariablePropertyBase
{
[VariableProperty("启用", "")]
public bool Enable { get; set; } = true;
}

View File

@@ -0,0 +1,56 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using SqlSugar;
namespace ThingsGateway.Plugin.SQLDB;
[SplitTable(SplitType.Month)]//按月分表 (自带分表支持 年、季、月、周、日)
[SugarTable("historyValue_{year}{month}{day}", TableDescription = "设备采集历史表")]//3个变量必须要有
[SugarIndex("index_Name", nameof(SQLHistoryValue.Name), OrderByType.Desc)]
[SugarIndex("index_DeviceName", nameof(SQLHistoryValue.DeviceName), OrderByType.Desc)]
[SugarIndex("index_CollectTime", nameof(SQLHistoryValue.CollectTime), OrderByType.Desc)]
public class SQLHistoryValue
{
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
public long Id { get; set; }
/// <summary>
/// 变量名称
/// </summary>
[SugarColumn(ColumnName = "Name", ColumnDescription = "变量名称")]
public string Name { get; set; }
/// <summary>
/// 设备名称
/// </summary>
[SugarColumn(ColumnName = "DeviceName", ColumnDescription = "设备名称")]
public string DeviceName { get; set; }
///<summary>
///实时值
///</summary>
[SugarColumn(ColumnName = "Value", ColumnDescription = "实时值")]
public string Value { get; set; }
///<summary>
///是否在线
///</summary>
[SugarColumn(ColumnName = "IsOnline", ColumnDescription = "是否在线 True=在线;False=离线")]
public bool IsOnline { get; set; }
public DateTime CollectTime { get; set; }
[SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表
public DateTime CreateTime { get; set; }
}

View File

@@ -0,0 +1,52 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using SqlSugar;
namespace ThingsGateway.Plugin.SQLDB;
[SugarTable(TableDescription = "设备采集实时表")]
[SugarIndex("index_Name", nameof(SQLHistoryValue.Name), OrderByType.Desc)]
[SugarIndex("index_DeviceName", nameof(SQLHistoryValue.DeviceName), OrderByType.Desc)]
[SugarIndex("index_CollectTime", nameof(SQLHistoryValue.CollectTime), OrderByType.Desc)]
public class SQLRealValue
{
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
public long Id { get; set; }
/// <summary>
/// 变量名称
/// </summary>
[SugarColumn(ColumnName = "Name", ColumnDescription = "变量名称")]
public string Name { get; set; }
/// <summary>
/// 设备名称
/// </summary>
[SugarColumn(ColumnName = "DeviceName", ColumnDescription = "设备名称")]
public string DeviceName { get; set; }
///<summary>
///实时值
///</summary>
[SugarColumn(ColumnName = "Value", ColumnDescription = "实时值")]
public string Value { get; set; }
///<summary>
///是否在线
///</summary>
[SugarColumn(ColumnName = "IsOnline", ColumnDescription = "是否在线 True=在线;False=离线")]
public bool IsOnline { get; set; }
public DateTime CollectTime { get; set; }
}

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