Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a85e31a4f | ||
|
|
302c270ad5 | ||
|
|
3c1517d0f3 | ||
|
|
f9fb222044 | ||
|
|
e8edc02ba3 | ||
|
|
95a44e3053 | ||
|
|
74a9fe9a87 | ||
|
|
4d03f9ea1a | ||
|
|
67c96ca991 | ||
|
|
88fb793c68 | ||
|
|
d6d02d8cc5 | ||
|
|
c5a3f8e2e3 | ||
|
|
27e8653a1a | ||
|
|
863beda82c | ||
|
|
bac84c3ecd | ||
|
|
2fca2ad9f8 | ||
|
|
dd75286fe0 | ||
|
|
7f91792cf1 | ||
|
|
0e0ccad311 | ||
|
|
0691f72e67 | ||
|
|
7e38a51720 | ||
|
|
34ca8243a3 | ||
|
|
112fea7632 | ||
|
|
378763e4ee |
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>3.0.0.8</Version>
|
||||
<Version>3.0.0.16</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Authors>Diego</Authors>
|
||||
|
||||
@@ -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}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -115,12 +115,20 @@
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
|
||||
<ItemGroup >
|
||||
<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" />
|
||||
<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 >
|
||||
<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -132,10 +140,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages\Mqtt\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor" Link="Pages\Mqtt\MqttClientDebugPage.razor" />
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>3.0.0.8</Version>
|
||||
<Version>3.0.0.16</Version>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>
|
||||
<Description>
|
||||
ThingsGateway.Foundation是工业设备通讯类库,归属于ThingsGateway边缘网关项目,说明文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
</Description>
|
||||
<Authors>Diego</Authors>
|
||||
<Product>ThingsGateway</Product>
|
||||
<Copyright>© 2023-present Diego</Copyright>
|
||||
@@ -16,7 +19,6 @@
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
|
||||
<PackageTags>ThingsGateway;Diego;dotNET China;Blazor;设备采集;边缘网关</PackageTags>
|
||||
@@ -47,4 +49,5 @@
|
||||
<DebugType>Embedded</DebugType>
|
||||
<EmbedAllSources>True</EmbedAllSources>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ModbusSerialServerDataHandleAdapter : ReadWriteDevicesTcpDataHandle
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAda
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex.Message);
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#region copyright
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -70,6 +70,11 @@ public class OPCNode
|
||||
/// </summary>
|
||||
[Description("安全策略")]
|
||||
public bool IsUseSecurity { get; set; } = false;
|
||||
/// <summary>
|
||||
/// 加载服务端数据类型
|
||||
/// </summary>
|
||||
[Description("加载服务端数据类型")]
|
||||
public bool LoadType { get; set; } = true;
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#region copyright
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
@@ -200,7 +200,7 @@ public class OPCUAClient : IDisposable
|
||||
/// <summary>
|
||||
/// 新增订阅,需要指定订阅组名称,订阅的tag名数组
|
||||
/// </summary>
|
||||
public async Task AddSubscriptionAsync(string subscriptionName, string[] items)
|
||||
public async Task AddSubscriptionAsync(string subscriptionName, string[] items, bool loadType = true)
|
||||
{
|
||||
Subscription m_subscription = new(m_session.DefaultSubscription)
|
||||
{
|
||||
@@ -213,14 +213,14 @@ public class OPCUAClient : IDisposable
|
||||
DisplayName = subscriptionName
|
||||
};
|
||||
List<MonitoredItem> monitoredItems = new();
|
||||
var variableNodes = loadType ? await ReadNodesAsync(items) : null;
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var variableNode = await ReadNodeAsync(items[i], false);
|
||||
var item = new MonitoredItem
|
||||
{
|
||||
StartNodeId = variableNode.NodeId,
|
||||
StartNodeId = loadType ? variableNodes[i].NodeId : items[i],
|
||||
AttributeId = Attributes.Value,
|
||||
DisplayName = items[i],
|
||||
Filter = OPCNode.DeadBand == 0 ? null : new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.Absolute, Trigger = DataChangeTrigger.StatusValue },
|
||||
@@ -249,7 +249,7 @@ public class OPCUAClient : IDisposable
|
||||
{
|
||||
_logAction?.Invoke(3, this, $"创建以下变量订阅失败:{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()}", null);
|
||||
}
|
||||
|
||||
lock (dic_subscriptions)
|
||||
@@ -307,11 +307,11 @@ public class OPCUAClient : IDisposable
|
||||
|
||||
}
|
||||
|
||||
private async void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
|
||||
private void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var variableNode = await ReadNodeAsync(monitoreditem.StartNodeId.ToString(), false);
|
||||
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false);
|
||||
foreach (var value in monitoreditem.DequeueValues())
|
||||
{
|
||||
if (value.Value != null)
|
||||
@@ -532,7 +532,7 @@ public class OPCUAClient : IDisposable
|
||||
|
||||
//如果是订阅模式,连接时添加订阅组
|
||||
if (OPCNode.ActiveSubscribe)
|
||||
await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray());
|
||||
await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray(), OPCNode.LoadType);
|
||||
return m_session;
|
||||
}
|
||||
|
||||
@@ -680,634 +680,67 @@ public class OPCUAClient : IDisposable
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
NodeId nodeToRead = new(nodeIdStr);
|
||||
var node = (VariableNode)await ReadNodeAsync(nodeToRead, NodeClass.Unspecified, false, cancellationToken);
|
||||
await typeSystem.LoadType(node.DataType, true, false);
|
||||
var node = (VariableNode)await m_session.ReadNodeAsync(nodeToRead, NodeClass.Variable, false, cancellationToken);
|
||||
if (OPCNode.LoadType)
|
||||
await typeSystem.LoadType(node.DataType);
|
||||
_variableDicts.AddOrUpdate(nodeIdStr, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从服务器或缓存读取节点
|
||||
/// </summary>
|
||||
private VariableNode ReadNode(string nodeIdStr, bool isOnlyServer = true)
|
||||
{
|
||||
if (!isOnlyServer)
|
||||
{
|
||||
if (_variableDicts.TryGetValue(nodeIdStr, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
NodeId nodeToRead = new(nodeIdStr);
|
||||
var node = (VariableNode)m_session.ReadNode(nodeToRead, NodeClass.Variable, false);
|
||||
_variableDicts.AddOrUpdate(nodeIdStr, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region session
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Node> ReadNodeAsync(
|
||||
NodeId nodeId,
|
||||
NodeClass nodeClass,
|
||||
bool optionalAttributes = true,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
// build list of attributes.
|
||||
var attributes = CreateAttributes(nodeClass, optionalAttributes);
|
||||
|
||||
// build list of values to read.
|
||||
ReadValueIdCollection itemsToRead = new ReadValueIdCollection();
|
||||
foreach (uint attributeId in attributes.Keys)
|
||||
{
|
||||
ReadValueId itemToRead = new ReadValueId
|
||||
{
|
||||
NodeId = nodeId,
|
||||
AttributeId = attributeId
|
||||
};
|
||||
itemsToRead.Add(itemToRead);
|
||||
}
|
||||
|
||||
// read from server.
|
||||
ReadResponse readResponse = await m_session.ReadAsync(
|
||||
null,
|
||||
0,
|
||||
TimestampsToReturn.Neither,
|
||||
itemsToRead, ct).ConfigureAwait(false);
|
||||
|
||||
DataValueCollection values = readResponse.Results;
|
||||
DiagnosticInfoCollection diagnosticInfos = readResponse.DiagnosticInfos;
|
||||
|
||||
ClientBase.ValidateResponse(values, itemsToRead);
|
||||
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, itemsToRead);
|
||||
|
||||
return ProcessReadResponse(readResponse.ResponseHeader, attributes, itemsToRead, values, diagnosticInfos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Node based on the read response.
|
||||
/// 从服务器读取节点
|
||||
/// </summary>
|
||||
private Node ProcessReadResponse(
|
||||
ResponseHeader responseHeader,
|
||||
IDictionary<uint, DataValue> attributes,
|
||||
ReadValueIdCollection itemsToRead,
|
||||
DataValueCollection values,
|
||||
DiagnosticInfoCollection diagnosticInfos)
|
||||
private async Task<List<Node>> ReadNodesAsync(string[] nodeIdStrs, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// process results.
|
||||
int? nodeClass = null;
|
||||
|
||||
for (int ii = 0; ii < itemsToRead.Count; ii++)
|
||||
List<NodeId> nodeIds = new List<NodeId>();
|
||||
foreach (var item in nodeIdStrs)
|
||||
{
|
||||
uint attributeId = itemsToRead[ii].AttributeId;
|
||||
|
||||
// the node probably does not exist if the node class is not found.
|
||||
if (attributeId == Attributes.NodeClass)
|
||||
NodeId nodeToRead = new(item);
|
||||
nodeIds.Add(nodeToRead);
|
||||
}
|
||||
(IList<Node>, IList<ServiceResult>) nodes = await m_session.ReadNodesAsync(nodeIds, NodeClass.Variable, false, cancellationToken);
|
||||
for (int i = 0; i < nodes.Item1.Count; i++)
|
||||
{
|
||||
if (StatusCode.IsGood(nodes.Item2[i].StatusCode))
|
||||
{
|
||||
if (!DataValue.IsGood(values[ii]))
|
||||
{
|
||||
throw ServiceResultException.Create(values[ii].StatusCode, ii, diagnosticInfos, responseHeader.StringTable);
|
||||
}
|
||||
|
||||
// check for valid node class.
|
||||
nodeClass = values[ii].Value as int?;
|
||||
|
||||
if (nodeClass == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not have a valid value for NodeClass: {0}.", values[ii].Value);
|
||||
}
|
||||
var node = ((VariableNode)nodes.Item1[i]);
|
||||
await typeSystem.LoadType(node.DataType);
|
||||
_variableDicts.AddOrUpdate(nodeIdStrs[i], node);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!DataValue.IsGood(values[ii]))
|
||||
{
|
||||
// check for unsupported attributes.
|
||||
if (values[ii].StatusCode == StatusCodes.BadAttributeIdInvalid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore errors on optional attributes
|
||||
if (StatusCode.IsBad(values[ii].StatusCode))
|
||||
{
|
||||
if (attributeId == Attributes.AccessRestrictions ||
|
||||
attributeId == Attributes.Description ||
|
||||
attributeId == Attributes.RolePermissions ||
|
||||
attributeId == Attributes.UserRolePermissions ||
|
||||
attributeId == Attributes.DataTypeDefinition ||
|
||||
attributeId == Attributes.AccessLevelEx ||
|
||||
attributeId == Attributes.UserWriteMask ||
|
||||
attributeId == Attributes.WriteMask)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// all supported attributes must be readable.
|
||||
if (attributeId != Attributes.Value)
|
||||
{
|
||||
throw ServiceResultException.Create(values[ii].StatusCode, ii, diagnosticInfos, responseHeader.StringTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attributes[attributeId] = values[ii];
|
||||
}
|
||||
|
||||
Node node;
|
||||
DataValue value;
|
||||
switch ((NodeClass)nodeClass.Value)
|
||||
{
|
||||
default:
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not have a valid value for NodeClass: {0}.", nodeClass.Value);
|
||||
}
|
||||
|
||||
case NodeClass.Object:
|
||||
{
|
||||
ObjectNode objectNode = new ObjectNode();
|
||||
|
||||
value = attributes[Attributes.EventNotifier];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Object does not support the EventNotifier attribute.");
|
||||
}
|
||||
|
||||
objectNode.EventNotifier = (byte)value.GetValue(typeof(byte));
|
||||
node = objectNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeClass.ObjectType:
|
||||
{
|
||||
ObjectTypeNode objectTypeNode = new ObjectTypeNode();
|
||||
|
||||
value = attributes[Attributes.IsAbstract];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "ObjectType does not support the IsAbstract attribute.");
|
||||
}
|
||||
|
||||
objectTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
|
||||
node = objectTypeNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeClass.Variable:
|
||||
{
|
||||
VariableNode variableNode = new VariableNode();
|
||||
|
||||
// DataType Attribute
|
||||
value = attributes[Attributes.DataType];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the DataType attribute.");
|
||||
}
|
||||
|
||||
variableNode.DataType = (NodeId)value.GetValue(typeof(NodeId));
|
||||
|
||||
// ValueRank Attribute
|
||||
value = attributes[Attributes.ValueRank];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the ValueRank attribute.");
|
||||
}
|
||||
|
||||
variableNode.ValueRank = (int)value.GetValue(typeof(int));
|
||||
|
||||
// ArrayDimensions Attribute
|
||||
value = attributes[Attributes.ArrayDimensions];
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
if (value.Value == null)
|
||||
{
|
||||
variableNode.ArrayDimensions = Array.Empty<uint>();
|
||||
}
|
||||
else
|
||||
{
|
||||
variableNode.ArrayDimensions = (uint[])value.GetValue(typeof(uint[]));
|
||||
}
|
||||
}
|
||||
|
||||
// AccessLevel Attribute
|
||||
value = attributes[Attributes.AccessLevel];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the AccessLevel attribute.");
|
||||
}
|
||||
|
||||
variableNode.AccessLevel = (byte)value.GetValue(typeof(byte));
|
||||
|
||||
// UserAccessLevel Attribute
|
||||
value = attributes[Attributes.UserAccessLevel];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the UserAccessLevel attribute.");
|
||||
}
|
||||
|
||||
variableNode.UserAccessLevel = (byte)value.GetValue(typeof(byte));
|
||||
|
||||
// Historizing Attribute
|
||||
value = attributes[Attributes.Historizing];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Variable does not support the Historizing attribute.");
|
||||
}
|
||||
|
||||
variableNode.Historizing = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
// MinimumSamplingInterval Attribute
|
||||
value = attributes[Attributes.MinimumSamplingInterval];
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
variableNode.MinimumSamplingInterval = Convert.ToDouble(attributes[Attributes.MinimumSamplingInterval].Value);
|
||||
}
|
||||
|
||||
// AccessLevelEx Attribute
|
||||
value = attributes[Attributes.AccessLevelEx];
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
variableNode.AccessLevelEx = (uint)value.GetValue(typeof(uint));
|
||||
}
|
||||
|
||||
node = variableNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeClass.VariableType:
|
||||
{
|
||||
VariableTypeNode variableTypeNode = new VariableTypeNode();
|
||||
|
||||
// IsAbstract Attribute
|
||||
value = attributes[Attributes.IsAbstract];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "VariableType does not support the IsAbstract attribute.");
|
||||
}
|
||||
|
||||
variableTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
// DataType Attribute
|
||||
value = attributes[Attributes.DataType];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "VariableType does not support the DataType attribute.");
|
||||
}
|
||||
|
||||
variableTypeNode.DataType = (NodeId)value.GetValue(typeof(NodeId));
|
||||
|
||||
// ValueRank Attribute
|
||||
value = attributes[Attributes.ValueRank];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "VariableType does not support the ValueRank attribute.");
|
||||
}
|
||||
|
||||
variableTypeNode.ValueRank = (int)value.GetValue(typeof(int));
|
||||
|
||||
// ArrayDimensions Attribute
|
||||
value = attributes[Attributes.ArrayDimensions];
|
||||
|
||||
if (value != null && value.Value != null)
|
||||
{
|
||||
variableTypeNode.ArrayDimensions = (uint[])value.GetValue(typeof(uint[]));
|
||||
}
|
||||
|
||||
node = variableTypeNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeClass.Method:
|
||||
{
|
||||
MethodNode methodNode = new MethodNode();
|
||||
|
||||
// Executable Attribute
|
||||
value = attributes[Attributes.Executable];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Method does not support the Executable attribute.");
|
||||
}
|
||||
|
||||
methodNode.Executable = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
// UserExecutable Attribute
|
||||
value = attributes[Attributes.UserExecutable];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Method does not support the UserExecutable attribute.");
|
||||
}
|
||||
|
||||
methodNode.UserExecutable = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
node = methodNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeClass.DataType:
|
||||
{
|
||||
DataTypeNode dataTypeNode = new DataTypeNode();
|
||||
|
||||
// IsAbstract Attribute
|
||||
value = attributes[Attributes.IsAbstract];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "DataType does not support the IsAbstract attribute.");
|
||||
}
|
||||
|
||||
dataTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
// DataTypeDefinition Attribute
|
||||
value = attributes[Attributes.DataTypeDefinition];
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
dataTypeNode.DataTypeDefinition = value.Value as ExtensionObject;
|
||||
}
|
||||
|
||||
node = dataTypeNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeClass.ReferenceType:
|
||||
{
|
||||
ReferenceTypeNode referenceTypeNode = new ReferenceTypeNode();
|
||||
|
||||
// IsAbstract Attribute
|
||||
value = attributes[Attributes.IsAbstract];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "ReferenceType does not support the IsAbstract attribute.");
|
||||
}
|
||||
|
||||
referenceTypeNode.IsAbstract = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
// Symmetric Attribute
|
||||
value = attributes[Attributes.Symmetric];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "ReferenceType does not support the Symmetric attribute.");
|
||||
}
|
||||
|
||||
referenceTypeNode.Symmetric = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
// InverseName Attribute
|
||||
value = attributes[Attributes.InverseName];
|
||||
|
||||
if (value != null && value.Value != null)
|
||||
{
|
||||
referenceTypeNode.InverseName = (LocalizedText)value.GetValue(typeof(LocalizedText));
|
||||
}
|
||||
|
||||
node = referenceTypeNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NodeClass.View:
|
||||
{
|
||||
ViewNode viewNode = new ViewNode();
|
||||
|
||||
// EventNotifier Attribute
|
||||
value = attributes[Attributes.EventNotifier];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "View does not support the EventNotifier attribute.");
|
||||
}
|
||||
|
||||
viewNode.EventNotifier = (byte)value.GetValue(typeof(byte));
|
||||
|
||||
// ContainsNoLoops Attribute
|
||||
value = attributes[Attributes.ContainsNoLoops];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "View does not support the ContainsNoLoops attribute.");
|
||||
}
|
||||
|
||||
viewNode.ContainsNoLoops = (bool)value.GetValue(typeof(bool));
|
||||
|
||||
node = viewNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NodeId Attribute
|
||||
value = attributes[Attributes.NodeId];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the NodeId attribute.");
|
||||
}
|
||||
|
||||
node.NodeId = (NodeId)value.GetValue(typeof(NodeId));
|
||||
node.NodeClass = (NodeClass)nodeClass.Value;
|
||||
|
||||
// BrowseName Attribute
|
||||
value = attributes[Attributes.BrowseName];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the BrowseName attribute.");
|
||||
}
|
||||
|
||||
node.BrowseName = (QualifiedName)value.GetValue(typeof(QualifiedName));
|
||||
|
||||
// DisplayName Attribute
|
||||
value = attributes[Attributes.DisplayName];
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw ServiceResultException.Create(StatusCodes.BadUnexpectedError, "Node does not support the DisplayName attribute.");
|
||||
}
|
||||
|
||||
node.DisplayName = (LocalizedText)value.GetValue(typeof(LocalizedText));
|
||||
|
||||
// all optional attributes follow
|
||||
|
||||
// Description Attribute
|
||||
if (attributes.TryGetValue(Attributes.Description, out value) &&
|
||||
value != null && value.Value != null)
|
||||
{
|
||||
node.Description = (LocalizedText)value.GetValue(typeof(LocalizedText));
|
||||
}
|
||||
|
||||
// WriteMask Attribute
|
||||
if (attributes.TryGetValue(Attributes.WriteMask, out value) &&
|
||||
value != null)
|
||||
{
|
||||
node.WriteMask = (uint)value.GetValue(typeof(uint));
|
||||
}
|
||||
|
||||
// UserWriteMask Attribute
|
||||
if (attributes.TryGetValue(Attributes.UserWriteMask, out value) &&
|
||||
value != null)
|
||||
{
|
||||
node.UserWriteMask = (uint)value.GetValue(typeof(uint));
|
||||
}
|
||||
|
||||
// RolePermissions Attribute
|
||||
if (attributes.TryGetValue(Attributes.RolePermissions, out value) &&
|
||||
value != null)
|
||||
{
|
||||
ExtensionObject[] rolePermissions = value.Value as ExtensionObject[];
|
||||
|
||||
if (rolePermissions != null)
|
||||
{
|
||||
node.RolePermissions = new RolePermissionTypeCollection();
|
||||
|
||||
foreach (ExtensionObject rolePermission in rolePermissions)
|
||||
{
|
||||
node.RolePermissions.Add(rolePermission.Body as RolePermissionType);
|
||||
}
|
||||
_logAction?.Invoke(3, this, $"获取服务器节点信息失败{nodes.Item2[i]}", null);
|
||||
}
|
||||
}
|
||||
|
||||
// UserRolePermissions Attribute
|
||||
if (attributes.TryGetValue(Attributes.UserRolePermissions, out value) &&
|
||||
value != null)
|
||||
{
|
||||
ExtensionObject[] userRolePermissions = value.Value as ExtensionObject[];
|
||||
|
||||
if (userRolePermissions != null)
|
||||
{
|
||||
node.UserRolePermissions = new RolePermissionTypeCollection();
|
||||
|
||||
foreach (ExtensionObject rolePermission in userRolePermissions)
|
||||
{
|
||||
node.UserRolePermissions.Add(rolePermission.Body as RolePermissionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AccessRestrictions Attribute
|
||||
if (attributes.TryGetValue(Attributes.AccessRestrictions, out value) &&
|
||||
value != null)
|
||||
{
|
||||
node.AccessRestrictions = (ushort)value.GetValue(typeof(ushort));
|
||||
}
|
||||
|
||||
return node;
|
||||
return nodes.Item1.ToList();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a dictionary of attributes to read for a nodeclass.
|
||||
/// </summary>
|
||||
private IDictionary<uint, DataValue> CreateAttributes(NodeClass nodeclass = NodeClass.Unspecified, bool optionalAttributes = true)
|
||||
{
|
||||
// Attributes to read for all types of nodes
|
||||
var attributes = new SortedDictionary<uint, DataValue>() {
|
||||
{ Attributes.NodeId, null },
|
||||
{ Attributes.NodeClass, null },
|
||||
{ Attributes.BrowseName, null },
|
||||
{ Attributes.DisplayName, null },
|
||||
};
|
||||
|
||||
switch (nodeclass)
|
||||
{
|
||||
case NodeClass.Object:
|
||||
attributes.Add(Attributes.EventNotifier, null);
|
||||
break;
|
||||
|
||||
case NodeClass.Variable:
|
||||
attributes.Add(Attributes.DataType, null);
|
||||
attributes.Add(Attributes.ValueRank, null);
|
||||
attributes.Add(Attributes.ArrayDimensions, null);
|
||||
attributes.Add(Attributes.AccessLevel, null);
|
||||
attributes.Add(Attributes.UserAccessLevel, null);
|
||||
attributes.Add(Attributes.Historizing, null);
|
||||
attributes.Add(Attributes.MinimumSamplingInterval, null);
|
||||
attributes.Add(Attributes.AccessLevelEx, null);
|
||||
break;
|
||||
|
||||
case NodeClass.Method:
|
||||
attributes.Add(Attributes.Executable, null);
|
||||
attributes.Add(Attributes.UserExecutable, null);
|
||||
break;
|
||||
|
||||
case NodeClass.ObjectType:
|
||||
attributes.Add(Attributes.IsAbstract, null);
|
||||
break;
|
||||
|
||||
case NodeClass.VariableType:
|
||||
attributes.Add(Attributes.IsAbstract, null);
|
||||
attributes.Add(Attributes.DataType, null);
|
||||
attributes.Add(Attributes.ValueRank, null);
|
||||
attributes.Add(Attributes.ArrayDimensions, null);
|
||||
break;
|
||||
|
||||
case NodeClass.ReferenceType:
|
||||
attributes.Add(Attributes.IsAbstract, null);
|
||||
attributes.Add(Attributes.Symmetric, null);
|
||||
attributes.Add(Attributes.InverseName, null);
|
||||
break;
|
||||
|
||||
case NodeClass.DataType:
|
||||
attributes.Add(Attributes.IsAbstract, null);
|
||||
attributes.Add(Attributes.DataTypeDefinition, null);
|
||||
break;
|
||||
|
||||
case NodeClass.View:
|
||||
attributes.Add(Attributes.EventNotifier, null);
|
||||
attributes.Add(Attributes.ContainsNoLoops, null);
|
||||
break;
|
||||
|
||||
default:
|
||||
// build complete list of attributes.
|
||||
attributes = new SortedDictionary<uint, DataValue> {
|
||||
{ Attributes.NodeId, null },
|
||||
{ Attributes.NodeClass, null },
|
||||
{ Attributes.BrowseName, null },
|
||||
{ Attributes.DisplayName, null },
|
||||
//{ Attributes.Description, null },
|
||||
//{ Attributes.WriteMask, null },
|
||||
//{ Attributes.UserWriteMask, null },
|
||||
{ Attributes.DataType, null },
|
||||
{ Attributes.ValueRank, null },
|
||||
{ Attributes.ArrayDimensions, null },
|
||||
{ Attributes.AccessLevel, null },
|
||||
{ Attributes.UserAccessLevel, null },
|
||||
{ Attributes.MinimumSamplingInterval, null },
|
||||
{ Attributes.Historizing, null },
|
||||
{ Attributes.EventNotifier, null },
|
||||
{ Attributes.Executable, null },
|
||||
{ Attributes.UserExecutable, null },
|
||||
{ Attributes.IsAbstract, null },
|
||||
{ Attributes.InverseName, null },
|
||||
{ Attributes.Symmetric, null },
|
||||
{ Attributes.ContainsNoLoops, null },
|
||||
{ Attributes.DataTypeDefinition, null },
|
||||
//{ Attributes.RolePermissions, null },
|
||||
//{ Attributes.UserRolePermissions, null },
|
||||
//{ Attributes.AccessRestrictions, null },
|
||||
{ Attributes.AccessLevelEx, null }
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (optionalAttributes)
|
||||
{
|
||||
attributes.Add(Attributes.Description, null);
|
||||
attributes.Add(Attributes.WriteMask, null);
|
||||
attributes.Add(Attributes.UserWriteMask, null);
|
||||
attributes.Add(Attributes.RolePermissions, null);
|
||||
attributes.Add(Attributes.UserRolePermissions, null);
|
||||
attributes.Add(Attributes.AccessRestrictions, null);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 特性
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public static class JTokenUtil
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
tagValue = JToken.Parse("\"" + item + "\"");
|
||||
tagValue = JToken.Parse($"\"{item}\"");
|
||||
}
|
||||
|
||||
return tagValue;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace ThingsGateway.Foundation.Core
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.OnError(ex.Message);
|
||||
this.OnError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace ThingsGateway.Foundation.Core
|
||||
catch (Exception ex)
|
||||
{
|
||||
fileStorage = null;
|
||||
msg = ex.Message;
|
||||
msg = ex.ToString();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
/// 输出警示日志
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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; }
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -108,10 +108,6 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最大BufferLength
|
||||
/// </summary>
|
||||
public static int MaxBufferLength { get; set; } = 1024 * 1024 * 10;
|
||||
|
||||
/// <summary>
|
||||
/// 命中BufferLength
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.BreakOut(default, false, ex.Message);
|
||||
this.BreakOut(default, false, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,20 +398,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/>
|
||||
|
||||
@@ -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);
|
||||
@@ -526,20 +523,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/>
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -498,9 +504,9 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
this.Clear();
|
||||
|
||||
this.m_serverState = ServerState.Stopped;
|
||||
if (this.PluginsManager.Enable)
|
||||
if (this.PluginsManager?.Enable == true)
|
||||
{
|
||||
this.m_pluginsManager.Raise(nameof(IServerStopedPlugin.OnServerStoped), this, new ServiceStateEventArgs(this.m_serverState, default));
|
||||
this.m_pluginsManager?.Raise(nameof(IServerStopedPlugin.OnServerStoped), this, new ServiceStateEventArgs(this.m_serverState, default));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -540,11 +546,11 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
this.Clear();
|
||||
|
||||
this.m_serverState = ServerState.Disposed;
|
||||
if (this.PluginsManager.Enable)
|
||||
if (this.PluginsManager?.Enable == true)
|
||||
{
|
||||
this.m_pluginsManager.Raise(nameof(IServerStopedPlugin.OnServerStoped), this, new ServiceStateEventArgs(this.m_serverState, default));
|
||||
this.m_pluginsManager?.Raise(nameof(IServerStopedPlugin.OnServerStoped), this, new ServiceStateEventArgs(this.m_serverState, default));
|
||||
}
|
||||
this.PluginsManager.SafeDispose();
|
||||
this.PluginsManager?.SafeDispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.OnError(ex.Message);
|
||||
this.OnError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
/// 日志记录器
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
{
|
||||
if (this.ReturnException)
|
||||
{
|
||||
client.Send(ex.Message);
|
||||
client.Send(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ 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;
|
||||
@@ -64,7 +64,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;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
this.m_client.ClearReceiver();
|
||||
this.m_resetEventForComplateRead.SafeDispose();
|
||||
//this.m_resetEventForComplateRead.SafeDispose();
|
||||
this.m_resetEventForRead.SafeDispose();
|
||||
this.m_byteBlock = null;
|
||||
base.Dispose(disposing);
|
||||
@@ -85,7 +85,7 @@ namespace ThingsGateway.Foundation.Sockets
|
||||
{
|
||||
this.m_byteBlock = default;
|
||||
this.m_requestInfo = default;
|
||||
this.m_resetEventForComplateRead.Set();
|
||||
this.m_resetEventForComplateRead.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 字节异步
|
||||
}
|
||||
}
|
||||
@@ -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 发送
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>3.0.0.8</Version>
|
||||
<Version>3.0.0.16</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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}"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ public class OPCDAClient : CollectBase
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, ToString());
|
||||
LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -200,6 +200,7 @@ public class OPCUAClient : CollectBase
|
||||
UserName = driverPropertys.UserName,
|
||||
Password = driverPropertys.Password,
|
||||
CheckDomain = driverPropertys.CheckDomain,
|
||||
LoadType = driverPropertys.LoadType,
|
||||
};
|
||||
if (_plc == null)
|
||||
{
|
||||
@@ -282,7 +283,7 @@ public class OPCUAClient : CollectBase
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, ToString());
|
||||
LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
|
||||
namespace ThingsGateway.Plugin.OPCUA;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -51,6 +52,12 @@ public class OPCUAClientProperty : CollectDriverPropertyBase
|
||||
[DeviceProperty("使用SourceTime", "")]
|
||||
public bool SourceTimestampEnable { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 加载服务端数据类型
|
||||
/// </summary>
|
||||
[DeviceProperty("加载服务端数据类型")]
|
||||
public bool LoadType { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 激活订阅
|
||||
/// </summary>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,10 @@
|
||||
<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 />
|
||||
|
||||
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
|
||||
连接
|
||||
|
||||
@@ -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());
|
||||
@@ -147,6 +152,7 @@ public partial class OPCUAImportVariable
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.UserName), Value = PLC.OPCNode.UserName, Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.UserName)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.Password), Value = PLC.OPCNode.Password, Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.Password)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.CheckDomain), Value = PLC.OPCNode.CheckDomain.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.CheckDomain)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.LoadType), Value = PLC.OPCNode.LoadType.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.LoadType)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.IsUseSecurity), Value = PLC.OPCNode.IsUseSecurity.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.IsUseSecurity)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.ActiveSubscribe), Value = true.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.ActiveSubscribe)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.DeadBand), Value = PLC.OPCNode.DeadBand.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.DeadBand)).GetCustomAttribute<DevicePropertyAttribute>().Description });
|
||||
@@ -164,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);
|
||||
});
|
||||
@@ -205,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)
|
||||
{
|
||||
@@ -231,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();
|
||||
}
|
||||
@@ -246,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
325
framework/Plugin/ThingsGateway.Plugin.QuestDB/QuestDB.cs
Normal file
325
framework/Plugin/ThingsGateway.Plugin.QuestDB/QuestDB.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
||||
<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25 md %25dir%25 
copy "$(TargetDir)*QuestDB*.dll" %25dir%25



