Compare commits

...

24 Commits

Author SHA1 Message Date
Kimdiego2098
4a85e31a4f update 3.0.0.16 2023-10-18 00:38:36 +08:00
Kimdiego2098
302c270ad5 opcuaClient浏览空间 添加是否显示子变量的选项 2023-10-18 00:36:44 +08:00
Kimdiego2098
3c1517d0f3 更改日志输出内容 2023-10-18 00:15:07 +08:00
Kimdiego2098
f9fb222044 update 3.0.0.15 2023-10-18 00:07:24 +08:00
Kimdiego2098
e8edc02ba3 增加不支持单文件发布的说明 2023-10-17 23:47:24 +08:00
Kimdiego2098
95a44e3053 update tdengineDB plugin 2023-10-17 23:43:48 +08:00
Kimdiego2098
74a9fe9a87 update touchsocket 2023-10-17 23:12:19 +08:00
Kimdiego2098
4d03f9ea1a TD时序库插件,创建时间更改为主键 2023-10-17 23:08:09 +08:00
Kimdiego2098
67c96ca991 update touchsocket 2023-10-17 23:06:21 +08:00
Kimdiego2098
88fb793c68 更新nuget包 2023-10-17 21:00:50 +08:00
Kimdiego2098
d6d02d8cc5 update SQLDB 2023-10-16 20:50:10 +08:00
Kimdiego2098
c5a3f8e2e3 update touchsocket and other 2023-10-16 20:36:51 +08:00
Kimdiego2098
27e8653a1a 3.0.0.13 2023-10-16 17:44:09 +08:00
Kimdiego2098
863beda82c 增加关系库存储插件; 2023-10-16 17:40:17 +08:00
Kimdiego2098
bac84c3ecd 增加时序库存储插件; 2023-10-16 17:40:13 +08:00
Kimdiego2098
2fca2ad9f8 更新pro用户列表 2023-10-16 17:39:14 +08:00
Kimdiego2098
dd75286fe0 3.0.0.12 2023-10-16 08:47:39 +08:00
Kimdiego2098
7f91792cf1 opcuaclient添加是否加载服务端数据类型的选项 2023-10-16 08:46:46 +08:00
Kimdiego2098
0e0ccad311 fix:tcpservice dispose err 2023-10-16 08:46:32 +08:00
Diego2098
0691f72e67 !9 增加可选择安全订阅
Merge pull request !9 from youthalan/N/A
2023-10-16 00:33:19 +00:00
youthalan
7e38a51720 增加可选择安全订阅,以加快订阅速度
Signed-off-by: youthalan <youthalan@126.com>
2023-10-16 00:26:59 +00:00
Kimdiego2098
34ca8243a3 更新3.0.0.11 2023-10-15 20:26:18 +08:00
Diego2098
112fea7632 读取数据类型方法改为批量 2023-10-15 20:21:23 +08:00
Kimdiego2098
378763e4ee 同步Pro版本 2023-10-13 20:14:35 +08:00
176 changed files with 2900 additions and 1477 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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" />

View File

@@ -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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>
</PropertyGroup>
</Project>

View File

@@ -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()
{

View File

@@ -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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -149,7 +149,7 @@ namespace ThingsGateway.Foundation.Sockets
}
catch (Exception ex)
{
this.BreakOut(default, false, ex.Message);
this.BreakOut(default, false, ex.ToString());
}
}
@@ -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/>

View File

@@ -71,7 +71,7 @@ namespace ThingsGateway.Foundation.Sockets
private DelaySender m_delaySender;
private volatile bool m_online;
private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
private readonly EasyLock m_semaphore = new();
private readonly InternalTcpCore m_tcpCore;
#endregion
@@ -334,10 +334,7 @@ namespace ThingsGateway.Foundation.Sockets
var iPHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty) ?? throw new ArgumentNullException(nameof(IPHost), "iPHost不能为空。");
this.MainSocket.SafeDispose();
var socket = this.CreateSocket(iPHost);
this.PrivateOnConnecting(new ConnectingEventArgs(socket))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
this.PrivateOnConnecting(new ConnectingEventArgs(socket)).GetFalseAwaitResult();
if (timeout == 5000)
{
socket.Connect(iPHost.Host, iPHost.Port);
@@ -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/>

View File

@@ -31,6 +31,12 @@ namespace ThingsGateway.Foundation.Sockets
/// </summary>
public class TcpService<TClient> : TcpServiceBase, ITcpService<TClient> where TClient : SocketClient, new()
{
/// <inheritdoc/>
public override int SendBufferSize => 1024 * 1024 * 10;
/// <inheritdoc/>
public override int ReceiveBufferSize => 1024 * 1024 * 10;
/// <summary>
/// 构造函数
/// </summary>
@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,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();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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)
{

View File

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

View File

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

View File

@@ -37,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">
连接

View File

@@ -43,6 +43,11 @@ public partial class OPCUAImportVariable
/// </summary>
[Parameter]
public ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient PLC { get; set; }
/// <summary>
/// 是否显示子变量
/// </summary>
[Parameter]
public bool IsShowSubvariable { get; set; }
private List<ReferenceDescription> Actived
{
@@ -73,7 +78,7 @@ public partial class OPCUAImportVariable
//动态加载子项时,导出内容需要添加手动加载代码
foreach (var node in Selected.ToList())
{
List<OPCUATagModel> nodes = await PopulateBranchAsync((NodeId)node.NodeId, true);
List<OPCUATagModel> nodes = await PopulateBranchAsync((NodeId)node.NodeId, true, IsShowSubvariable);
if (nodes.Count > 0)
{
Selected.AddRange(nodes.SelectMany(a => a.GetAllTags()).Select(a => a.Tag).Where(a => a != null).ToList());
@@ -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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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; }
}

View File

@@ -0,0 +1,325 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion;
using Mapster;
using Microsoft.Extensions.Hosting;
using SqlSugar;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Plugin.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));
}
}

View File

@@ -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;
}

View File

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

View File

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

View File

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

View File

@@ -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}

View File

@@ -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