" />
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
15
framework/Plugin/ThingsGateway.Plugin.SQLDB/GlobalUsings.cs
Normal file
15
framework/Plugin/ThingsGateway.Plugin.SQLDB/GlobalUsings.cs
Normal 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;
|
||||
334
framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDB.cs
Normal file
334
framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDB.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
37
framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDBProperty.cs
Normal file
37
framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLDBProperty.cs
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
52
framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLRealValue.cs
Normal file
52
framework/Plugin/ThingsGateway.Plugin.SQLDB/SQLRealValue.cs
Normal 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; }
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
||||
<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25 md %25dir%25 
copy "$(TargetDir)*SQLDB*.dll" %25dir%25



" />
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -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;
|
||||
@@ -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 SqlSugar.TDengine;
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Gateway.Core;
|
||||
/// <summary>
|
||||
/// 历史数据表
|
||||
/// </summary>
|
||||
[SugarTable("historyValue")]
|
||||
public class TDHistoryValue : STable
|
||||
{
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true, InsertServerTime = true)]
|
||||
[Description("上传时间")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[Description("采集时间")]
|
||||
public DateTime CollectTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[Description("设备名称")]
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量名称
|
||||
/// </summary>
|
||||
[Description("变量名称")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[Description("是否在线")]
|
||||
public bool IsOnline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量值
|
||||
/// </summary>
|
||||
[Description("变量值")]
|
||||
[SugarColumn(Length = 18, DecimalDigits = 2)]
|
||||
public double Value { get; set; }
|
||||
}
|
||||
325
framework/Plugin/ThingsGateway.Plugin.TDengineDB/TDengineDB.cs
Normal file
325
framework/Plugin/ThingsGateway.Plugin.TDengineDB/TDengineDB.cs
Normal 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.SQLDB;
|
||||
public class TDengineDB : UpLoadBase
|
||||
{
|
||||
private readonly ConcurrentQueue<TDHistoryValue> DeviceVariableRunTimes = new();
|
||||
private readonly TDengineDBProperty driverPropertys = new();
|
||||
private readonly TDengineDBVariableProperty variablePropertys = new();
|
||||
private GlobalDeviceData _globalDeviceData;
|
||||
private TypeAdapterConfig _config;
|
||||
|
||||
private List<DeviceVariableRunTime> _uploadVariables = new();
|
||||
private TimerTick exTimerTick;
|
||||
public TDengineDB()
|
||||
{
|
||||
_config = new TypeAdapterConfig();
|
||||
_config.ForType<DeviceVariableRunTime, HistoryValue>()
|
||||
.Map(dest => dest.Value, (src) => ValueReturn(src));
|
||||
}
|
||||
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(TDHistoryValue));
|
||||
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<TDHistoryValue>>(_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.TDengine,//数据库类型
|
||||
IsAutoCloseConnection = true, //不设成true要手动close
|
||||
ConfigureExternalServices = configureExternalServices,
|
||||
}
|
||||
);
|
||||
AopSetting(sqlSugarClient);//aop配置
|
||||
return sqlSugarClient;
|
||||
}
|
||||
|
||||
private async Task InserableAsync(SqlSugarClient db, List<TDHistoryValue> 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<TDHistoryValue>>();
|
||||
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<TDHistoryValue>(_config));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#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 TDengineDBProperty : UpDriverPropertyBase
|
||||
{
|
||||
[DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "Host=localhost;Port=6030;Username=root;Password=taosdata;Database=test";
|
||||
[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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 TDengineDBVariableProperty : VariablePropertyBase
|
||||
{
|
||||
[VariableProperty("启用", "")]
|
||||
public bool Enable { get; set; } = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
||||
<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25 md %25dir%25 
copy "$(TargetDir)*TDengineDB*.dll" %25dir%25



" />
|
||||
|
||||
</Target>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SqlSugar.TDengineCore" Version="2.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
||||
<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25 md %25dir%25 
copy "$(TargetDir)*ThingsGateway.Plugin.TDengineDB*.dll" %25dir%25



" />
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -52,10 +52,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "W
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "Web\ThingsGateway.Core\ThingsGateway.Core.csproj", "{51313113-7BB8-494E-9C24-6787BECE39BB}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{79E7042F-F9E3-4D87-BFA9-4B7DD9736735}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateway", "Gateway", "{000C3C62-345E-451C-8CEE-6F2C6A087116}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.ApiController", "Web\ThingsGateway.Gateway.ApiController\ThingsGateway.Gateway.ApiController.csproj", "{5D7BE567-2345-46C8-9F54-DDC1DA96D198}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "Web\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}"
|
||||
@@ -64,8 +60,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Blazo
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Core", "Web\ThingsGateway.Gateway.Core\ThingsGateway.Gateway.Core.csproj", "{5CD79F91-7182-4A9D-9BEF-4DF410C782D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugin", "Plugin", "{CC8D0880-B73E-4DFC-9052-86504728708E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Plugin\Directory.Build.props = Plugin\Directory.Build.props
|
||||
@@ -105,6 +99,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.De
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Photino", "Demo\ThingsGateway.Foundation.Demo.Photino\ThingsGateway.Foundation.Demo.Photino.csproj", "{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLDB", "Plugin\ThingsGateway.Plugin.SQLDB\ThingsGateway.Plugin.SQLDB.csproj", "{7EBD5500-0DA0-415A-831D-5DC350917501}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.QuestDB", "Plugin\ThingsGateway.Plugin.QuestDB\ThingsGateway.Plugin.QuestDB.csproj", "{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.TDengineDB", "Plugin\ThingsGateway.Plugin.TDengineDB\ThingsGateway.Plugin.TDengineDB.csproj", "{2C827B2C-75DF-413B-9AB2-2D1B438AC082}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -231,6 +231,18 @@ Global
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -242,21 +254,18 @@ Global
|
||||
{566783A4-222B-46F5-AA12-0753997B3254} = {0874CBC5-C583-4FAD-BA93-94571D446898}
|
||||
{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF} = {0874CBC5-C583-4FAD-BA93-94571D446898}
|
||||
{9695B353-D773-40DD-B65E-7B10EB0C16EC} = {0874CBC5-C583-4FAD-BA93-94571D446898}
|
||||
{799C49A4-8E23-475A-A82D-080854718BEE} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}
|
||||
{616CA361-B667-42C8-B4DC-097C7CD39830} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{16C62A28-BACE-4391-91F8-C2D78D063A1E} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{8FA03089-322F-44CB-8E4B-F2637388E944} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{14FF7150-6DB7-455B-AD00-6AB4DE37855B} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{799C49A4-8E23-475A-A82D-080854718BEE} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{616CA361-B667-42C8-B4DC-097C7CD39830} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{16C62A28-BACE-4391-91F8-C2D78D063A1E} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{8FA03089-322F-44CB-8E4B-F2637388E944} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{14FF7150-6DB7-455B-AD00-6AB4DE37855B} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{51313113-7BB8-494E-9C24-6787BECE39BB} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}
|
||||
{79E7042F-F9E3-4D87-BFA9-4B7DD9736735} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{000C3C62-345E-451C-8CEE-6F2C6A087116} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5D7BE567-2345-46C8-9F54-DDC1DA96D198} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{CD0F211A-F65B-4026-9750-68AC3C70D012} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{5CD79F91-7182-4A9D-9BEF-4DF410C782D2} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{51313113-7BB8-494E-9C24-6787BECE39BB} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5D7BE567-2345-46C8-9F54-DDC1DA96D198} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{CD0F211A-F65B-4026-9750-68AC3C70D012} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5CD79F91-7182-4A9D-9BEF-4DF410C782D2} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{A723D4D7-B796-4D97-BA68-95E5696C9559} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
@@ -269,6 +278,9 @@ Global
|
||||
{681F774F-7B0B-450A-917C-1385E1847CA6} = {237C7BC5-7B07-40B5-AF42-CE2F8E0893C3}
|
||||
{637A662B-7B70-4CE8-8F5F-0A095B9D77EC} = {95008B83-0324-4A7C-80DE-2BBDDD1A9099}
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB} = {95008B83-0324-4A7C-80DE-2BBDDD1A9099}
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>3.0.0.8</Version>
|
||||
<Version>3.0.0.16</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Authors>Diego</Authors>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user