mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-24 04:17:08 +08:00
Compare commits
288 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cbf289b50 | ||
|
|
4097da79a5 | ||
|
|
91b7ae554f | ||
|
|
3121aa2542 | ||
|
|
4bf895e6e1 | ||
|
|
0c5489e920 | ||
|
|
d63c3aaa80 | ||
|
|
4f188ea6cc | ||
|
|
acb17018ae | ||
|
|
2affe2988d | ||
|
|
4174dd2206 | ||
|
|
e1c492f238 | ||
|
|
fb08e34fa3 | ||
|
|
a1793a0afe | ||
|
|
4da9763b49 | ||
|
|
81e0918bd0 | ||
|
|
c1e064f06d | ||
|
|
1c52be8b47 | ||
|
|
bcd82055ca | ||
|
|
c47d95d170 | ||
|
|
3e62f1ad51 | ||
|
|
8dcae973ef | ||
|
|
4cf35f7294 | ||
|
|
94c77d151b | ||
|
|
7f600e2b4b | ||
|
|
c809d0ba87 | ||
|
|
50f038ec89 | ||
|
|
9199a255a2 | ||
|
|
d324537b47 | ||
|
|
d0c05685f7 | ||
|
|
1063c930b5 | ||
|
|
79cbd44366 | ||
|
|
7fdac1c5cb | ||
|
|
0c0cf72ebb | ||
|
|
8e2fe175ed | ||
|
|
d1cff037c9 | ||
|
|
fc88a2fafa | ||
|
|
45fcceb056 | ||
|
|
7043477038 | ||
|
|
7dd685cf54 | ||
|
|
5f5e4969c0 | ||
|
|
8a53fd19e9 | ||
|
|
baf4714c36 | ||
|
|
7ba9ac7a5b | ||
|
|
85b8f26e8e | ||
|
|
594a0f1410 | ||
|
|
d317d757d7 | ||
|
|
fdf0ba6318 | ||
|
|
15bf7de5fa | ||
|
|
d3402b058e | ||
|
|
e7dfdd4031 | ||
|
|
b2dd7b6364 | ||
|
|
9bd6d9abbf | ||
|
|
cd14428fea | ||
|
|
19d9f03c2b | ||
|
|
0d57e72bbf | ||
|
|
329516a61b | ||
|
|
d566869589 | ||
|
|
9cb8d8e6c7 | ||
|
|
9de3c57e5d | ||
|
|
f32ff92b0b | ||
|
|
88d71e271e | ||
|
|
fd9c14612a | ||
|
|
e26e5a160f | ||
|
|
b836bfed22 | ||
|
|
a4b598c6d0 | ||
|
|
c9ab755839 | ||
|
|
9920edba53 | ||
|
|
12bd7280d1 | ||
|
|
d30ea7f63b | ||
|
|
ebd3390db6 | ||
|
|
9a374a9ebc | ||
|
|
b1bc22cb08 | ||
|
|
4930d53890 | ||
|
|
c31327b5bc | ||
|
|
3f2aa1f1e1 | ||
|
|
6e78c00a96 | ||
|
|
c27dde085e | ||
|
|
d26cc308c0 | ||
|
|
fb1efdf290 | ||
|
|
3c99f2a472 | ||
|
|
affe9a44e0 | ||
|
|
43730fa519 | ||
|
|
d39aa22b09 | ||
|
|
e232a6b6ea | ||
|
|
71ebb36fe9 | ||
|
|
78a0b86327 | ||
|
|
2636c16a97 | ||
|
|
fd77c0242d | ||
|
|
e74819a900 | ||
|
|
9b7f696c9b | ||
|
|
0230d614e7 | ||
|
|
252d99ad78 | ||
|
|
1ffc200350 | ||
|
|
807d89b2b2 | ||
|
|
4013afa1f1 | ||
|
|
a580927ceb | ||
|
|
bf2cf52034 | ||
|
|
81bb8b7c31 | ||
|
|
a825007fb5 | ||
|
|
988124d96a | ||
|
|
f0de815296 | ||
|
|
0e2d58c887 | ||
|
|
b155382626 | ||
|
|
f362d740af | ||
|
|
4a85e31a4f | ||
|
|
302c270ad5 | ||
|
|
3c1517d0f3 | ||
|
|
f9fb222044 | ||
|
|
e8edc02ba3 | ||
|
|
95a44e3053 | ||
|
|
74a9fe9a87 | ||
|
|
4d03f9ea1a | ||
|
|
67c96ca991 | ||
|
|
88fb793c68 | ||
|
|
d6d02d8cc5 | ||
|
|
c5a3f8e2e3 | ||
|
|
27e8653a1a | ||
|
|
863beda82c | ||
|
|
bac84c3ecd | ||
|
|
2fca2ad9f8 | ||
|
|
dd75286fe0 | ||
|
|
7f91792cf1 | ||
|
|
0e0ccad311 | ||
|
|
0691f72e67 | ||
|
|
7e38a51720 | ||
|
|
34ca8243a3 | ||
|
|
112fea7632 | ||
|
|
378763e4ee | ||
|
|
517bd0394d | ||
|
|
70adb97fb5 | ||
|
|
623d44cabe | ||
|
|
0d479ca00b | ||
|
|
8bc49ef437 | ||
|
|
f83fcec786 | ||
|
|
93690ce40d | ||
|
|
f82c5f2f27 | ||
|
|
a83c1c3899 | ||
|
|
91d6aed109 | ||
|
|
db8f8fe51d | ||
|
|
4596004b17 | ||
|
|
d5540906cb | ||
|
|
90796a979d | ||
|
|
2190a87772 | ||
|
|
c5953b83f8 | ||
|
|
24bc60abf0 | ||
|
|
31eee6b009 | ||
|
|
c5da565a8f | ||
|
|
947cd712e1 | ||
|
|
edc208f96b | ||
|
|
1fb0296ee7 | ||
|
|
6488d3df87 | ||
|
|
56189d78e0 | ||
|
|
bff18127b8 | ||
|
|
363206e0ba | ||
|
|
fd3e378501 | ||
|
|
4ba2fe4c9d | ||
|
|
2c499626ad | ||
|
|
2b581a03c3 | ||
|
|
450c15210a | ||
|
|
65fed8cc93 | ||
|
|
4b64771ea2 | ||
|
|
f39977a6ff | ||
|
|
933b535caa | ||
|
|
8abc5d2f20 | ||
|
|
d8783cd994 | ||
|
|
d5d087feb5 | ||
|
|
6ba3399df7 | ||
|
|
65124b3aa8 | ||
|
|
98597f4726 | ||
|
|
e7981f0d8e | ||
|
|
cf654427c3 | ||
|
|
ff2f628282 | ||
|
|
ae818ca265 | ||
|
|
0f2aed458e | ||
|
|
d486c44ff6 | ||
|
|
ca7b9980bf | ||
|
|
3c71e6a8e3 | ||
|
|
542442864c | ||
|
|
5edb64fa85 | ||
|
|
8dc1c898a3 | ||
|
|
1ed35726b0 | ||
|
|
27fae9ebaa | ||
|
|
b103f25c94 | ||
|
|
abff450274 | ||
|
|
c260736a11 | ||
|
|
166ac2307a | ||
|
|
b21a4e1a4d | ||
|
|
f7dc943fa3 | ||
|
|
bfbd2693ec | ||
|
|
819e71c993 | ||
|
|
9fd0b489a2 | ||
|
|
f5fe9f8dae | ||
|
|
f9ffc18145 | ||
|
|
08db5b983a | ||
|
|
5b3b4c8c50 | ||
|
|
73f914ffc4 | ||
|
|
d6bdd73ed6 | ||
|
|
7370ee7349 | ||
|
|
4574596bac | ||
|
|
4d16855e36 | ||
|
|
13a0d4d282 | ||
|
|
b9cd06b829 | ||
|
|
5b460e8fa2 | ||
|
|
41087edf17 | ||
|
|
2afcc38e38 | ||
|
|
e59ccce25f | ||
|
|
d7425890e8 | ||
|
|
a989a837fb | ||
|
|
db1221da50 | ||
|
|
cf794569ed | ||
|
|
51e5bbab0d | ||
|
|
2c197ed2b2 | ||
|
|
d8fc6665b3 | ||
|
|
c671a79822 | ||
|
|
9d93ce4c41 | ||
|
|
a6d99fe227 | ||
|
|
923b8bca31 | ||
|
|
e2c30d1c88 | ||
|
|
b6d9f2a04e | ||
|
|
57306ea664 | ||
|
|
cd7f3fd02f | ||
|
|
0482e077a8 | ||
|
|
5f986a45ca | ||
|
|
ca7b49c0d5 | ||
|
|
52dd555e6c | ||
|
|
579b1a59f9 | ||
|
|
5299c5c4be | ||
|
|
f7756bccef | ||
|
|
a6b874d160 | ||
|
|
3e5fb3ddcf | ||
|
|
5e6bcb12d3 | ||
|
|
14303f1429 | ||
|
|
96711ba022 | ||
|
|
cbfc0fdbdc | ||
|
|
6e81886c0e | ||
|
|
2d976bc132 | ||
|
|
57f6a476af | ||
|
|
8491ed296e | ||
|
|
cd1288afdc | ||
|
|
ec6c830cb0 | ||
|
|
2f86ccc4bf | ||
|
|
8ca445aec0 | ||
|
|
1e1f27c8a5 | ||
|
|
2b84bde367 | ||
|
|
b52e58551d | ||
|
|
9aceed00bf | ||
|
|
58814f7f74 | ||
|
|
6a70ef9f31 | ||
|
|
82cc4ca500 | ||
|
|
4567fa04ed | ||
|
|
8b98b5d818 | ||
|
|
176d0351af | ||
|
|
d63dc3384b | ||
|
|
1ccd704e30 | ||
|
|
f5d23dbe79 | ||
|
|
75bfe53ac3 | ||
|
|
3308f916dd | ||
|
|
e7140279ca | ||
|
|
1034719f5e | ||
|
|
2c00043a7f | ||
|
|
65c695d9ce | ||
|
|
57253fe46a | ||
|
|
4e5c443440 | ||
|
|
0b3b73d8ec | ||
|
|
921eabc134 | ||
|
|
0faa428751 | ||
|
|
f71a2fdd63 | ||
|
|
4eb9ed8aba | ||
|
|
d7b549abb8 | ||
|
|
95d723c578 | ||
|
|
2fcd853e86 | ||
|
|
07eef7c812 | ||
|
|
b01e0757fa | ||
|
|
32844a20c6 | ||
|
|
5b6532c601 | ||
|
|
2c5b4b4027 | ||
|
|
72d7ecf195 | ||
|
|
2cfa6b4306 | ||
|
|
6f6ffde0ab | ||
|
|
1694739a16 | ||
|
|
95d1e8bfca | ||
|
|
60dec08e3c | ||
|
|
a99d71be93 | ||
|
|
f1331b6a0c | ||
|
|
10d66b642b | ||
|
|
cd2310e4a8 | ||
|
|
1b399cf6b0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -363,4 +363,6 @@ MigrationBackup/
|
||||
FodyWeavers.xsd
|
||||
|
||||
|
||||
/framework/*pro*
|
||||
/framework/*Pro*
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
||||
|
||||
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 [**ThingsGateway.Admin**](https://gitee.com/dotnetchina/ThingsGateway/blob/master/framework/ThingsGateway.Admin.sln)
|
||||
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
|
||||
|
||||
|
||||
## 文档
|
||||
@@ -22,7 +22,9 @@
|
||||
|
||||
[ThingsGateway演示地址](http://120.24.62.140:5000/)
|
||||
|
||||
账户 : **superAdmin** 密码 : **111111**
|
||||
账户 : **superAdmin**
|
||||
|
||||
密码 : **111111**
|
||||
|
||||
## 赞助
|
||||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
[*.cs]
|
||||
|
||||
# CA1822: 将成员标记为 static
|
||||
dotnet_diagnostic.CA1822.severity = none
|
||||
|
||||
# CA1816: Dispose 方法应调用 SuppressFinalize
|
||||
dotnet_diagnostic.CA1816.severity = none
|
||||
|
||||
# CA2254: 模板应为静态表达式
|
||||
dotnet_diagnostic.CA2254.severity = none
|
||||
|
||||
[*.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
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
csharp_style_var_when_type_is_apparent = false:silent
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_style_namespace_declarations = block_scoped:silent
|
||||
csharp_style_prefer_method_group_conversion = true:silent
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
|
||||
# CA2208: 正确实例化参数异常
|
||||
dotnet_diagnostic.CA2208.severity = none
|
||||
|
||||
# IDE0057: 使用范围运算符
|
||||
dotnet_diagnostic.IDE0057.severity = none
|
||||
|
||||
# IDE0056: 使用索引运算符
|
||||
dotnet_diagnostic.IDE0056.severity = none
|
||||
|
||||
# CA1830: 最好使用 StringBuilder 的强类型 Append 和 Insert 方法重载
|
||||
dotnet_diagnostic.CA1830.severity = none
|
||||
|
||||
# CA1847: 将字符型文本用于单个字符查找
|
||||
dotnet_diagnostic.CA1847.severity = none
|
||||
|
||||
[*.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
|
||||
|
||||
[*.{cs,vb}]
|
||||
dotnet_style_qualification_for_field = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_event = false:silent
|
||||
end_of_line = crlf
|
||||
|
||||
# IDE0060: 删除未使用的参数
|
||||
dotnet_diagnostic.IDE0060.severity = none
|
||||
@@ -1,30 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Version>2.0.7.0</Version>
|
||||
<Authors>Diego</Authors>
|
||||
<Product>ThingsGateway</Product>
|
||||
<Copyright>© 2023-present Diego</Copyright>
|
||||
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSource>true</EmbedUntrackedSource>
|
||||
<EmbedAllSources>true</EmbedAllSources>
|
||||
<RepositoryType>Gitee</RepositoryType>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<DelaySign>False</DelaySign>
|
||||
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -1,66 +0,0 @@
|
||||
#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.DynamicApiController;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Admin.Application
|
||||
{
|
||||
/// <summary>
|
||||
/// OpenApi登录控制器
|
||||
/// </summary>
|
||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayOpenApi, Order = 200)]
|
||||
[Route("auth/openapi")]
|
||||
[LoggingMonitor]
|
||||
[Description("OpenApi登录")]
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
public class OpenApiAuthController : IDynamicApiController
|
||||
{
|
||||
private readonly IOpenApiAuthService _authService;
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="OpenApiAuthController"/>
|
||||
/// </summary>
|
||||
/// <param name="authService"></param>
|
||||
public OpenApiAuthController(IOpenApiAuthService authService)
|
||||
{
|
||||
_authService = authService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OpenApi登录
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("login")]
|
||||
[Description(EventSubscriberConst.LoginOpenApi)]
|
||||
public async Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input)
|
||||
{
|
||||
return await _authService.LoginOpenApiAsync(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登出
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("logout")]
|
||||
[Description(EventSubscriberConst.LogoutOpenApi)]
|
||||
public async Task LogoutAsync()
|
||||
{
|
||||
await _authService.LogoutAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
using Furion.DynamicApiController;
|
||||
using Furion.SpecificationDocument;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Swagger登录授权服务
|
||||
/// </summary>
|
||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
|
||||
[Route("Swagger")]
|
||||
public class SwaggerController : IDynamicApiController, IScoped
|
||||
{
|
||||
private readonly ConfigService _configService;
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="SwaggerController"/>
|
||||
/// </summary>
|
||||
/// <param name="sysConfigService"></param>
|
||||
public SwaggerController(ConfigService sysConfigService)
|
||||
{
|
||||
_configService = sysConfigService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swagger登录检查
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("CheckUrl")]
|
||||
[AllowAnonymous, NonUnify]
|
||||
public async Task<int> SwaggerCheckUrlAsync()
|
||||
{
|
||||
var enable = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGERLOGIN_OPEN)).ConfigValue.ToBoolean();
|
||||
return enable ? 401 : 200;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swagger登录
|
||||
/// </summary>
|
||||
/// <param name="auth"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("SubmitUrl")]
|
||||
[AllowAnonymous, NonUnify]
|
||||
public async Task<int> SwaggerSubmitUrlAsync([FromForm] SpecificationAuth auth)
|
||||
{
|
||||
var userName = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGER_NAME)).ConfigValue;
|
||||
var password = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGER_PASSWORD)).ConfigValue;
|
||||
if (auth.UserName == userName && auth.Password == password)
|
||||
{
|
||||
return 200;
|
||||
}
|
||||
return 401;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>ThingsGateway.Admin.ApiController</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:ThingsGateway.Admin.Application.AuthController">
|
||||
<summary>
|
||||
后台登录控制器
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.AuthController.#ctor(ThingsGateway.Admin.Application.IAuthService)">
|
||||
<summary>
|
||||
<inheritdoc cref="T:ThingsGateway.Admin.Application.AuthController"/>
|
||||
</summary>
|
||||
<param name="authService"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.AuthController.LoginAsync(ThingsGateway.Admin.Application.LoginInput)">
|
||||
<summary>
|
||||
后台登录
|
||||
</summary>
|
||||
<param name="input"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.AuthController.LogoutAsync">
|
||||
<summary>
|
||||
后台登出
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Application.FileController">
|
||||
<summary>
|
||||
文件下载
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.FileController.#ctor(ThingsGateway.Admin.Application.IFileService,ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)">
|
||||
<summary>
|
||||
<inheritdoc cref="T:ThingsGateway.Admin.Application.FileController"/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.FileController.DownloadOperateLogAsync(ThingsGateway.Admin.Application.OperateLogInput)">
|
||||
<summary>
|
||||
下载操作日志
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.FileController.DownloadVisitLogAsync(ThingsGateway.Admin.Application.VisitLogInput)">
|
||||
<summary>
|
||||
下载访问日志
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Application.OpenApiAuthController">
|
||||
<summary>
|
||||
OpenApi登录控制器
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.OpenApiAuthController.#ctor(ThingsGateway.Admin.Application.IOpenApiAuthService)">
|
||||
<summary>
|
||||
<inheritdoc cref="T:ThingsGateway.Admin.Application.OpenApiAuthController"/>
|
||||
</summary>
|
||||
<param name="authService"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.OpenApiAuthController.LoginOpenApiAsync(ThingsGateway.Admin.Application.LoginOpenApiInput)">
|
||||
<summary>
|
||||
OpenApi登录
|
||||
</summary>
|
||||
<param name="input"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.OpenApiAuthController.LogoutAsync">
|
||||
<summary>
|
||||
登出
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Application.SwaggerController">
|
||||
<summary>
|
||||
Swagger登录授权服务
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.SwaggerController.#ctor(ThingsGateway.Admin.Application.ConfigService)">
|
||||
<summary>
|
||||
<inheritdoc cref="T:ThingsGateway.Admin.Application.SwaggerController"/>
|
||||
</summary>
|
||||
<param name="sysConfigService"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.SwaggerController.SwaggerCheckUrlAsync">
|
||||
<summary>
|
||||
Swagger登录检查
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Application.SwaggerController.SwaggerSubmitUrlAsync(Furion.SpecificationDocument.SpecificationAuth)">
|
||||
<summary>
|
||||
Swagger登录
|
||||
</summary>
|
||||
<param name="auth"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -1,104 +0,0 @@
|
||||
{
|
||||
"RECORDS": [
|
||||
{
|
||||
"Id": "22222222222222",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_SWAGGER_NAME",
|
||||
"ConfigValue": "admin",
|
||||
"Remark": "swagger账号",
|
||||
"SortCode": "1",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222223",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_SWAGGER_PASSWORD",
|
||||
"ConfigValue": "123456",
|
||||
"Remark": "swagger密码",
|
||||
"SortCode": "2",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222224",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_SWAGGERLOGIN_OPEN",
|
||||
"ConfigValue": "false",
|
||||
"Remark": "swagger开启登录",
|
||||
"SortCode": "3",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
|
||||
{
|
||||
"Id": "22222222222226",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_TITLE",
|
||||
"ConfigValue": "ThingsGateway",
|
||||
"Remark": "标题",
|
||||
"SortCode": "5",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222228",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_COPYRIGHT",
|
||||
"ConfigValue": "ThingsGateway ©2023 Diego",
|
||||
"Remark": "系统版权",
|
||||
"SortCode": "6",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222229",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_COPYRIGHT_URL",
|
||||
"ConfigValue": "https://gitee.com/diego2098/ThingsGateway",
|
||||
"Remark": "系统版权链接地址",
|
||||
"SortCode": "7",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222231",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_PASSWORD",
|
||||
"ConfigValue": "111111",
|
||||
"Remark": "默认用户密码",
|
||||
"SortCode": "8",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222227",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_VERIFICAT_EXPIRES",
|
||||
"ConfigValue": "14400",
|
||||
"Remark": "Verificat过期时间(分)",
|
||||
"SortCode": "9",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222232",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_SINGLE_OPEN",
|
||||
"ConfigValue": "false",
|
||||
"Remark": "单用户登录开关",
|
||||
"SortCode": "10",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222230",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_CAPTCHA_OPEN",
|
||||
"ConfigValue": "true",
|
||||
"Remark": "登录验证码开关",
|
||||
"SortCode": "11",
|
||||
"IsDelete": "false"
|
||||
},
|
||||
{
|
||||
"Id": "22222222222225",
|
||||
"Category": "SYS_CONFIGBASEDEFAULT",
|
||||
"ConfigKey": "CONFIG_REMARK",
|
||||
"ConfigValue": "边缘采集网关",
|
||||
"Remark": "说明",
|
||||
"SortCode": "12",
|
||||
"IsDelete": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"RECORDS": [
|
||||
{
|
||||
"Id": "212725263001001",
|
||||
"Code": "superAdmin",
|
||||
"Name": "超级管理员",
|
||||
"SortCode": "1"
|
||||
},
|
||||
{
|
||||
"Id": "212725263001002",
|
||||
"Code": "admin",
|
||||
"Name": "业务管理员",
|
||||
"SortCode": "2"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"RECORDS": [
|
||||
{
|
||||
"Id": "212725263002001",
|
||||
"Account": "superAdmin",
|
||||
"LastLoginDevice": "PC",
|
||||
"LastLoginIp": "0.0.0.1",
|
||||
"LastLoginTime": "2023-03-03 21:18:43.7092169",
|
||||
"LatestLoginDevice": "PC",
|
||||
"LatestLoginIp": "0.0.0.1",
|
||||
"LatestLoginTime": "2023-03-03 21:19:16.1043309",
|
||||
"Password": "7DA385A25A98388E",
|
||||
"SortCode": "1",
|
||||
"UserEnable": "true",
|
||||
"IsDelete": "false",
|
||||
"UpdateTime": "2023-03-03 21:19:16.1202211"
|
||||
},
|
||||
{
|
||||
"Id": "201725263002001",
|
||||
"Account": "admin",
|
||||
"LastLoginDevice": "PC",
|
||||
"LastLoginIp": "0.0.0.1",
|
||||
"LastLoginTime": "2023-03-03 18:20:49.1875384",
|
||||
"LatestLoginDevice": "PC",
|
||||
"LatestLoginIp": "0.0.0.1",
|
||||
"LatestLoginTime": "2023-03-03 18:23:08.6424099",
|
||||
"Password": "7DA385A25A98388E",
|
||||
"SortCode": "2",
|
||||
"UserEnable": "true",
|
||||
"IsDelete": "false",
|
||||
"UpdateTime": "2023-03-03 18:23:08.6727296"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="SeedData\Json\sys_config.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\sys_relation.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\sys_resource.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\sys_role.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\sys_user.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
|
||||
/// <summary>
|
||||
/// AppBarItems
|
||||
/// </summary>
|
||||
public partial class AppBarItems
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_COPYRIGHT_URL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <20><>Ȩ
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_COPYRIGHT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_TITLE { get; set; }
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using BlazorComponent;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
/// <summary>
|
||||
/// Breadcrumb
|
||||
/// </summary>
|
||||
public partial class Breadcrumb
|
||||
{
|
||||
private List<BreadcrumbItem> BreadcrumbItems = new();
|
||||
|
||||
[Inject]
|
||||
UserResoures UserResoures { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public override void Dispose()
|
||||
{
|
||||
NavigationManager.LocationChanged -= OnLocationChanged;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
NavigationManager.LocationChanged += OnLocationChanged;
|
||||
BreadcrumbItems = GetBreadcrumbItems();
|
||||
base.OnInitialized();
|
||||
}
|
||||
private List<BreadcrumbItem> GetBreadcrumbItems()
|
||||
{
|
||||
var items = new List<BreadcrumbItem>();
|
||||
var currentNav = UserResoures.AllSameLevelMenuSpas.FirstOrDefault(n => n.Component is not null && NavigationManager.Uri.Replace(NavigationManager.BaseUri, "/") == (n.Component));
|
||||
if (currentNav is not null)
|
||||
{
|
||||
if (currentNav.ParentId != 0)
|
||||
{
|
||||
var parentNav = UserResoures.AllSameLevelMenuSpas.FirstOrDefault(n => n.Id == currentNav.ParentId);
|
||||
if (parentNav != null)
|
||||
items.Add(new BreadcrumbItem { Text = parentNav.Title, Href = null });
|
||||
}
|
||||
|
||||
items.Add(new BreadcrumbItem() { Text = currentNav.Title, Href = currentNav.Component });
|
||||
items.Last().Href = currentNav.Component;
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private void OnLocationChanged(object sender, LocationChangedEventArgs e)
|
||||
{
|
||||
BreadcrumbItems = GetBreadcrumbItems();
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@inject NavigationManager NavigationManager
|
||||
@namespace ThingsGateway.Admin.Blazor.Core
|
||||
@inject UserResoures UserResoures;
|
||||
@inherits BaseComponentBase
|
||||
@using BlazorComponent;
|
||||
@using Masa.Blazor;
|
||||
|
||||
|
||||
<div class="ml-16">
|
||||
<MMenu OffsetY Bottom Right CloseOnContentClick="true" @bind-Value="_open" MinWidth="@("auto")">
|
||||
<ActivatorContent>
|
||||
<MTooltip Color="primary" Bottom>
|
||||
<ActivatorContent Context="tooltipContext">
|
||||
@{
|
||||
var attrs = new Dictionary<string, object>();
|
||||
foreach (var (key, value) in context.Attrs)
|
||||
{
|
||||
attrs.Add(key, value);
|
||||
}
|
||||
foreach (var (key, value) in tooltipContext.Attrs)
|
||||
{
|
||||
if (attrs.ContainsKey(key) is false) attrs.Add(key, value);
|
||||
}
|
||||
}
|
||||
<MIcon @attributes="@attrs" Size=20 Color="dark-yellow">mdi-star-outline</MIcon>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>收藏</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList Class="pb-1" Style="min-width:320px;">
|
||||
@foreach (var nav in UserResoures.WorkbenchOutputs)
|
||||
{
|
||||
<MListItem Dense OnClick="()=> NavigationManager.NavigateTo(nav.Component)" Class="px-4">
|
||||
<MListItemAction Class="mr-3">
|
||||
<MIcon Size=20 Color="neutral-lighten-3">@nav.Icon</MIcon>
|
||||
</MListItemAction>
|
||||
<MListItemContent>
|
||||
<span Class="text-btn">@(nav.Title)</span>
|
||||
</MListItemContent>
|
||||
</MListItem>
|
||||
}
|
||||
</MList>
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
</div>
|
||||
@@ -1,21 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
/// <summary>
|
||||
/// <20>ղ<EFBFBD>/<2F><><EFBFBD>ݷ<EFBFBD>ʽ
|
||||
/// </summary>
|
||||
public partial class Favorite
|
||||
{
|
||||
bool _open;
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
/// <summary>
|
||||
/// Foter
|
||||
/// </summary>
|
||||
public partial class Foter
|
||||
{
|
||||
private string Version = "";
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_COPYRIGHT_URL { get; set; }
|
||||
/// <summary>
|
||||
/// <20><>Ȩ
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_COPYRIGHT { get; set; }
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_TITLE { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
Version = "v" + Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logo
|
||||
/// </summary>
|
||||
public partial class Logo
|
||||
{
|
||||
/// <summary>
|
||||
/// Logo<67>߶<EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int HeightInt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_COPYRIGHT_URL { get; set; }
|
||||
/// <summary>
|
||||
/// <20><>Ȩ
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_COPYRIGHT { get; set; }
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string CONFIG_TITLE { get; set; }
|
||||
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Masa.Blazor.Presets;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
/// <summary>
|
||||
/// PageTabs
|
||||
/// </summary>
|
||||
public partial class PageTabs
|
||||
{
|
||||
private TabOptions TabOptions(PageTabPathValue value)
|
||||
{
|
||||
var item = UserResoures.PageTabItems.FirstOrDefault(u => value.IsMatch(u.Href));
|
||||
var title = item?.Title;
|
||||
var icon = item?.Icon;
|
||||
var titleClass = $"mx-2 text-capitalize {(value.Selected ? "primary--text" : "")}";
|
||||
var op = new TabOptions(title, icon, titleClass)
|
||||
{
|
||||
TitleStyle = "min-width:46px;",
|
||||
Class = "systemTab",
|
||||
};
|
||||
return op;
|
||||
}
|
||||
/// <summary>
|
||||
/// Tabsʵ<73><CAB5>
|
||||
/// </summary>
|
||||
public PPageTabs PPageTabs { get; private set; }
|
||||
|
||||
[Inject]
|
||||
UserResoures UserResoures { get; set; }
|
||||
/// <summary>
|
||||
/// SelfPatterns
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IEnumerable<string> SelfPatterns { get; set; }
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
/// <summary>
|
||||
/// Search
|
||||
/// </summary>
|
||||
public partial class Search
|
||||
{
|
||||
private string _value;
|
||||
private string Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
NavigationManager.NavigateTo(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Inject]
|
||||
private NavigationManager NavigationManager { get; set; }
|
||||
|
||||
private List<SysResource> AvalidMenus;
|
||||
[Inject]
|
||||
private UserResoures UserResoures { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
AvalidMenus = UserResoures.SameLevelMenus.Where(it => it.Component != null).ToList();
|
||||
base.OnParametersSet();
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using BlazorComponent;
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
/// <summary>
|
||||
/// UserMenu
|
||||
/// </summary>
|
||||
public partial class UserMenu
|
||||
{
|
||||
[Inject]
|
||||
NavigationManager NavigationManager { get; set; }
|
||||
[Inject]
|
||||
private UserResoures UserResoures { get; set; }
|
||||
|
||||
[Inject]
|
||||
private AjaxService AjaxService { get; set; }
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private async Task LogoutAsync()
|
||||
{
|
||||
var ajaxOption = new AjaxOption
|
||||
{
|
||||
Url = "/auth/b/logout",
|
||||
};
|
||||
var str = await AjaxService.GetMessageAsync(ajaxOption);
|
||||
var ret = str?.ToJsonWithT<UnifyResult<string>>();
|
||||
if (ret?.Code != 200)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("ע<><D7A2>ʧ<EFBFBD><CAA7>", AlertTypes.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("ע<><D7A2><EFBFBD>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
await Task.Delay(500);
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#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 Masa.Blazor;
|
||||
using Masa.Blazor.Presets;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor.Core;
|
||||
|
||||
/// <summary>
|
||||
/// AppStartup启动类
|
||||
/// </summary>
|
||||
public class Startup : AppStartup
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMasaBlazor(options =>
|
||||
{
|
||||
options.Defaults = new Dictionary<string, IDictionary<string, object>>()
|
||||
{
|
||||
{
|
||||
PopupComponents.SNACKBAR, new Dictionary<string, object>()
|
||||
{
|
||||
{ nameof(PEnqueuedSnackbars.Closeable), true },
|
||||
{ nameof(PEnqueuedSnackbars.Position), SnackPosition.TopCenter }
|
||||
}
|
||||
}
|
||||
};
|
||||
options.ConfigureTheme(theme =>
|
||||
{
|
||||
theme.Themes.Dark.Accent = "#FF4081";
|
||||
theme.Themes.Dark.Error = "#FF5252";
|
||||
theme.Themes.Dark.Info = "#2196F3";
|
||||
theme.Themes.Dark.Primary = "#2196F3";
|
||||
theme.Themes.Dark.Secondary = "#424242";
|
||||
theme.Themes.Dark.Success = "#4CAF50";
|
||||
theme.Themes.Dark.Warning = "#FB8C00";
|
||||
theme.Themes.Dark.UserDefined.Add("barcolor", "#1e1e1e");
|
||||
|
||||
theme.Themes.Light.Accent = "#82B1FF";
|
||||
theme.Themes.Light.Error = "#FF5252";
|
||||
theme.Themes.Light.Info = "#2196F3";
|
||||
theme.Themes.Light.Primary = "#1976D2";
|
||||
theme.Themes.Light.Secondary = "#424242";
|
||||
theme.Themes.Light.Success = "#4CAF50";
|
||||
theme.Themes.Light.Warning = "#FB8C00";
|
||||
theme.Themes.Light.UserDefined.Add("barcolor", "#fff");
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
services.AddScoped<InitTimezone>();
|
||||
services.AddScoped<AjaxService>();
|
||||
services.AddScoped<UserResoures>();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<PackageReference Include="Masa.Blazor" Version="1.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.10" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\**">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,861 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>ThingsGateway.Admin.Blazor.Core</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.BaseComponentBase">
|
||||
<summary>
|
||||
Razor组件
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.BaseComponentBase.PopupService">
|
||||
<summary>
|
||||
弹出层服务
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.BaseComponentBase.Dispose">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.BaseComponentBase.InvokeStateHasChangedAsync">
|
||||
<summary>
|
||||
InvokeAsync(StateHasChanged)
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.BaseComponentBase.IsMobile">
|
||||
<summary>
|
||||
是否手机端
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.BaseComponentBase.Changed">
|
||||
<summary>
|
||||
主动更新
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.AppBarItems">
|
||||
<summary>
|
||||
AppBarItems
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppBarItems.CONFIG_COPYRIGHT_URL">
|
||||
<summary>
|
||||
链接
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppBarItems.CONFIG_COPYRIGHT">
|
||||
<summary>
|
||||
版权
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppBarItems.CONFIG_TITLE">
|
||||
<summary>
|
||||
标题
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Filters">
|
||||
<summary>
|
||||
过滤选择Model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Filters.Key">
|
||||
<summary>
|
||||
DateTable Value
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Filters.Title">
|
||||
<summary>
|
||||
DateTable Text
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Filters.Value">
|
||||
<summary>
|
||||
是否显示
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.PageSize">
|
||||
<summary>
|
||||
分页选择Model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.PageSize.Key">
|
||||
<summary>
|
||||
显示
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.PageSize.Value">
|
||||
<summary>
|
||||
值
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker">
|
||||
<summary>
|
||||
DateTimePicker
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.Max">
|
||||
<summary>
|
||||
max time [utc]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.Min">
|
||||
<summary>
|
||||
min time [utc]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.NoTitle">
|
||||
<summary>
|
||||
NoTitle
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.Value">
|
||||
<summary>
|
||||
selected datetime[utc]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.ValueChanged">
|
||||
<summary>
|
||||
ValueChanged
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.ChildContent">
|
||||
<summary>
|
||||
ChildContent
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.OutputTimezoneOffset">
|
||||
<summary>
|
||||
OutputTimezoneOffset
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.DisplayTimezoneOffset">
|
||||
<summary>
|
||||
DisplayTimezoneOffset
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView)">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
<param name="parameters"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AppDateTimePicker.UpdateValueAsync(System.Nullable{System.DateTime})">
|
||||
<summary>
|
||||
|
||||
</summary>
|
||||
<param name="dateTime">accept the time using display time zone</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.AppListGroup`1">
|
||||
<summary>
|
||||
AppListGroup
|
||||
</summary>
|
||||
<typeparam name="TItem"></typeparam>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppListGroup`1.Icon">
|
||||
<summary>
|
||||
icon
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppListGroup`1.Item">
|
||||
<summary>
|
||||
item
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppListGroup`1.SubGroup">
|
||||
<summary>
|
||||
sub
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AppListGroup`1.OnInitialized">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Breadcrumb">
|
||||
<summary>
|
||||
Breadcrumb
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Breadcrumb.Dispose">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Breadcrumb.OnInitialized">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.EnableChip">
|
||||
<summary>
|
||||
启用/停用 文本提示
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.EnableChip.Class">
|
||||
<summary>
|
||||
Class
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.EnableChip.Style">
|
||||
<summary>
|
||||
Style
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.EnableChip.Value">
|
||||
<summary>
|
||||
Value
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.EnableChip.DisabledLabel">
|
||||
<summary>
|
||||
DisabledLabel
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.EnableChip.EnabledLabel">
|
||||
<summary>
|
||||
EnabledLabel
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Favorite">
|
||||
<summary>
|
||||
收藏/快捷方式
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Foter">
|
||||
<summary>
|
||||
Foter
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Foter.CONFIG_COPYRIGHT_URL">
|
||||
<summary>
|
||||
链接
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Foter.CONFIG_COPYRIGHT">
|
||||
<summary>
|
||||
版权
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Foter.CONFIG_TITLE">
|
||||
<summary>
|
||||
标题
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Foter.OnParametersSetAsync">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Logo">
|
||||
<summary>
|
||||
Logo
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Logo.HeightInt">
|
||||
<summary>
|
||||
Logo高度
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Logo.CONFIG_COPYRIGHT_URL">
|
||||
<summary>
|
||||
链接
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Logo.CONFIG_COPYRIGHT">
|
||||
<summary>
|
||||
版权
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Logo.CONFIG_TITLE">
|
||||
<summary>
|
||||
标题
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.AppItem">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Children">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Divider">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.HasChildren">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Heading">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Href">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Icon">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.SubTitle">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Target">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Title">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppItem.Value">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.IAppItem`1">
|
||||
<summary>
|
||||
ListItem
|
||||
</summary>
|
||||
<typeparam name="TItem"></typeparam>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Children">
|
||||
<summary>
|
||||
子菜单
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Divider">
|
||||
<summary>
|
||||
是否启用下划线
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Heading">
|
||||
<summary>
|
||||
菜单头部标题
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Href">
|
||||
<summary>
|
||||
链接
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Icon">
|
||||
<summary>
|
||||
图标
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.SubTitle">
|
||||
<summary>
|
||||
菜单副标题
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Target">
|
||||
<summary>
|
||||
跳转方式
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Title">
|
||||
<summary>
|
||||
菜单标题
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.IAppItem`1.Value">
|
||||
<summary>
|
||||
菜单值
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.IAppItem`1.HasChildren">
|
||||
<summary>
|
||||
是否有子菜单
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.NavItem">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.NavItem.#ctor">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Children">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Divider">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Group">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Heading">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Href">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Icon">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Segment">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.State">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.SubTitle">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Target">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Title">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.NavItem.Value">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.PageTabs">
|
||||
<summary>
|
||||
PageTabs
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.PageTabs.PPageTabs">
|
||||
<summary>
|
||||
Tabs实例
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.PageTabs.SelfPatterns">
|
||||
<summary>
|
||||
SelfPatterns
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.PageTabs.ChildContent">
|
||||
<summary>
|
||||
子组件
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Search">
|
||||
<summary>
|
||||
Search
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Search.OnParametersSet">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.SysSignalR">
|
||||
<summary>
|
||||
SignalR连接
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.SysSignalR.DisposeAsync">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.SysSignalR.OnAfterRenderAsync(System.Boolean)">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.UserMenu">
|
||||
<summary>
|
||||
UserMenu
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserMenu.OnInitialized">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst">
|
||||
<summary>
|
||||
资源标识常量
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst.ResourceUrl">
|
||||
<summary>
|
||||
资源默认路径
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst.DataTableActions">
|
||||
<summary>
|
||||
表格操作列标识
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst.ThemeCookieKey">
|
||||
<summary>
|
||||
主题Cookie
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst.AppBarHeight">
|
||||
<summary>
|
||||
AppBarHeight
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst.PageTabsHeight">
|
||||
<summary>
|
||||
Tab高度
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst.FooterHeight">
|
||||
<summary>
|
||||
FooterHeight
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:ThingsGateway.Admin.Blazor.Core.BlazorResourceConst.DefaultHeight">
|
||||
<summary>
|
||||
DefaultHeight
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.MenuExtensions">
|
||||
<summary>
|
||||
菜单扩展
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.MenuExtensions.Parse(System.Collections.Generic.List{ThingsGateway.Admin.Core.SysResource})">
|
||||
<summary>
|
||||
转化为NavItem
|
||||
</summary>
|
||||
<param name="menus"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.PopupServiceExtensions">
|
||||
<summary>
|
||||
扩展方法
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.PopupServiceExtensions.OpenConfirmDialogAsync(Masa.Blazor.IPopupService,System.String,System.String)">
|
||||
<summary>
|
||||
确认弹窗,默认Error
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.PopupServiceExtensions.OpenConfirmDialogAsync(Masa.Blazor.IPopupService,System.String,System.String,BlazorComponent.AlertTypes)">
|
||||
<summary>
|
||||
确认弹窗
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.PopupServiceExtensions.OpenInformationMessageAsync(Masa.Blazor.IPopupService,System.String)">
|
||||
<summary>
|
||||
消息提示
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Ajax">
|
||||
<summary>
|
||||
Ajax组件
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.Ajax.JSRuntime">
|
||||
<summary>
|
||||
获得/设置 IJSRuntime 实例
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Ajax.Dispose">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Ajax.GetMessageAsync(ThingsGateway.Admin.Blazor.Core.AjaxOption)">
|
||||
<summary>
|
||||
请求并返回消息
|
||||
</summary>
|
||||
<param name="option">Ajax配置</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Ajax.OnAfterRenderAsync(System.Boolean)">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Ajax.OnInitialized">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.AjaxOption">
|
||||
<summary>
|
||||
Ajax配置类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AjaxOption.Data">
|
||||
<summary>
|
||||
获取/设置 要上传的参数类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AjaxOption.Method">
|
||||
<summary>
|
||||
获取/设置 传输方式,默认为POST
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AjaxOption.Url">
|
||||
<summary>
|
||||
获取/设置 请求的URL
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.AjaxService">
|
||||
<summary>
|
||||
Ajax服务类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AjaxService.Cache">
|
||||
<summary>
|
||||
获得 回调委托缓存集合
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AjaxService.GotoCache">
|
||||
<summary>
|
||||
获得 跳转其他页面的回调委托缓存集合
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AjaxService.DownFileCache">
|
||||
<summary>
|
||||
获得 下载委托缓存集合
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.GetMessageAsync(ThingsGateway.Admin.Blazor.Core.AjaxOption)">
|
||||
<summary>
|
||||
调用Ajax方法发送请求
|
||||
</summary>
|
||||
<param name="option"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.DownFileAsync(System.String,System.String,System.Object)">
|
||||
<summary>
|
||||
调用Ajax方法发送请求
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.GotoAsync(System.String)">
|
||||
<summary>
|
||||
调用 Goto 方法跳转其他页面
|
||||
</summary>
|
||||
<param name="url"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.Register(Microsoft.AspNetCore.Components.IComponent,System.Func{ThingsGateway.Admin.Blazor.Core.AjaxOption,System.Threading.Tasks.Task{System.String}})">
|
||||
<summary>
|
||||
注册服务
|
||||
</summary>
|
||||
<param name="key"></param>
|
||||
<param name="callback"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.RegisterGoto(Microsoft.AspNetCore.Components.IComponent,System.Func{System.String,System.Threading.Tasks.Task})">
|
||||
<summary>
|
||||
注册服务
|
||||
</summary>
|
||||
<param name="key"></param>
|
||||
<param name="callback"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.RegisterDownFile(Microsoft.AspNetCore.Components.IComponent,System.Func{System.String,System.String,System.Object,System.Threading.Tasks.Task})">
|
||||
<summary>
|
||||
注册服务
|
||||
</summary>
|
||||
<param name="key"></param>
|
||||
<param name="callback"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.UnRegister(Microsoft.AspNetCore.Components.IComponent)">
|
||||
<summary>
|
||||
注销事件
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.UnRegisterGoto(Microsoft.AspNetCore.Components.IComponent)">
|
||||
<summary>
|
||||
注销事件
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AjaxService.UnRegisterDownFile(Microsoft.AspNetCore.Components.IComponent)">
|
||||
<summary>
|
||||
注销事件
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.BootstrapDynamicComponent">
|
||||
<summary>
|
||||
动态组件类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.BootstrapDynamicComponent.#ctor(System.Type,System.Collections.Generic.IDictionary{System.String,System.Object})">
|
||||
<summary>
|
||||
构造函数
|
||||
</summary>
|
||||
<param name="componentType"></param>
|
||||
<param name="parameters">TCom 组件所需要的参数集合</param>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.BootstrapDynamicComponent.ComponentType">
|
||||
<summary>
|
||||
获得/设置 组件类型
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.BootstrapDynamicComponent.Parameters">
|
||||
<summary>
|
||||
获得/设置 组件参数集合
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.BootstrapDynamicComponent.CreateComponent``1(System.Collections.Generic.IDictionary{System.String,System.Object})">
|
||||
<summary>
|
||||
创建自定义组件方法
|
||||
</summary>
|
||||
<typeparam name="TCom"></typeparam>
|
||||
<param name="parameters">TCom 组件所需要的参数集合</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.BootstrapDynamicComponent.CreateComponent``1">
|
||||
<summary>
|
||||
创建自定义组件方法
|
||||
</summary>
|
||||
<typeparam name="TCom"></typeparam>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.BootstrapDynamicComponent.Render(System.Action{System.Object})">
|
||||
<summary>
|
||||
创建组件实例并渲染
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.JSModuleExtensions">
|
||||
<summary>
|
||||
JSModule extensions class
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.JSModuleExtensions.LoadModuleAsync(Microsoft.JSInterop.IJSRuntime,System.String,System.Boolean)">
|
||||
<summary>
|
||||
IJSRuntime 扩展方法 动态加载脚本
|
||||
</summary>
|
||||
<param name="jsRuntime"></param>
|
||||
<param name="fileName"></param>
|
||||
<param name="relative">是否为相对路径 默认 true</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.JSRuntimeExtensions">
|
||||
<summary>
|
||||
JSRuntime 扩展操作类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.JSRuntimeExtensions.InternalInvokeAsync``1(Microsoft.JSInterop.IJSRuntime,System.String,System.Threading.CancellationToken,System.Object[])">
|
||||
<summary>
|
||||
调用 JSInvoke 方法
|
||||
</summary>
|
||||
<param name="jsRuntime">IJSRuntime 实例</param>
|
||||
<param name="func">Javascript 方法</param>
|
||||
<param name="token">取消传播</param>
|
||||
<param name="args">Javascript 参数</param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.JSRuntimeExtensions.InternalInvokeVoidAsync(Microsoft.JSInterop.IJSRuntime,System.String,System.Threading.CancellationToken,System.Object[])">
|
||||
<summary>
|
||||
调用 JSInvoke 方法
|
||||
</summary>
|
||||
<param name="jsRuntime">IJSRuntime 实例</param>
|
||||
<param name="func">Javascript 方法</param>
|
||||
<param name="token">取消传播</param>
|
||||
<param name="args">Javascript 参数</param>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.InitTimezone">
|
||||
<summary>
|
||||
获取Web客户端时差
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.InitTimezone.TimezoneOffset">
|
||||
<summary>
|
||||
当前的客户端时差
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.InitTimezone.#ctor(Microsoft.JSInterop.IJSRuntime,BlazorComponent.I18n.CookieStorage,Microsoft.AspNetCore.Http.IHttpContextAccessor)">
|
||||
<summary>
|
||||
构造函数
|
||||
</summary>
|
||||
<param name="jsRuntime"></param>
|
||||
<param name="storage"></param>
|
||||
<param name="httpContextAccessor"></param>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.InitTimezone.SetTimezoneOffsetAsync">
|
||||
<summary>
|
||||
获取Web客户端时差
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.InitTimezone.Dispose">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.Startup">
|
||||
<summary>
|
||||
AppStartup启动类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.Startup.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.UserResoures">
|
||||
<summary>
|
||||
当前用户资源
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.#ctor(BlazorComponent.I18n.CookieStorage,Masa.Blazor.MasaBlazor,Microsoft.AspNetCore.Http.IHttpContextAccessor)">
|
||||
<summary>
|
||||
构造函数
|
||||
</summary>
|
||||
<param name="cookieStorage"></param>
|
||||
<param name="masaBlazor"></param>
|
||||
<param name="httpContextAccessor"></param>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.UserResoures.CurrentUser">
|
||||
<summary>
|
||||
当前用户
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.UserResoures.IsDark">
|
||||
<summary>
|
||||
是否深色主图
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.UserResoures.Menus">
|
||||
<summary>
|
||||
当前菜单
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.UserResoures.PageTabItems">
|
||||
<summary>
|
||||
当前的Tab列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.UserResoures.SameLevelMenus">
|
||||
<summary>
|
||||
当前的菜单列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.UserResoures.AllSameLevelMenuSpas">
|
||||
<summary>
|
||||
当前的菜单与单页列表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.UserResoures.WorkbenchOutputs">
|
||||
<summary>
|
||||
当前工作台
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.Dispose">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.InitAllAsync">
|
||||
<summary>
|
||||
初始化获取全部资源
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.InitMenuAsync">
|
||||
<summary>
|
||||
初始化获取当前菜单资源
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.InitUserAsync">
|
||||
<summary>
|
||||
初始化获取当前用户
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.IsHasButtonWithRole(System.String)">
|
||||
<summary>
|
||||
是否拥有按钮授权
|
||||
</summary>
|
||||
<param name="code"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.IsHasPageWithRole(System.String)">
|
||||
<summary>
|
||||
是否拥有页面授权
|
||||
</summary>
|
||||
<param name="code"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.UserResoures.SetMasaTheme(System.Nullable{System.Boolean})">
|
||||
<summary>
|
||||
设置深浅主题统一由这个方法为入口
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -1,84 +0,0 @@
|
||||
/*自定义样式*/
|
||||
.table-text-truncate {
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
max-width: 200px;
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.table-minwidth {
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
/*masa样式覆盖修改*/
|
||||
.m-application--is-ltr .m-data-table__mobile-row__cell {
|
||||
text-align: right;
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.m-application--is-ltr .m-list-group--no-action > .m-list-group__items > .m-list-item {
|
||||
padding-left: 36px;
|
||||
}
|
||||
|
||||
.m-application--is-ltr .m-list--dense.m-list--nav .m-list-group--no-action > .m-list-group__items > .m-list-item {
|
||||
padding-left: 36px;
|
||||
}
|
||||
|
||||
.m-application--is-ltr .m-list-item__action:first-child, .m-application--is-ltr .m-list-item__icon:first-child {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.m-tabs-bar {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.m-breadcrumbs li:nth-child(even) {
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
|
||||
.m-page-tabs > .m-tabs-bar {
|
||||
border-bottom-left-radius: 20px !important;
|
||||
border-bottom-right-radius: 20px !important;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
|
||||
.m-application .text-start {
|
||||
text-align: start !important;
|
||||
min-width: 130px;
|
||||
}
|
||||
.neutral--text {
|
||||
color: #1B2559 !important;
|
||||
caret-color: #1B2559 !important;
|
||||
}
|
||||
|
||||
|
||||
/*下面都是html默认样式修改*/
|
||||
html {
|
||||
overflow-y: hidden
|
||||
}
|
||||
|
||||
/*滚动条样式*/
|
||||
::-webkit-scrollbar-track {
|
||||
border-radius: 10px;
|
||||
margin: 12px 0 0 0;
|
||||
background-color: #F6F8FD;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 6px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
margin: 5px 0;
|
||||
border-radius: 4px;
|
||||
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.44, #A3AED0), color-stop(0.72, #A3AED0), color-stop(0.86, #A3AED0));
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,53 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// Base
|
||||
/// </summary>
|
||||
public partial class Base
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
/// <returns></returns>
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await App.GetService<UserCenterService>().GetLoginDefaultRazorAsync(UserManager.UserId);
|
||||
var sameLevelMenus = await App.GetService<IResourceService>().GetaMenuAndSpaListAsync();
|
||||
if (NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/Login" || NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/")
|
||||
NavigationManager.NavigateTo(sameLevelMenus.FirstOrDefault(a => a.Id == data)?.Component ?? "index");
|
||||
else
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri);
|
||||
}
|
||||
catch
|
||||
{
|
||||
NavigationManager.NavigateTo("index");
|
||||
}
|
||||
}
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor;
|
||||
/// <summary>
|
||||
/// <20><>ҳ
|
||||
/// </summary>
|
||||
public partial class Index
|
||||
{
|
||||
List<SysOperateLog> SysOperateLogs;
|
||||
|
||||
List<SysVisitLog> SysVisitLogs;
|
||||
|
||||
[Inject]
|
||||
private InitTimezone InitTimezone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
SysVisitLogs = (await App.GetService<IVisitLogService>().PageAsync(new() { Size = 5, Account = UserResoures.CurrentUser?.Account })).Records.ToList();
|
||||
SysOperateLogs = (await App.GetService<IOperateLogService>().PageAsync(new() { Size = 5, Account = UserResoures.CurrentUser?.Account })).Records.ToList();
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#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 Mapster;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor;
|
||||
|
||||
/// <summary>
|
||||
/// 操作日志
|
||||
/// </summary>
|
||||
public partial class Oplog
|
||||
{
|
||||
private readonly OperateLogPageInput search = new();
|
||||
private IAppDataTable _datatable;
|
||||
private List<StringFilters> CategoryFilters { get; set; } = new();
|
||||
private List<StringFilters> ExeStatus { get; set; } = new();
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
CategoryFilters.Add(new StringFilters() { Key = "操作", Value = LogConst.LOG_OPERATE });
|
||||
CategoryFilters.Add(new StringFilters() { Key = "第三方操作", Value = LogConst.LOG_OPENAPIOPERATE });
|
||||
ExeStatus.Add(new StringFilters() { Key = "成功", Value = LogConst.LOG_SUCCESS });
|
||||
ExeStatus.Add(new StringFilters() { Key = "失败", Value = LogConst.LOG_FAIL });
|
||||
}
|
||||
|
||||
private async Task ClearClick()
|
||||
{
|
||||
var confirm = await PopupService.OpenConfirmDialogAsync("删除", "确定 ?");
|
||||
if (confirm)
|
||||
{
|
||||
await OperateLogService.DeleteAsync(CategoryFilters.Select(it => it.Value).ToArray());
|
||||
await _datatable?.QueryClickAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private Task<SqlSugarPagedList<SysOperateLog>> QueryCallAsync(OperateLogPageInput input)
|
||||
{
|
||||
input.Account = search.Account;
|
||||
input.Category = search.Category;
|
||||
input.ExeStatus = search.ExeStatus;
|
||||
return OperateLogService.PageAsync(input);
|
||||
}
|
||||
[Inject]
|
||||
AjaxService AjaxService { get; set; }
|
||||
async Task DownExportAsync(OperateLogPageInput input = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await AjaxService.DownFileAsync("file/operateLog", SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat(), input.Adapt<OperateLogInput>());
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/admin/role"
|
||||
@using System.Linq.Expressions;
|
||||
@using BlazorComponent;
|
||||
@using Masa.Blazor.Presets;
|
||||
@using Microsoft.AspNetCore.Authorization;
|
||||
@using ThingsGateway.Admin.Application;
|
||||
@inject IRoleService SysRoleService
|
||||
@namespace ThingsGateway.Admin.Blazor
|
||||
@attribute [Authorize]
|
||||
@inject UserResoures UserResoures
|
||||
@inherits BaseComponentBase
|
||||
@layout MainLayout
|
||||
<AppDataTable @ref="_datatable" TItem="SysRole" SearchItem="RolePageInput" AddItem="RoleAddInput" EditItem="RoleEditInput"
|
||||
SearchModel="@search" IsShowSearchKey
|
||||
QueryCallAsync="QueryCallAsync" AddCallAsync="AddCallAsync"
|
||||
EditCallAsync="EditCallAsync" DeleteCallAsync="DeleteCallAsync"
|
||||
IsShowQueryButton
|
||||
IsShowAddButton=@UserResoures.IsHasButtonWithRole("sysroleadd")
|
||||
IsShowDeleteButton=@UserResoures.IsHasButtonWithRole("sysroledelete")
|
||||
IsShowEditButton=@UserResoures.IsHasButtonWithRole("sysroleedit")>
|
||||
|
||||
<AddTemplate>
|
||||
@GetRenderFragment(context)
|
||||
|
||||
</AddTemplate>
|
||||
|
||||
<EditTemplate>
|
||||
@GetRenderFragment(context)
|
||||
</EditTemplate>
|
||||
|
||||
<ItemColOperTemplate>
|
||||
<MList Dense>
|
||||
@if (@UserResoures.IsHasButtonWithRole("sysroleperresuorce"))
|
||||
{
|
||||
<MListItem OnClick="async()=>
|
||||
{
|
||||
ChoiceRoleId=context.Item.Id;
|
||||
await ResuorceInitAsync();
|
||||
IsShowResuorces=true;
|
||||
}">
|
||||
<MListItemTitle Class="ml-2">资源权限</MListItemTitle>
|
||||
</MListItem>
|
||||
|
||||
}
|
||||
@if (@UserResoures.IsHasButtonWithRole("sysroleperuser"))
|
||||
{
|
||||
<MListItem OnClick="async()=>
|
||||
{
|
||||
ChoiceRoleId=context.Item.Id;
|
||||
await UserInitAsync();
|
||||
IsShowUsers=true;
|
||||
}">
|
||||
<MListItemTitle Class="ml-2">授权用户</MListItemTitle>
|
||||
</MListItem>
|
||||
|
||||
}
|
||||
</MList>
|
||||
</ItemColOperTemplate>
|
||||
|
||||
</AppDataTable>
|
||||
|
||||
<PDrawer @bind-Value="IsShowResuorces" OnCancel="() => IsShowResuorces = false"
|
||||
Title=资源授权
|
||||
Width=@(IsMobile?"100%":"600")
|
||||
MaxWidth="600" OnSave="OnRoleHasResuorcesSaveAsync">
|
||||
@if (IsShowResuorces)
|
||||
{
|
||||
<MSheet Outlined Class="ma-0 pa-2">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MCol> <MLabel Class="ml-4 font-weight-black">菜单</MLabel> </MCol>
|
||||
<MDivider Vertical />
|
||||
<MCol> <MLabel Class="ml-4 font-weight-black">按钮</MLabel> </MCol>
|
||||
</MRow>
|
||||
</MSheet>
|
||||
@foreach (var menu in ResTreeSelectors)
|
||||
{
|
||||
<MSheet Outlined Class="ma-0 pa-4">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MCol>
|
||||
<MListItem IsActive=@(RoleHasResuorces.Any(it=>it.MenuId==menu.Id))>
|
||||
<ItemContent>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>@menu.Title</MListItemTitle>
|
||||
</MListItemContent>
|
||||
<MListItemAction>
|
||||
<MCheckbox TValue=bool Value="@context.Active" ValueChanged=@(enable=>
|
||||
{
|
||||
if(!enable)
|
||||
RoleHasResuorces.RemoveWhere(it=>it.MenuId==menu.Id);
|
||||
else if(!RoleHasResuorces.Any(it=>it.MenuId==menu.Id))
|
||||
RoleHasResuorces.Add(new() {MenuId=menu.Id});
|
||||
}
|
||||
)></MCheckbox>
|
||||
</MListItemAction>
|
||||
</ItemContent>
|
||||
</MListItem>
|
||||
</MCol>
|
||||
<MDivider Vertical />
|
||||
<MCol>
|
||||
@GetButtonCore(menu)
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MSheet>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</PDrawer>
|
||||
|
||||
<PDrawer @bind-Value="IsShowUsers" OnCancel="() => IsShowUsers = false"
|
||||
Title=授权用户
|
||||
Width=@(IsMobile?"100%":"500")
|
||||
MaxWidth="500" OnSave="OnUsersSaveAsync">
|
||||
|
||||
<MCard Flat Class="ma-0 pa-4">
|
||||
<MCardTitle Class="py-2">
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 ml-6" @bind-Value="SearchKey"
|
||||
Outlined Label=@typeof(SysUser).GetDescription(nameof(SysUser.Account)) />
|
||||
</MCardTitle>
|
||||
<MTreeview Class="my-1" Dense OpenAll TItem="UserSelectorOutput" TKey="UserSelectorOutput" Selectable @bind-Value="UsersChoice"
|
||||
Items="AllUsers" ItemText="r=>r.Account" ItemChildren="r=>null"
|
||||
ItemKey=@(r=>r)>
|
||||
<LabelContent>
|
||||
<span title=@context.Item.Account>
|
||||
@context.Item.Account
|
||||
</span>
|
||||
</LabelContent>
|
||||
</MTreeview>
|
||||
</MCard>
|
||||
|
||||
</PDrawer>
|
||||
|
||||
|
||||
@code {
|
||||
RenderFragment GetButtonCore(RoleGrantResourceMenu menu)
|
||||
{
|
||||
RenderFragment ViewSubMenu = null;
|
||||
|
||||
foreach (var button in menu.Button ?? new())
|
||||
{
|
||||
ViewSubMenu +=
|
||||
@<MListItem Class="ml-6" IsActive=@(RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id)?.ButtonInfo?.Contains(button.Id)==true)>
|
||||
<ItemContent>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>@button.Title</MListItemTitle>
|
||||
</MListItemContent>
|
||||
<MListItemAction>
|
||||
<MCheckbox TValue=bool Value="@context.Active" ValueChanged=@(a=>
|
||||
{
|
||||
if(!a)
|
||||
{
|
||||
RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id)?.ButtonInfo?.RemoveWhere(it=>it==button.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !(RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id)?.ButtonInfo?.Any(it=>it==button.Id)==true))
|
||||
{
|
||||
if(!RoleHasResuorces.Any(it=>it.MenuId==menu.Id))
|
||||
{
|
||||
RoleHasResuorces.Add(new() {MenuId=menu.Id});
|
||||
}
|
||||
RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id).ButtonInfo.Add(button.Id);
|
||||
}
|
||||
}
|
||||
})></MCheckbox>
|
||||
</MListItemAction>
|
||||
</ItemContent>
|
||||
</MListItem>
|
||||
;
|
||||
}
|
||||
|
||||
return ViewSubMenu;
|
||||
}
|
||||
|
||||
RenderFragment GetRenderFragment(RoleAddInput context)
|
||||
{
|
||||
RenderFragment renderFragment =
|
||||
@<div>
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(context.Description(x => x.Name)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Name />
|
||||
|
||||
<MSubheader Class="mt-4 mb-5 font-weight-black">@(context.Description(x => x.SortCode)) </MSubheader>
|
||||
<MSlider @bind-Value=@context.SortCode Class="mb-5" TValue=int ThumbLabel="@("always")" Dense />
|
||||
</div>
|
||||
;
|
||||
return renderFragment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
#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 Masa.Blazor;
|
||||
using Masa.Blazor.Presets;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor;
|
||||
/// <summary>
|
||||
/// 角色页面
|
||||
/// </summary>
|
||||
public partial class Role
|
||||
{
|
||||
private readonly RolePageInput search = new();
|
||||
private IAppDataTable _datatable;
|
||||
private List<UserSelectorOutput> AllUsers;
|
||||
long ChoiceRoleId;
|
||||
bool IsShowResuorces;
|
||||
bool IsShowUsers;
|
||||
List<RoleGrantResourceMenu> ResTreeSelectors = new();
|
||||
List<RelationRoleResuorce> RoleHasResuorces = new();
|
||||
private List<UserSelectorOutput> UsersChoice;
|
||||
|
||||
[CascadingParameter]
|
||||
MainLayout MainLayout { get; set; }
|
||||
|
||||
[Inject]
|
||||
IResourceService ResourceService { get; set; }
|
||||
|
||||
private string SearchKey { get; set; }
|
||||
|
||||
[Inject]
|
||||
ISysUserService SysUserService { get; set; }
|
||||
|
||||
private Task AddCallAsync(RoleAddInput input)
|
||||
{
|
||||
return SysRoleService.AddAsync(input);
|
||||
}
|
||||
private async Task DeleteCallAsync(IEnumerable<SysRole> sysRoles)
|
||||
{
|
||||
await SysRoleService.DeleteAsync(sysRoles.Select(a => a.Id).ToArray());
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
private async Task EditCallAsync(RoleEditInput input)
|
||||
{
|
||||
await SysRoleService.EditAsync(input);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
private async Task OnRoleHasResuorcesSaveAsync(ModalActionEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
GrantResourceInput userGrantRoleInput = new();
|
||||
var data = new List<SysResource>();
|
||||
userGrantRoleInput.Id = ChoiceRoleId;
|
||||
userGrantRoleInput.GrantInfoList = RoleHasResuorces;
|
||||
await SysRoleService.GrantResourceAsync(userGrantRoleInput);
|
||||
IsShowResuorces = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.Cancel();
|
||||
await PopupService.EnqueueSnackbarAsync(ex, false);
|
||||
}
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
private async Task OnUsersSaveAsync(ModalActionEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
GrantUserInput userGrantRoleInput = new();
|
||||
userGrantRoleInput.Id = ChoiceRoleId;
|
||||
userGrantRoleInput.GrantInfoList = UsersChoice.Select(it => it.Id).ToList();
|
||||
await SysRoleService.GrantUserAsync(userGrantRoleInput);
|
||||
IsShowUsers = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.Cancel();
|
||||
await PopupService.EnqueueSnackbarAsync(ex, false);
|
||||
}
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
private Task<SqlSugarPagedList<SysRole>> QueryCallAsync(RolePageInput input)
|
||||
{
|
||||
return SysRoleService.PageAsync(input);
|
||||
}
|
||||
|
||||
private async Task ResuorceInitAsync()
|
||||
{
|
||||
ResTreeSelectors = (await ResourceService.GetRoleGrantResourceMenusAsync());
|
||||
RoleHasResuorces = (await SysRoleService.OwnResourceAsync(ChoiceRoleId))?.GrantInfoList;
|
||||
}
|
||||
|
||||
private async Task<List<UserSelectorOutput>> UserInitAsync()
|
||||
{
|
||||
AllUsers = await SysUserService.UserSelectorAsync(SearchKey);
|
||||
var data = await SysRoleService.OwnUserAsync(ChoiceRoleId);
|
||||
UsersChoice = AllUsers.Where(a => data.Contains(a.Id)).ToList();
|
||||
return AllUsers;
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
#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 Masa.Blazor;
|
||||
using Masa.Blazor.Presets;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor;
|
||||
|
||||
/// <summary>
|
||||
/// 用户界面
|
||||
/// </summary>
|
||||
public partial class User
|
||||
{
|
||||
private readonly UserPageInput search = new();
|
||||
private IAppDataTable _datatable;
|
||||
private List<SysRole> AllRoles;
|
||||
long ChoiceUserId;
|
||||
bool IsShowRoles;
|
||||
List<SysRole> RolesChoice = new();
|
||||
string SearchName;
|
||||
[CascadingParameter]
|
||||
MainLayout MainLayout { get; set; }
|
||||
|
||||
[Inject]
|
||||
IRoleService SysRoleService { get; set; }
|
||||
private Task AddCallAsync(UserAddInput input)
|
||||
{
|
||||
return SysUserService.AddAsync(input);
|
||||
}
|
||||
private async Task DeleteCallAsync(IEnumerable<SysUser> users)
|
||||
{
|
||||
await SysUserService.DeleteAsync(users.Select(a => a.Id).ToArray());
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
private async Task EditCallAsync(UserEditInput users)
|
||||
{
|
||||
await SysUserService.EditAsync(users);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
private async Task OnRolesSaveAsync(ModalActionEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
UserGrantRoleInput userGrantRoleInput = new();
|
||||
userGrantRoleInput.Id = ChoiceUserId;
|
||||
userGrantRoleInput.RoleIdList = RolesChoice.Select(it => it.Id).ToList();
|
||||
await SysUserService.GrantRoleAsync(userGrantRoleInput);
|
||||
IsShowRoles = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.Cancel();
|
||||
await PopupService.EnqueueSnackbarAsync(ex, false);
|
||||
}
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
private Task<SqlSugarPagedList<SysUser>> QueryCallAsync(UserPageInput input)
|
||||
{
|
||||
return SysUserService.PageAsync(input);
|
||||
}
|
||||
|
||||
private async Task ResetPasswordAsync(SysUser sysUser)
|
||||
{
|
||||
await SysUserService.ResetPasswordAsync(sysUser.Id);
|
||||
await PopupService.EnqueueSnackbarAsync(new("成功", AlertTypes.Success));
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
private async Task RoleInitAsync()
|
||||
{
|
||||
AllRoles = await SysRoleService.RoleSelectorAsync();
|
||||
var data = await SysRoleService.GetRoleIdListByUserIdAsync(ChoiceUserId);
|
||||
RolesChoice = AllRoles.Where(a => data.Contains(a.Id)).ToList();
|
||||
}
|
||||
private async Task UserStatusChangeAsync(SysUser context, bool enable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (enable)
|
||||
await SysUserService.EnableUserAsync(context.Id);
|
||||
else
|
||||
await SysUserService.DisableUserAsync(context.Id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _datatable?.QueryClickAsync();
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor;
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public partial class UserCenter
|
||||
{
|
||||
List<long> _menusChoice = new();
|
||||
readonly PasswordInfoInput _passwordInfoInput = new();
|
||||
readonly UpdateInfoInput _updateInfoInput = new();
|
||||
|
||||
long DefaultMenuId { get; set; }
|
||||
[CascadingParameter]
|
||||
MainLayout MainLayout { get; set; }
|
||||
|
||||
[Inject]
|
||||
NavigationManager NavigationManager { get; set; }
|
||||
|
||||
[Inject]
|
||||
IUserCenterService UserCenterService { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
DefaultMenuId = await App.GetService<IUserCenterService>().GetLoginDefaultRazorAsync(UserManager.UserId);
|
||||
_updateInfoInput.Email = UserResoures.CurrentUser.Email;
|
||||
_updateInfoInput.Phone = UserResoures.CurrentUser.Phone;
|
||||
_menusChoice = await App.GetService<IUserCenterService>().GetLoginWorkbenchAsync();
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
async Task OnDefaultRazorSaveAsync()
|
||||
{
|
||||
await UserCenterService.UpdateUserDefaultRazorAsync(UserManager.UserId, DefaultMenuId);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
}
|
||||
|
||||
async Task OnShortcutSaveAsync()
|
||||
{
|
||||
await UserCenterService.UpdateWorkbenchAsync(_menusChoice);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
}
|
||||
async Task OnUpdatePasswordInfoAsync(FormContext context)
|
||||
{
|
||||
var success = context.Validate();
|
||||
if (success)
|
||||
{
|
||||
//<2F><>֤<EFBFBD>ɹ<EFBFBD><C9B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><D2B5>
|
||||
_passwordInfoInput.Id = UserResoures.CurrentUser.Id;
|
||||
await UserCenterService.EditPasswordAsync(_passwordInfoInput);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD><C9B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD>¼", AlertTypes.Success);
|
||||
await Task.Delay(2000);
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri);
|
||||
}
|
||||
}
|
||||
|
||||
async Task OnUpdateUserInfoAsync()
|
||||
{
|
||||
await UserCenterService.UpdateUserInfoAsync(_updateInfoInput);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/login"
|
||||
@layout BaseLayout
|
||||
@inherits BaseComponentBase
|
||||
@namespace ThingsGateway.Admin.Blazor
|
||||
@using BlazorComponent;
|
||||
@using Masa.Blazor.Presets;
|
||||
|
||||
<Ajax></Ajax>
|
||||
@if (IsMobile)
|
||||
{
|
||||
<MCard @onkeydown=Enter Height=@("100%") >
|
||||
@GetLoginCore()
|
||||
</MCard>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MRow NoGutters Style="height:100%">
|
||||
<MCol Md=5 Sm=12>
|
||||
<MSheet Elevation=1 Style="width:100%; height:100%;" Class="d-flex align-start flex-column mb-6">
|
||||
<div class="d-flex align-center ml-12 mt-12">
|
||||
<MAvatar Size="40" Color="teal">
|
||||
<span class="white--text text-h6">@CONFIG_TITLE?.GetNameLen2()</span>
|
||||
</MAvatar>
|
||||
<h1>@CONFIG_TITLE</h1>
|
||||
</div>
|
||||
<div class="d-flex align-center ml-12 mt-12 mb-auto">
|
||||
<h3>@CONFIG_REMARK</h3>
|
||||
</div>
|
||||
<div class="d-flex align-center pa-2" style="width:100%;height:100%;">
|
||||
<MImage Src=@(BlazorResourceConst.ResourceUrl+"images/login-left.svg")></MImage>
|
||||
</div>
|
||||
</MSheet>
|
||||
|
||||
</MCol>
|
||||
|
||||
<MCol Md=7 Sm=12 Align="AlignTypes.Center">
|
||||
<MRow Md=6 Sm=12 Justify="JustifyTypes.Center" Align="AlignTypes.Center">
|
||||
<MCard Class="px-16 py-12" @onkeydown=Enter>
|
||||
@GetLoginCore()
|
||||
</MCard>
|
||||
</MRow>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
RenderFragment GetLoginCore()
|
||||
{
|
||||
RenderFragment ViewSubMenu =
|
||||
@<div class="mt-2 px-2 py-1 mx-auto text-center my-auto" >
|
||||
<MAvatar Size=80>
|
||||
<MImage Src=@UserLogoUrl>
|
||||
</MImage>
|
||||
</MAvatar>
|
||||
<h5 class="mt-2 mb-12">@Welcome 👋</h5>
|
||||
<MTextField TValue="string"
|
||||
Label=账号
|
||||
Outlined
|
||||
HideDetails="@("auto")"
|
||||
@bind-Value=@loginModel.Account>
|
||||
</MTextField>
|
||||
<MTextField TValue="string"
|
||||
Class="mt-10"
|
||||
Label="密码"
|
||||
Type="@(_showPassword ? "text" : "password")"
|
||||
AppendIcon="@(_showPassword ? "mdi-eye" : "mdi-eye-off")"
|
||||
OnAppendClick="()=>_showPassword = !_showPassword"
|
||||
Outlined
|
||||
HideDetails="@("auto")"
|
||||
@bind-Value=@Password>
|
||||
</MTextField>
|
||||
@if (_showCaptcha)
|
||||
{
|
||||
<PImageCaptcha @ref=captcha @bind-Value="CaptchaValue"
|
||||
TextFieldClass="mt-10 mx-auto"
|
||||
Height="60"
|
||||
Label=验证码 Outlined
|
||||
OnRefresh="RefreshCode"
|
||||
ErrorMessage=验证码错误>
|
||||
</PImageCaptcha>
|
||||
}
|
||||
<MButton Class="mt-11 rounded-4" OnClick=LoginAsync Height=46 Width=@("100%") Color="primary">登录</MButton>
|
||||
</div>
|
||||
;
|
||||
return ViewSubMenu;
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
#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.DataEncryption;
|
||||
|
||||
using Masa.Blazor.Presets;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor;
|
||||
|
||||
/// <summary>
|
||||
/// 登录页面
|
||||
/// </summary>
|
||||
public partial class Login
|
||||
{
|
||||
private string CaptchaValue;
|
||||
bool _showPassword;
|
||||
bool _showCaptcha;
|
||||
private readonly LoginInput loginModel = new();
|
||||
|
||||
|
||||
[Inject]
|
||||
AjaxService AjaxService { get; set; }
|
||||
|
||||
|
||||
[Inject]
|
||||
IAuthService AuthService { get; set; }
|
||||
|
||||
|
||||
|
||||
string UserLogoUrl { get; set; } = BlazorResourceConst.ResourceUrl + "images/defaultUser.svg";
|
||||
|
||||
string Welcome { get; set; }
|
||||
|
||||
private ValidCodeOutput CaptchaInfo { get; set; }
|
||||
|
||||
private string Password { get; set; }
|
||||
|
||||
private string CONFIG_REMARK { get; set; }
|
||||
|
||||
private string CONFIG_TITLE { get; set; }
|
||||
|
||||
private async Task Enter(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
||||
{
|
||||
await LoginAsync();
|
||||
}
|
||||
}
|
||||
private PImageCaptcha captcha;
|
||||
[Inject]
|
||||
IUserCenterService UserCenterService { get; set; }
|
||||
[Inject]
|
||||
IResourceService ResourceService { get; set; }
|
||||
[Inject]
|
||||
ISysUserService SysUserService { get; set; }
|
||||
private async Task LoginAsync()
|
||||
{
|
||||
loginModel.ValidCodeReqNo = CaptchaInfo.ValidCodeReqNo;
|
||||
loginModel.ValidCode = CaptchaValue;
|
||||
loginModel.Password = DESCEncryption.Encrypt(Password, DESCKeyConst.DESCKey);
|
||||
if (IsMobile)
|
||||
{
|
||||
loginModel.Device = AuthDeviceTypeEnum.APP;
|
||||
}
|
||||
else
|
||||
{
|
||||
loginModel.Device = AuthDeviceTypeEnum.PC;
|
||||
}
|
||||
|
||||
var ajaxOption = new AjaxOption { Url = "/auth/b/login", Data = loginModel, };
|
||||
var str = await AjaxService.GetMessageAsync(ajaxOption);
|
||||
if (str != null)
|
||||
{
|
||||
var ret = str.ToJsonWithT<UnifyResult<LoginOutput>>();
|
||||
if (ret.Code != 200)
|
||||
{
|
||||
if (captcha != null)
|
||||
{
|
||||
await captcha.RefreshCode();
|
||||
}
|
||||
await PopupService.EnqueueSnackbarAsync(new("登录错误" + ": " + ret.Msg.ToString(), AlertTypes.Error));
|
||||
}
|
||||
else
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync(new("登录成功", AlertTypes.Success));
|
||||
await Task.Delay(500);
|
||||
var userId = await SysUserService.GetIdByAccountAsync(loginModel.Account);
|
||||
var data = await UserCenterService.GetLoginDefaultRazorAsync(userId);
|
||||
var sameLevelMenus = await ResourceService.GetaMenuAndSpaListAsync();
|
||||
if (NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/Login" || NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/")
|
||||
await AjaxService.GotoAsync(sameLevelMenus.FirstOrDefault(a => a.Id == data)?.Component ?? "index");
|
||||
else
|
||||
await AjaxService.GotoAsync(NavigationManager.Uri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (captcha != null)
|
||||
{
|
||||
await captcha.RefreshCode();
|
||||
}
|
||||
await PopupService.EnqueueSnackbarAsync(new("登录错误", AlertTypes.Error));
|
||||
}
|
||||
}
|
||||
[Inject]
|
||||
private NavigationManager NavigationManager { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (App.HostEnvironment.IsDevelopment())
|
||||
{
|
||||
loginModel.Account = "superAdmin";
|
||||
Password = "111111";
|
||||
}
|
||||
GetCaptchaInfo();
|
||||
CONFIG_TITLE = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_TITLE))?.ConfigValue;
|
||||
CONFIG_REMARK = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_REMARK))?.ConfigValue;
|
||||
_showCaptcha = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_CAPTCHA_OPEN))?.ConfigValue?.ToBoolean() == true;
|
||||
Welcome = "欢迎使用" + CONFIG_TITLE + "!";
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
private void GetCaptchaInfo()
|
||||
{
|
||||
CaptchaInfo = AuthService.GetCaptchaInfo();
|
||||
}
|
||||
|
||||
private Task<string> RefreshCode()
|
||||
{
|
||||
CaptchaInfo = AuthService.GetCaptchaInfo();
|
||||
return Task.FromResult(CaptchaInfo.CodeValue);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#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 ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
|
||||
namespace ThingsGateway.Admin.Blazor;
|
||||
|
||||
/// <summary>
|
||||
/// Layout
|
||||
/// </summary>
|
||||
public partial class MainLayout
|
||||
{
|
||||
private static readonly string[] selfPatterns =
|
||||
{
|
||||
};
|
||||
bool Changed { get; set; }
|
||||
private bool? _drawerOpen = true;
|
||||
|
||||
private PageTabs _pageTabs;
|
||||
|
||||
private string CONFIG_COPYRIGHT = "";
|
||||
|
||||
private string CONFIG_COPYRIGHT_URL = "";
|
||||
|
||||
private string CONFIG_TITLE = "";
|
||||
/// <summary>
|
||||
/// IsMobile
|
||||
/// </summary>
|
||||
[CascadingParameter(Name = "IsMobile")]
|
||||
public bool IsMobile { get; set; }
|
||||
|
||||
|
||||
|
||||
private List<NavItem> Navs { get; set; } = new();
|
||||
|
||||
[Inject]
|
||||
private UserResoures UserResoures { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 页面刷新
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task StateHasChangedAsync()
|
||||
{
|
||||
CONFIG_COPYRIGHT = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_COPYRIGHT)).ConfigValue;
|
||||
CONFIG_TITLE = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_TITLE)).ConfigValue;
|
||||
CONFIG_COPYRIGHT_URL = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_COPYRIGHT_URL)).ConfigValue;
|
||||
await UserResoures.InitUserAsync();
|
||||
await UserResoures.InitMenuAsync();
|
||||
Navs = UserResoures.Menus.Parse();
|
||||
Changed = !Changed;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await StateHasChangedAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.Blazor.Core\ThingsGateway.Admin.Blazor.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,363 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>ThingsGateway.Admin.Blazor</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.AppDataTable`4">
|
||||
<summary>
|
||||
通用表格
|
||||
</summary>
|
||||
<typeparam name="TItem"></typeparam>
|
||||
<typeparam name="SearchItem"></typeparam>
|
||||
<typeparam name="AddItem"></typeparam>
|
||||
<typeparam name="EditItem"></typeparam>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.AddCallAsync">
|
||||
<summary>
|
||||
添加项委托
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.AddTemplate">
|
||||
<summary>
|
||||
获得/设置 添加模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.ClassString">
|
||||
<summary>
|
||||
MSheet.Class
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.StyleString">
|
||||
<summary>
|
||||
MSheet.Style
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.DeleteCallAsync">
|
||||
<summary>
|
||||
删除项委托
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.Dense">
|
||||
<summary>
|
||||
表格紧凑
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.EditCallAsync">
|
||||
<summary>
|
||||
编辑项委托
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.EditTemplate">
|
||||
<summary>
|
||||
获得/设置 编辑模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.Detailemplate">
|
||||
<summary>
|
||||
获得/设置 详情模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.FilterHeaders">
|
||||
<summary>
|
||||
表头过滤,返回DataTableHeader列表,传输参数已包含全部初始表头与表头标题
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.Filters">
|
||||
<summary>
|
||||
表头过滤之后执行的方法,返回Filter值,ture则显示,false则隐藏
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.HeaderTemplate">
|
||||
<summary>
|
||||
获得/设置 Table Header 模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsMenuOperTemplate">
|
||||
<summary>
|
||||
右侧操作栏以菜单形式显示
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsPage">
|
||||
<summary>
|
||||
是否分页
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowAddButton">
|
||||
<summary>
|
||||
是否显示添加按钮
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowClearSearch">
|
||||
<summary>
|
||||
是否显示清空搜索
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowDeleteButton">
|
||||
<summary>
|
||||
是否显示删除按钮
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowDetailButton">
|
||||
<summary>
|
||||
是否显示详情按钮
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowEditButton">
|
||||
<summary>
|
||||
是否显示编辑按钮
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowFilter">
|
||||
<summary>
|
||||
是否显示过滤
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowOperCol">
|
||||
<summary>
|
||||
是否显示右侧操作栏
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowQueryButton">
|
||||
<summary>
|
||||
是否显示查询按钮
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowSearchKey">
|
||||
<summary>
|
||||
是否显示搜索关键字
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowSelect">
|
||||
<summary>
|
||||
是否显示表格多项选择
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.IsShowToolbar">
|
||||
<summary>
|
||||
是否显示顶部操作工具栏
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.ItemColOperTemplate">
|
||||
<summary>
|
||||
获得/设置 Table Oper 模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.ItemColTemplate">
|
||||
<summary>
|
||||
获得/设置 Table Cols 模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.ItemColWithDTTemplate">
|
||||
<summary>
|
||||
独立设置 Table Cols 模板,需自行实现DateTime类型的时区转换
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.Items">
|
||||
<summary>
|
||||
当前显示项目
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.OtherToolbarTemplate">
|
||||
<summary>
|
||||
获得/设置 其他操作栏模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.PageSizeItems">
|
||||
<summary>
|
||||
分页选择项目
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.QueryCallAsync">
|
||||
<summary>
|
||||
查询项委托
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.SearchModel">
|
||||
<summary>
|
||||
获得/设置 SearchModel 实例
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.SearchTemplate">
|
||||
<summary>
|
||||
获得/设置 查询与操作栏模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.QueryClickAsync">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.OnAfterRenderAsync(System.Boolean)">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.OnInitializedAsync">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Core.IAppDataTable">
|
||||
<summary>
|
||||
通用表格
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Core.IAppDataTable.QueryClickAsync">
|
||||
<summary>
|
||||
查询刷新
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.StringFilters">
|
||||
<summary>
|
||||
键值表示
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.StringFilters.Key">
|
||||
<summary>
|
||||
键
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.StringFilters.Value">
|
||||
<summary>
|
||||
值
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Base">
|
||||
<summary>
|
||||
Base
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Base.OnAfterRenderAsync(System.Boolean)">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
<param name="firstRender"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Config">
|
||||
<summary>
|
||||
系统配置页面
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Config.OnParametersSetAsync">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Index">
|
||||
<summary>
|
||||
首页
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Index.OnParametersSetAsync">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Menu">
|
||||
<summary>
|
||||
菜单页面
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Menu.OnParametersSetAsync">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.OpenApiSession">
|
||||
<summary>
|
||||
OpenApiSession
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.OpenApiUserR">
|
||||
<summary>
|
||||
OpenApiUserR
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Oplog">
|
||||
<summary>
|
||||
操作日志
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Oplog.OnInitialized">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Role">
|
||||
<summary>
|
||||
角色页面
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Session">
|
||||
<summary>
|
||||
会话页面
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Spa">
|
||||
<summary>
|
||||
SPA
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.User">
|
||||
<summary>
|
||||
用户界面
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.UserCenter">
|
||||
<summary>
|
||||
个人设置
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.UserCenter.OnParametersSetAsync">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Vislog">
|
||||
<summary>
|
||||
访问日志页面
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Vislog.CategoryFilters">
|
||||
<summary>
|
||||
日志分类菜单
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.Vislog.ExeStatus">
|
||||
<summary>
|
||||
执行结果菜单
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Vislog.OnInitialized">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.Login">
|
||||
<summary>
|
||||
登录页面
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.Login.OnParametersSetAsync">
|
||||
<inheritdoc/>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Admin.Blazor.MainLayout">
|
||||
<summary>
|
||||
Layout
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:ThingsGateway.Admin.Blazor.MainLayout.IsMobile">
|
||||
<summary>
|
||||
IsMobile
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.MainLayout.StateHasChangedAsync">
|
||||
<summary>
|
||||
页面刷新
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Admin.Blazor.MainLayout.OnInitializedAsync">
|
||||
<summary>
|
||||
<inheritdoc/>
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -1,81 +0,0 @@
|
||||
#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 Mapster;
|
||||
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 系统内存缓存
|
||||
/// </summary>
|
||||
public class SysMemoryCache : IDisposable
|
||||
{
|
||||
private readonly MemoryCache _cache;
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="SysMemoryCache"/>
|
||||
/// </summary>
|
||||
public SysMemoryCache()
|
||||
{
|
||||
_cache = new MemoryCache(new MemoryCacheOptions());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Get<T>(string key, bool mapster)
|
||||
{
|
||||
return _cache.TryGetValue<T>(key, out var value) ? mapster ? value.Adapt<T>() : value : default;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<T> GetOrCreateAsync<T>(string key, Func<ICacheEntry, Task<T>> func, bool mapster) where T : class
|
||||
{
|
||||
var value = await _cache.GetOrCreateAsync(key, func);
|
||||
return mapster ? value.Adapt<T>() : value;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T GetOrCreate<T>(string key, Func<ICacheEntry, T> func, bool mapster) where T : class
|
||||
{
|
||||
var value = _cache.GetOrCreate(key, func);
|
||||
return mapster ? value.Adapt<T>() : value;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Remove(object key)
|
||||
{
|
||||
_cache.Remove(key);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Set<T>(object key, T value, bool mapster)
|
||||
{
|
||||
_cache.Set(key, mapster ? value.Adapt<T>() : value);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Set<T>(object key, T value, TimeSpan offset, bool mapster)
|
||||
{
|
||||
_cache.Set(key, mapster ? value.Adapt<T>() : value, offset);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
_cache?.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Collections;
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 对象拓展
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
public static class ListExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// List转DataTable
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="list"></param>
|
||||
/// <returns></returns>
|
||||
public static DataTable ToDataTable<T>(this List<T> list)
|
||||
{
|
||||
DataTable result = new();
|
||||
if (list.Count > 0)
|
||||
{
|
||||
var propertys = list[0].GetType().GetPropertiesWithCache();
|
||||
foreach (PropertyInfo pi in propertys)
|
||||
{
|
||||
Type colType = pi.PropertyType;
|
||||
if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
colType = colType.GetGenericArguments().First();
|
||||
}
|
||||
if (IsIgnoreColumn(pi))
|
||||
continue;
|
||||
if (IsJsonColumn(pi))//如果是json特性就是sting类型
|
||||
colType = typeof(string);
|
||||
if (colType.IsEnum)//如果是Enum需要转string才会保存Enum字符串
|
||||
colType = typeof(string);
|
||||
result.Columns.Add(pi.Name, colType);
|
||||
}
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
ArrayList tempList = new();
|
||||
foreach (PropertyInfo pi in propertys)
|
||||
{
|
||||
if (IsIgnoreColumn(pi))
|
||||
continue;
|
||||
object obj = pi.GetValue(list[i], null);
|
||||
if (IsJsonColumn(pi))//如果是json特性就是转化为json格式
|
||||
obj = obj?.ToJsonString();//如果json字符串是空就传null
|
||||
tempList.Add(obj);
|
||||
}
|
||||
object[] array = tempList.ToArray();
|
||||
result.LoadDataRow(array, true);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SqlSugar是否忽略字段
|
||||
/// </summary>
|
||||
/// <param name="pi"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsIgnoreColumn(PropertyInfo pi)
|
||||
{
|
||||
return pi.GetCustomAttribute<SugarColumn>(false).IsIgnore == true;
|
||||
}
|
||||
/// <summary>
|
||||
/// SqlSugar是否Json字段
|
||||
/// </summary>
|
||||
/// <param name="pi"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsJsonColumn(PropertyInfo pi)
|
||||
{
|
||||
return pi.GetCustomAttribute<SugarColumn>(false).IsJson == true;
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 对象拓展
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回List,无其他处理
|
||||
/// </summary>
|
||||
public static List<string> StringToList(this string str)
|
||||
{
|
||||
return new List<string>() { str };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用 正则表达式 判断字符是不是汉字
|
||||
/// </summary>
|
||||
/// <param name="text">待判断字符或字符串</param>
|
||||
/// <returns>真:是汉字;假:不是</returns>
|
||||
private static bool IsChinese(string text) => Regex.IsMatch(text, @"[\u4e00-\u9fbb]");
|
||||
|
||||
/// <summary>
|
||||
/// 获取字符串中的两个字符作为名称简述
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetNameLen2(this string name)
|
||||
{
|
||||
if (name.IsNullOrEmpty())
|
||||
return string.Empty;
|
||||
var nameLength = name.Length;//获取姓名长度
|
||||
string nameWritten = name;//需要绘制的文字
|
||||
if (nameLength > 2)//如果名字长度超过2个
|
||||
{
|
||||
// 如果用户输入的姓名大于等于3个字符,截取后面两位
|
||||
string firstName = name.Substring(0, 1);
|
||||
if (IsChinese(firstName))
|
||||
{
|
||||
// 截取倒数两位汉字
|
||||
nameWritten = name.Substring(name.Length - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 截取第一个英文字母和第二个大写的字母
|
||||
var data = Regex.Match(name, @"[A-Z]?[a-z]+([A-Z])").Value;
|
||||
nameWritten = data.FirstCharToUpper() + data.LastCharToUpper();
|
||||
if (nameWritten.IsNullOrEmpty())
|
||||
{
|
||||
nameWritten = name.FirstCharToUpper() + name.LastCharToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nameWritten;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字符串是 null 或者 空
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNullOrEmpty(this string value) => value == null || value!.Length <= 0;
|
||||
|
||||
/// <summary>
|
||||
/// 返回字符串首字符的大写字母
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static string FirstCharToUpper(this string input) => input.IsNullOrEmpty() ? input : input.First().ToString().ToUpper();
|
||||
|
||||
/// <summary>
|
||||
/// 返回字符串尾字符的大写字母
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static string LastCharToUpper(this string input) => input.IsNullOrEmpty() ? input : input.Last().ToString().ToUpper();
|
||||
|
||||
/// <summary>
|
||||
/// 转换布尔值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool ToBoolean(this string value, bool defaultValue = false) => value?.ToUpper() switch
|
||||
{
|
||||
"1" or "TRUE" => true,
|
||||
_ => defaultValue,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// ToLong
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static long ToLong(this string value, long defaultValue = 0) => value.IsNullOrEmpty() ? defaultValue : Int64.TryParse(value, out var n) ? n : defaultValue;
|
||||
|
||||
/// <summary>
|
||||
/// ToInt
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int ToInt(this string value, int defaultValue = 0) => value.IsNullOrEmpty() ? defaultValue : Int32.TryParse(value, out var n) ? n : defaultValue;
|
||||
|
||||
/// <summary>
|
||||
/// ToDecimal
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static decimal ToDecimal(this string value, int defaultValue = 0) => value.IsNullOrEmpty() ? defaultValue : Decimal.TryParse(value, out var n) ? n : defaultValue;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 匹配手机号码
|
||||
/// </summary>
|
||||
/// <param name="s">源字符串</param>
|
||||
/// <returns>是否匹配成功</returns>
|
||||
public static bool MatchPhoneNumber(this string s) => !string.IsNullOrEmpty(s) && Regex.IsMatch(s, @"^1[3456789][0-9]{9}$");
|
||||
|
||||
/// <summary>
|
||||
/// 匹配邮箱格式
|
||||
/// </summary>
|
||||
/// <param name="s">源字符串</param>
|
||||
/// <returns>是否匹配成功</returns>
|
||||
public static bool MatchEmail(this string s) => !string.IsNullOrEmpty(s) && Regex.IsMatch(s, @"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
|
||||
|
||||
/// <summary>合并多段路径</summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="ps"></param>
|
||||
/// <returns></returns>
|
||||
public static string CombinePath(this string path, params string[] ps)
|
||||
{
|
||||
if (ps == null || ps.Length <= 0) return path;
|
||||
path ??= string.Empty;
|
||||
|
||||
foreach (var item in ps)
|
||||
{
|
||||
if (!item.IsNullOrEmpty()) path = Path.Combine(path, item);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 对象拓展
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
public static class SysDateTimeExtensions
|
||||
{
|
||||
private static readonly DateTime _dt1970 = new(1970, 1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// 系统默认使用的当前时间
|
||||
/// </summary>
|
||||
public static DateTime CurrentDateTime => DateTime.Now;
|
||||
/// <summary>
|
||||
/// 返回yyyy-MM-dd HH:mm:ss:fff zz时间格式字符串
|
||||
/// </summary>
|
||||
public static string ToDefaultDateTimeFormat(this in DateTime dt, TimeSpan offset)
|
||||
{
|
||||
if (dt.Kind == DateTimeKind.Utc)
|
||||
return new DateTimeOffset(dt.ToLocalTime(), offset).ToString("yyyy-MM-dd HH:mm:ss:fff zz");
|
||||
else if (dt == DateTime.MinValue || dt == DateTime.MaxValue)
|
||||
return dt.ToString("yyyy-MM-dd HH:mm:ss:fff zz");
|
||||
else
|
||||
return new DateTimeOffset(dt, offset).ToString("yyyy-MM-dd HH:mm:ss:fff zz");
|
||||
}
|
||||
/// <summary>
|
||||
/// 返回yyyy-MM-dd HH:mm:ss:fff zz时间格式字符串
|
||||
/// </summary>
|
||||
public static string ToDefaultDateTimeFormat(this in DateTime dt)
|
||||
{
|
||||
return dt.ToString("yyyy-MM-dd HH:mm:ss:fff zz");
|
||||
}
|
||||
/// <summary>
|
||||
/// 返回yyyy-MM-dd HH:mm:ss:fff zz时间格式字符串
|
||||
/// </summary>
|
||||
public static string ToFileDateTimeFormat(this in DateTime dt)
|
||||
{
|
||||
return dt.ToString("yyyy-MM-dd HH-mm-ss-fff zz");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 计算2个时间差,返回文字描述
|
||||
/// </summary>
|
||||
/// <param name="beginTime">开始时间</param>
|
||||
/// <param name="endTime">结束时间</param>
|
||||
/// <returns>时间差</returns>
|
||||
public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime)
|
||||
{
|
||||
string strResout = string.Empty;
|
||||
|
||||
//获得2时间的时间间隔秒计算
|
||||
TimeSpan span = endTime.Subtract(beginTime);
|
||||
int sec = Convert.ToInt32(span.TotalSeconds);
|
||||
int minutes = 1 * 60;
|
||||
int hours = minutes * 60;
|
||||
int day = hours * 24;
|
||||
int month = day * 30;
|
||||
int year = month * 12;
|
||||
|
||||
//提醒时间,到了返回1,否则返回0
|
||||
if (sec > year)
|
||||
{
|
||||
strResout += (sec / year) + "年";
|
||||
sec %= year; //剩余
|
||||
}
|
||||
|
||||
if (sec > month)
|
||||
{
|
||||
strResout += (sec / month) + "月";
|
||||
sec %= month;
|
||||
}
|
||||
|
||||
if (sec > day)
|
||||
{
|
||||
strResout += (sec / day) + "天";
|
||||
sec %= day;
|
||||
}
|
||||
|
||||
if (sec > hours)
|
||||
{
|
||||
strResout += (sec / hours) + "小时";
|
||||
sec %= hours;
|
||||
}
|
||||
|
||||
if (sec > minutes)
|
||||
{
|
||||
strResout += (sec / minutes) + "分";
|
||||
sec %= minutes;
|
||||
}
|
||||
|
||||
strResout += sec + "秒";
|
||||
return strResout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算2个时间差,返回文字描述
|
||||
/// </summary>
|
||||
/// <param name="beginTime">开始时间</param>
|
||||
/// <param name="endTime">结束时间</param>
|
||||
/// <returns>时间差</returns>
|
||||
public static string GetDiffTime(this in DateTimeOffset beginTime, in DateTimeOffset endTime)
|
||||
{
|
||||
string strResout = string.Empty;
|
||||
|
||||
//获得2时间的时间间隔秒计算
|
||||
TimeSpan span = endTime.Subtract(beginTime);
|
||||
int sec = Convert.ToInt32(span.TotalSeconds);
|
||||
int minutes = 1 * 60;
|
||||
int hours = minutes * 60;
|
||||
int day = hours * 24;
|
||||
int month = day * 30;
|
||||
int year = month * 12;
|
||||
|
||||
//提醒时间,到了返回1,否则返回0
|
||||
if (sec > year)
|
||||
{
|
||||
strResout += (sec / year) + "年";
|
||||
sec %= year; //剩余
|
||||
}
|
||||
|
||||
if (sec > month)
|
||||
{
|
||||
strResout += (sec / month) + "月";
|
||||
sec %= month;
|
||||
}
|
||||
|
||||
if (sec > day)
|
||||
{
|
||||
strResout += (sec / day) + "天";
|
||||
sec %= day;
|
||||
}
|
||||
|
||||
if (sec > hours)
|
||||
{
|
||||
strResout += (sec / hours) + "小时";
|
||||
sec %= hours;
|
||||
}
|
||||
|
||||
if (sec > minutes)
|
||||
{
|
||||
strResout += (sec / minutes) + "分";
|
||||
sec %= minutes;
|
||||
}
|
||||
|
||||
strResout += sec + "秒";
|
||||
return strResout;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ToLong
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static long ToLong(this DateTime value, long defaultValue = 0)
|
||||
{
|
||||
// 特殊处理时间,转Unix毫秒
|
||||
if (value == DateTime.MinValue) return 0;
|
||||
|
||||
//// 先转UTC时间再相减,以得到绝对时间差
|
||||
//return (Int32)(dt.ToUniversalTime() - _dt1970).TotalSeconds;
|
||||
return (Int64)(value - _dt1970).TotalMilliseconds;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
#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 Furion.Logging;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace ThingsGateway.Admin.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 日志写入文件的组件
|
||||
/// </summary>
|
||||
public sealed class LoggingFileComponent : IServiceComponent
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void Load(IServiceCollection services, ComponentContext componentContext)
|
||||
{
|
||||
var logFileEnable = App.GetConfig<bool?>("Logging:LogEnable:File");
|
||||
if (logFileEnable != true) return;
|
||||
|
||||
//获取默认日志等级
|
||||
var defaultLevel = App.GetConfig<LogLevel?>("Logging:LogLevel:File");
|
||||
//获取程序根目录
|
||||
var rootPath = App.HostEnvironment.ContentRootPath;
|
||||
if (defaultLevel != null)//如果默认日志等级不是空
|
||||
{
|
||||
//遍历日志等级
|
||||
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel)))
|
||||
{
|
||||
//如果日志等级是默认等级和最大等级之间
|
||||
if (level >= defaultLevel && level != LogLevel.None)
|
||||
{
|
||||
//每天创建一个日志文件
|
||||
services.AddLogging(builder =>
|
||||
{
|
||||
var fileName = "logs/" + level.ToString() + "/{0:yyyy}-{0:MM}-{0:dd}{0:zz}.log";
|
||||
builder.AddFile(fileName, options =>
|
||||
{
|
||||
SetLogOptions(options, level);//日志格式化
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//添加日志文件
|
||||
services.AddFileLogging("logs/{0:yyyy}-{0:MM}-{0:dd}{0:zz}.log", options =>
|
||||
{
|
||||
SetLogOptions(options, null);//日志格式化
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志格式化
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="logLevel"></param>
|
||||
private static void SetLogOptions(FileLoggerOptions options, LogLevel? logLevel)
|
||||
{
|
||||
//每天创建一个日志文件
|
||||
var rootPath = App.HostEnvironment.ContentRootPath;
|
||||
if (logLevel != null)//如果日志等级不为空
|
||||
{
|
||||
//过滤日志等级
|
||||
options.WriteFilter = (logMsg) =>
|
||||
{
|
||||
//不写入LoggingMonitor
|
||||
if (logMsg.LogName == "System.Logging.LoggingMonitor")
|
||||
return false;
|
||||
//只写入NetCore日志
|
||||
if (!logMsg.LogName.StartsWith("System") && !logMsg.LogName.StartsWith("Microsoft"))
|
||||
return false;
|
||||
return logMsg.LogLevel == logLevel;
|
||||
};
|
||||
}
|
||||
//定义日志文件名
|
||||
options.FileNameRule = fileName =>
|
||||
{
|
||||
return rootPath + "\\" + string.Format(fileName, SysDateTimeExtensions.CurrentDateTime);
|
||||
};
|
||||
options.FileSizeLimitBytes = 50 * 1024 * 1024;//日志最大50M
|
||||
options.MessageFormat = logMsg =>
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
stringBuilder.AppendLine("【日志级别】:" + logMsg.LogLevel);
|
||||
stringBuilder.AppendLine("【日志类名】:" + logMsg.LogName);
|
||||
stringBuilder.AppendLine("【日志时间】:" + SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
|
||||
stringBuilder.AppendLine("【日志内容】:" + logMsg.Message);
|
||||
if (logMsg.Exception != null)
|
||||
{
|
||||
stringBuilder.AppendLine("【异常信息】:" + logMsg.Exception);
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.8.40" />
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.8.40" />
|
||||
<PackageReference Include="Furion.Pure" Version="4.8.8.40" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.96" />
|
||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||
<PackageReference Include="MiniExcel" Version="1.31.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,135 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.6.33829.357
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "script", "script", "{0BBD36E5-4FDB-4A9F-8387-6A39BEB6D87D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\.gitattributes = ..\.gitattributes
|
||||
..\.gitignore = ..\.gitignore
|
||||
Directory.Build.props = Directory.Build.props
|
||||
..\LICENSE.txt = ..\LICENSE.txt
|
||||
..\README.md = ..\README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor.Core", "ThingsGateway.Admin.Blazor.Core\ThingsGateway.Admin.Blazor.Core.csproj", "{096A0468-B4F0-490F-ABDF-78F66CCE2870}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{6819B227-F69F-4478-819B-B95F170E11D5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{07EEACCE-83E1-459F-89B8-C2DFD30AB86E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{516316AA-8891-4741-8D30-9565CEB64FEA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x86 = Debug|x86
|
||||
KINGVIEW|Any CPU = KINGVIEW|Any CPU
|
||||
KINGVIEW|x86 = KINGVIEW|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.KINGVIEW|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.KINGVIEW|Any CPU.Build.0 = Release|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.KINGVIEW|x86.ActiveCfg = Release|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.KINGVIEW|x86.Build.0 = Release|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{62FA6CF1-DB7E-4457-9EB7-C825A89751EC}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Debug|x86.Build.0 = Debug|x86
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.KINGVIEW|Any CPU.ActiveCfg = KINGVIEW|Any CPU
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.KINGVIEW|Any CPU.Build.0 = KINGVIEW|Any CPU
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.KINGVIEW|x86.ActiveCfg = KINGVIEW|x86
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.KINGVIEW|x86.Build.0 = KINGVIEW|x86
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Release|x86.ActiveCfg = Release|x86
|
||||
{1B5B6C91-4A6C-4635-8B2C-D0F0957D3788}.Release|x86.Build.0 = Release|x86
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.KINGVIEW|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.KINGVIEW|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.KINGVIEW|x86.ActiveCfg = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.KINGVIEW|x86.Build.0 = Debug|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E7DD3841-A7AD-4CC9-9122-E62C7976AA99}.Release|x86.Build.0 = Release|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.KINGVIEW|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.KINGVIEW|Any CPU.Build.0 = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.KINGVIEW|x86.ActiveCfg = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.KINGVIEW|x86.Build.0 = Debug|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{096A0468-B4F0-490F-ABDF-78F66CCE2870}.Release|x86.Build.0 = Release|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.KINGVIEW|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.KINGVIEW|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.KINGVIEW|x86.ActiveCfg = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.KINGVIEW|x86.Build.0 = Debug|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{6819B227-F69F-4478-819B-B95F170E11D5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.KINGVIEW|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.KINGVIEW|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.KINGVIEW|x86.ActiveCfg = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.KINGVIEW|x86.Build.0 = Debug|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2C2BEC10-8B46-4892-8CA5-0FCF1477834E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.KINGVIEW|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.KINGVIEW|Any CPU.Build.0 = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.KINGVIEW|x86.ActiveCfg = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.KINGVIEW|x86.Build.0 = Debug|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{516316AA-8891-4741-8D30-9565CEB64FEA}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {38B292E0-B3E7-4608-9912-1B057C2A508F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,15 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Application\ThingsGateway.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,125 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>ThingsGateway.ApiController</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:ThingsGateway.ApiController.CollectDbInfoControler">
|
||||
<summary>
|
||||
采集设备
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.CollectDbInfoControler.#ctor(Microsoft.Extensions.DependencyInjection.IServiceScopeFactory,ThingsGateway.Application.IVariableService,ThingsGateway.Application.ICollectDeviceService)">
|
||||
<inheritdoc cref="T:ThingsGateway.ApiController.CollectDbInfoControler"/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.CollectDbInfoControler.GetCollectDeviceList(ThingsGateway.Application.CollectDevicePageInput)">
|
||||
<summary>
|
||||
获取采集设备信息
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.CollectDbInfoControler.GetVariableList(ThingsGateway.Application.VariablePageInput)">
|
||||
<summary>
|
||||
获取变量信息
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.ApiController.FileController">
|
||||
<summary>
|
||||
文件下载
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.FileController.#ctor(ThingsGateway.Application.IRpcLogService,ThingsGateway.Application.IBackendLogService,ThingsGateway.Application.ICollectDeviceService,ThingsGateway.Application.IUploadDeviceService,ThingsGateway.Application.IVariableService)">
|
||||
<summary>
|
||||
<inheritdoc cref="T:ThingsGateway.ApiController.FileController"/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.FileController.DownloadRpcLogAsync(ThingsGateway.Application.RpcLogInput)">
|
||||
<summary>
|
||||
下载RPC日志
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.FileController.DownloadBackendLogAsync(ThingsGateway.Application.BackendLogInput)">
|
||||
<summary>
|
||||
下载后台日志
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.FileController.DownloadCollectDeviceAsync(ThingsGateway.Application.CollectDeviceInput)">
|
||||
<summary>
|
||||
导出采集设备
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.FileController.DownloadUploadDeviceAsync(ThingsGateway.Application.UploadDeviceInput)">
|
||||
<summary>
|
||||
导出上传设备
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.FileController.DownloadDeviceVariableAsync(ThingsGateway.Application.DeviceVariableInput)">
|
||||
<summary>
|
||||
导出采集变量
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.FileController.DownloadMemoryVariableAsync(ThingsGateway.Application.MemoryVariableInput)">
|
||||
<summary>
|
||||
导出内存变量
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.ApiController.RpcControler">
|
||||
<summary>
|
||||
设备控制
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.RpcControler.#ctor(Microsoft.Extensions.DependencyInjection.IServiceScopeFactory)">
|
||||
<inheritdoc cref="T:ThingsGateway.ApiController.RpcControler"/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.RpcControler.ConfigDeviceThread(System.Int64,System.Boolean)">
|
||||
<summary>
|
||||
控制采集线程启停
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.RpcControler.UpDeviceThread(System.Int64)">
|
||||
<summary>
|
||||
重启采集线程
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.ApiController.RpcControler.WriteDeviceMethods(System.Collections.Generic.Dictionary{System.String,System.String})">
|
||||
<summary>
|
||||
写入多个变量
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ThingsGateway.Web.Foundation.CollectInfoControler">
|
||||
<summary>
|
||||
采集状态信息
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Web.Foundation.CollectInfoControler.#ctor(Microsoft.Extensions.DependencyInjection.IServiceScopeFactory)">
|
||||
<inheritdoc cref="T:ThingsGateway.Web.Foundation.CollectInfoControler"/>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Web.Foundation.CollectInfoControler.GetCollectDeviceList">
|
||||
<summary>
|
||||
获取设备信息
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Web.Foundation.CollectInfoControler.GetDeviceVariableList(ThingsGateway.Application.VariablePageInput)">
|
||||
<summary>
|
||||
获取变量信息
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ThingsGateway.Web.Foundation.CollectInfoControler.GetRealAlarmList(ThingsGateway.Application.VariablePageInput)">
|
||||
<summary>
|
||||
获取实时报警信息
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -1,133 +0,0 @@
|
||||
#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 System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// 缓存帮助类
|
||||
/// </summary>
|
||||
public class CacheDb
|
||||
{
|
||||
private readonly string Id;
|
||||
/// <summary>
|
||||
/// 构造函数传入Id号作为Sqlite文件名称
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
public CacheDb(string id)
|
||||
{
|
||||
Id = id;
|
||||
Directory.CreateDirectory(Path.Combine(AppContext.BaseDirectory, "CacheDb"));
|
||||
if (!File.Exists(Path.Combine(AppContext.BaseDirectory, "CacheDb", $"{Id}.db")))
|
||||
{
|
||||
GetCacheDb().DbMaintenance.CreateDatabase();//创建数据库
|
||||
GetCacheDb().CodeFirst.InitTables(typeof(CacheTable));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取数据库链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private SqlSugarClient GetCacheDb()
|
||||
{
|
||||
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 = $"Data Source=CacheDb/{Id}.db;",//连接字符串
|
||||
DbType = DbType.Sqlite,//数据库类型
|
||||
IsAutoCloseConnection = true, //不设成true要手动close
|
||||
ConfigureExternalServices = configureExternalServices,
|
||||
}
|
||||
);
|
||||
return sqlSugarClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存表前n条
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<CacheTable>> GetCacheData(int take)
|
||||
{
|
||||
var db = GetCacheDb();
|
||||
var data = await db.Queryable<CacheTable>().Take(take).ToListAsync();
|
||||
return data;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取缓存表全部
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<CacheTable>> GetCacheData()
|
||||
{
|
||||
var db = GetCacheDb();
|
||||
var data = await db.Queryable<CacheTable>().ToListAsync();
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增加离线缓存,限制表最大默认2000行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> AddCacheData(string topic, string data, int max = 2000)
|
||||
{
|
||||
var db = GetCacheDb();
|
||||
var count = await db.Queryable<CacheTable>().CountAsync();
|
||||
if (count > max)
|
||||
{
|
||||
var data1 = await db.Queryable<CacheTable>().OrderBy(a => a.Id).Take(count - max).ToListAsync();
|
||||
await db.Deleteable(data1).ExecuteCommandAsync();
|
||||
}
|
||||
var result = await db.Insertable(new CacheTable() { Id = YitIdHelper.NextId(), Topic = topic, CacheStr = data }).ExecuteCommandAsync();
|
||||
return result > 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// 清除离线缓存
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> DeleteCacheData(params long[] data)
|
||||
{
|
||||
var db = GetCacheDb();
|
||||
var result = await db.Deleteable<CacheTable>().In(data).ExecuteCommandAsync();
|
||||
return result > 0;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 缓存表
|
||||
/// </summary>
|
||||
public class CacheTable
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
/// <summary>
|
||||
/// Topic
|
||||
/// </summary>
|
||||
public string Topic { get; set; }
|
||||
/// <summary>
|
||||
/// 缓存值
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString)]
|
||||
public string CacheStr { get; set; }
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#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.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 采集设备表
|
||||
/// </summary>
|
||||
[SugarTable("collectDevice", TableDescription = "采集设备表")]
|
||||
[Tenant(ThingsGatewayConst.DB_ThingsGateway)]
|
||||
[SugarIndex("unique_collectdevice_name", nameof(CollectDevice.Name), OrderByType.Asc, true)]
|
||||
public class CollectDevice : UploadDevice
|
||||
{
|
||||
#region 冗余配置
|
||||
|
||||
/// <summary>
|
||||
/// 是否冗余
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsRedundant", ColumnDescription = "是否冗余")]
|
||||
[DataTable(Order = 9, IsShow = true, Sortable = true)]
|
||||
public bool IsRedundant { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 冗余设备Id,只能选择相同驱动
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "RedundantDeviceId", ColumnDescription = "冗余设备Id")]
|
||||
[IgnoreExcel]
|
||||
public long RedundantDeviceId { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#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 ThingsGateway.Foundation;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// 设备变量表
|
||||
/// </summary>
|
||||
[SugarTable("deviceVariable", TableDescription = "设备变量表")]
|
||||
[Tenant(ThingsGatewayConst.DB_ThingsGateway)]
|
||||
[SugarIndex("index_device", nameof(DeviceVariable.DeviceId), OrderByType.Asc)]
|
||||
[SugarIndex("unique_deviceVariable_name", nameof(DeviceVariable.Name), OrderByType.Asc, true)]
|
||||
public class DeviceVariable : MemoryVariable
|
||||
{
|
||||
/// <summary>
|
||||
/// 设备
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "DeviceId", ColumnDescription = "设备")]
|
||||
[DataTable(Order = 3, IsShow = true, Sortable = true)]
|
||||
[IgnoreExcel]
|
||||
public virtual long DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单位
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Unit", ColumnDescription = "单位", Length = 200, IsNullable = true)]
|
||||
[DataTable(Order = 3, IsShow = true, Sortable = true)]
|
||||
public string Unit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 执行间隔
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "InvokeInterval", ColumnDescription = "执行间隔")]
|
||||
[DataTable(Order = 3, IsShow = true, Sortable = true)]
|
||||
public virtual int IntervalTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 其他方法,若不为空,此时Address为方法参数
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "OtherMethod", ColumnDescription = "特殊方法", Length = 200, IsNullable = true)]
|
||||
[DataTable(Order = 7, IsShow = true, Sortable = true)]
|
||||
|
||||
public string OtherMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量地址,可能带有额外的信息,比如<see cref="DataFormat"/> ,以;分割
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "VariableAddress", ColumnDescription = "变量地址", Length = 200, IsNullable = true)]
|
||||
[DataTable(Order = 3, IsShow = true, Sortable = true)]
|
||||
public string VariableAddress { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否中间变量
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "IsMemoryVariable", ColumnDescription = "是否中间变量", IsNullable = false)]
|
||||
[IgnoreExcel]
|
||||
public override bool IsMemoryVariable { get; set; } = false;
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#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.DbConvert;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// 插件信息表
|
||||
/// </summary>
|
||||
[SugarTable("driverPlugin", TableDescription = "插件信息表")]
|
||||
[Tenant(ThingsGatewayConst.DB_ThingsGateway)]
|
||||
[SugarIndex("unique_driverplugin_name", nameof(DriverPlugin.AssembleName), OrderByType.Asc, true)]
|
||||
public class DriverPlugin : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "FileName", ColumnDescription = "文件名称")]
|
||||
[DataTable(Order = 1, IsShow = true, Sortable = true)]
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件类名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "AssembleName", ColumnDescription = "插件类名称")]
|
||||
[DataTable(Order = 2, IsShow = true, Sortable = true)]
|
||||
public string AssembleName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件类型
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "varchar(50)", ColumnName = "DriverTypeEnum", ColumnDescription = "插件类型", SqlParameterDbType = typeof(EnumToStringConvert))]
|
||||
[DataTable(Order = 3, IsShow = true, Sortable = true)]
|
||||
public DriverEnum DriverTypeEnum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件文件全路径
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "FilePath", ColumnDescription = "插件文件全路径")]
|
||||
[DataTable(Order = 4, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")]
|
||||
public string FilePath { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
#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 Furion.DependencyInjection;
|
||||
|
||||
using Hardware.Info;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 硬件信息获取
|
||||
/// </summary>
|
||||
public class HardwareInfoService : ISingleton
|
||||
{
|
||||
/// <summary>
|
||||
/// 硬件信息获取
|
||||
/// </summary>
|
||||
public HardwareInfo HardwareInfo { get; private set; }
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <inheritdoc cref="HardwareInfoService"/>
|
||||
public HardwareInfoService(ILogger<HardwareInfoService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 循环获取
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Init()
|
||||
{
|
||||
try
|
||||
{
|
||||
HardwareInfo = new();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "初始化硬件信息失败");
|
||||
}
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
string currentPath = Directory.GetCurrentDirectory();
|
||||
DriveInfo drive = new(Path.GetPathRoot(currentPath));
|
||||
appInfo = new()
|
||||
{
|
||||
DriveInfo = drive,
|
||||
HostName = Environment.MachineName, // 主机名称
|
||||
SystemOs = RuntimeInformation.OSDescription, // 操作系统
|
||||
OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(), // 系统架构
|
||||
RemoteIp = await GetIpFromOnlineAsync(), // 外网地址
|
||||
FrameworkDescription = RuntimeInformation.FrameworkDescription, // NET框架
|
||||
Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production",
|
||||
Stage = App.HostEnvironment.IsStaging() ? "Stage" : "非Stage", // 是否Stage环境
|
||||
UpdateTime = SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(),
|
||||
};
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
appInfo.UpdateTime = SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat();
|
||||
appInfo.RemoteIp = await GetIpFromOnlineAsync();
|
||||
|
||||
HardwareInfo?.RefreshMemoryStatus();
|
||||
HardwareInfo?.RefreshMemoryList();
|
||||
HardwareInfo?.RefreshNetworkAdapterList();
|
||||
HardwareInfo?.RefreshCPUList();
|
||||
//10秒更新一次
|
||||
await Task.Delay(10000);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "获取硬件信息失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
|
||||
}
|
||||
private TGAPPInfo appInfo = new();
|
||||
/// <summary>
|
||||
/// 运行信息获取
|
||||
/// </summary>
|
||||
public TGAPPInfo APPInfo => appInfo;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// IP地址信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<string> GetIpFromOnlineAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "http://myip.ipip.net";
|
||||
var stream = await new HttpClient().GetStreamAsync(url);
|
||||
var streamReader = new StreamReader(stream, Encoding.UTF8);
|
||||
var html = streamReader.ReadToEnd();
|
||||
return html.Replace("当前 IP:", "").Replace("来自于:", "");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class TGAPPInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 主机环境
|
||||
/// </summary>
|
||||
[Description("主机环境")]
|
||||
public string Environment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// NET框架
|
||||
/// </summary>
|
||||
[Description("NET框架")]
|
||||
public string FrameworkDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主机名称
|
||||
/// </summary>
|
||||
[Description("主机名称")]
|
||||
public string HostName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 系统架构
|
||||
/// </summary>
|
||||
[Description("系统架构")]
|
||||
public string OsArchitecture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 外网地址
|
||||
/// </summary>
|
||||
[Description("外网地址")]
|
||||
public string RemoteIp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stage环境
|
||||
/// </summary>
|
||||
[Description("Stage环境")]
|
||||
public string Stage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作系统
|
||||
/// </summary>
|
||||
[Description("操作系统")]
|
||||
public string SystemOs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[Description("更新时间")]
|
||||
public string UpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前磁盘信息
|
||||
/// </summary>
|
||||
[Description("当前磁盘信息")]
|
||||
public DriveInfo DriveInfo { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
#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 System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 读写扩展
|
||||
/// </summary>
|
||||
public static class ReadWriteHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据数据类型写入设备,只支持C#内置数据类型,但不包含<see cref="decimal"/>和<see cref="char"/>和<see cref="sbyte"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static object GetObjectData(this string value)
|
||||
{
|
||||
//判断数值类型
|
||||
Regex regex = new("^[-+]?[0-9]*\\.?[0-9]+$");
|
||||
bool match = regex.IsMatch(value);
|
||||
if (match)
|
||||
{
|
||||
if (value.ToDecimal() == 0 && Convert.ToInt64(value) != 0)
|
||||
{
|
||||
throw new("转换失败");
|
||||
}
|
||||
return value.ToDecimal();
|
||||
}
|
||||
else if (value.IsBoolValue())
|
||||
{
|
||||
return value.GetBoolValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据<see cref="OperResult.IsSuccess"/>执行action
|
||||
/// </summary>
|
||||
public static OperResult<T> DealWithReadResult<T>(OperResult<T> read, Action<T> action)
|
||||
{
|
||||
if (!read.IsSuccess || action == null)
|
||||
return read;
|
||||
action(read.Content);
|
||||
return read;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据<see cref="PropertyInfo"/> 数据类型转化返回值类型
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static object ObjToTypeValue(this PropertyInfo p, string value)
|
||||
{
|
||||
object _value = null;
|
||||
if (p.PropertyType == typeof(bool))
|
||||
_value = value.GetBoolValue();
|
||||
else if (p.PropertyType == typeof(byte))
|
||||
_value = byte.Parse(value);
|
||||
else if (p.PropertyType == typeof(sbyte))
|
||||
_value = sbyte.Parse(value);
|
||||
else if (p.PropertyType == typeof(short))
|
||||
_value = short.Parse(value);
|
||||
else if (p.PropertyType == typeof(ushort))
|
||||
_value = ushort.Parse(value);
|
||||
else if (p.PropertyType == typeof(int))
|
||||
_value = int.Parse(value);
|
||||
else if (p.PropertyType == typeof(uint))
|
||||
_value = uint.Parse(value);
|
||||
else if (p.PropertyType == typeof(long))
|
||||
_value = long.Parse(value);
|
||||
else if (p.PropertyType == typeof(ulong))
|
||||
_value = ulong.Parse(value);
|
||||
else if (p.PropertyType == typeof(float))
|
||||
_value = float.Parse(value);
|
||||
else if (p.PropertyType == typeof(double))
|
||||
_value = double.Parse(value);
|
||||
else if (p.PropertyType == typeof(decimal))
|
||||
_value = decimal.Parse(value);
|
||||
else if (p.PropertyType == typeof(DateTime))
|
||||
_value = DateTime.Parse(value);
|
||||
else if (p.PropertyType == typeof(DateTimeOffset))
|
||||
_value = DateTimeOffset.Parse(value);
|
||||
else if (p.PropertyType == typeof(string))
|
||||
_value = value;
|
||||
else if (p.PropertyType == typeof(IPAddress))
|
||||
_value = IPAddress.Parse(value);
|
||||
else if (p.PropertyType.IsEnum)
|
||||
_value = Enum.Parse(p.PropertyType, value);
|
||||
return _value;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在返回的字节数组中解析每个变量的值
|
||||
/// 根据每个变量的<see cref="DeviceVariableRunTime.Index"/>
|
||||
/// 不支持变长字符串类型变量,一定不能存在于变量List中
|
||||
/// </summary>
|
||||
/// <param name="buffer">返回的字节数组</param>
|
||||
/// <param name="values">设备变量List</param>
|
||||
/// <param name="startIndex">开始序号</param>
|
||||
public static void PraseStructContent(byte[] buffer, IList<DeviceVariableRunTime> values, int startIndex = 0)
|
||||
{
|
||||
foreach (DeviceVariableRunTime organizedVariable in values)
|
||||
{
|
||||
var deviceValue = organizedVariable;
|
||||
IThingsGatewayBitConverter byteConverter = deviceValue.ThingsGatewayBitConverter;
|
||||
Type propertyType = organizedVariable.DataType;
|
||||
int index = organizedVariable.Index;
|
||||
if (propertyType == typeof(bool))
|
||||
{
|
||||
if (index + startIndex > buffer.Length * 8)
|
||||
throw new Exception($"返回数据长度{buffer.Length}不足以转换为对应索引位{index + startIndex}的数据,请检查数据类型");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index + startIndex > buffer.Length)
|
||||
throw new Exception($"返回数据长度{buffer.Length}不足以转换为对应索引位{index + startIndex}的数据,请检查数据类型");
|
||||
}
|
||||
|
||||
if (propertyType == typeof(byte))
|
||||
{
|
||||
byte num = byteConverter.ToByte(buffer, index + startIndex);
|
||||
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(short))
|
||||
{
|
||||
short num = byteConverter.ToInt16(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(ushort))
|
||||
{
|
||||
ushort num = byteConverter.ToUInt16(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(int))
|
||||
{
|
||||
int num = byteConverter.ToInt32(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(uint))
|
||||
{
|
||||
uint num = byteConverter.ToUInt32(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(long))
|
||||
{
|
||||
long num = byteConverter.ToInt64(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(ulong))
|
||||
{
|
||||
ulong num = byteConverter.ToUInt64(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(float))
|
||||
{
|
||||
float num = byteConverter.ToSingle(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(double))
|
||||
{
|
||||
double num = byteConverter.ToDouble(buffer, index + startIndex);
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(bool))
|
||||
{
|
||||
bool num = byteConverter.ToBoolean(buffer, index + (startIndex * 8));
|
||||
Set(organizedVariable, num);
|
||||
}
|
||||
else if (propertyType == typeof(string))
|
||||
{
|
||||
string num = byteConverter.ToString(buffer, index + startIndex, byteConverter.StringLength);
|
||||
Set(organizedVariable, num);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
static void Set(DeviceVariableRunTime organizedVariable, object num)
|
||||
{
|
||||
var operResult = organizedVariable.SetValue(num); ;
|
||||
if (!operResult.IsSuccess)
|
||||
{
|
||||
throw new Exception(operResult.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
#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 System.ComponentModel;
|
||||
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 上传设备运行状态
|
||||
/// </summary>
|
||||
public class UploadDeviceRunTime : UploadDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// 设备驱动名称
|
||||
/// </summary>
|
||||
[Description("设备驱动名称")]
|
||||
public string PluginName { get; set; }
|
||||
/// <summary>
|
||||
/// 关联变量数量
|
||||
/// </summary>
|
||||
[Description("关联变量数量")]
|
||||
public int UploadVariableCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备活跃时间
|
||||
/// </summary>
|
||||
[Description("活跃时间")]
|
||||
public DateTime ActiveTime { get; private set; } = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// 设备状态
|
||||
/// </summary>
|
||||
[Description("设备状态")]
|
||||
public DeviceStatusEnum DeviceStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
if (KeepRun)
|
||||
return deviceStatus;
|
||||
else
|
||||
return DeviceStatusEnum.Pause;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (deviceStatus != value)
|
||||
{
|
||||
deviceStatus = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
private string lastErrorMessage;
|
||||
/// <summary>
|
||||
/// 最后一次失败原因
|
||||
/// </summary>
|
||||
[Description("最后一次失败原因")]
|
||||
public string LastErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return lastErrorMessage;
|
||||
}
|
||||
private set
|
||||
{
|
||||
lastErrorMessage = SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat() + " - " + value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行
|
||||
/// </summary>
|
||||
[Description("运行")]
|
||||
public bool KeepRun { get; set; } = true;
|
||||
|
||||
private int errorCount;
|
||||
/// <summary>
|
||||
/// 距上次成功时的读取失败次数,超过3次设备更新为离线,等于0时设备更新为在线
|
||||
/// </summary>
|
||||
[Description("失败次数")]
|
||||
public int ErrorCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return errorCount;
|
||||
}
|
||||
private set
|
||||
{
|
||||
errorCount = value;
|
||||
if (errorCount > 3)
|
||||
{
|
||||
DeviceStatus = DeviceStatusEnum.OffLine;
|
||||
}
|
||||
else if (errorCount == 0)
|
||||
{
|
||||
DeviceStatus = DeviceStatusEnum.OnLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DeviceStatusEnum deviceStatus = DeviceStatusEnum.None;
|
||||
|
||||
/// <summary>
|
||||
/// 传入设备的状态信息
|
||||
/// </summary>
|
||||
/// <param name="activeTime"></param>
|
||||
/// <param name="errorCount"></param>
|
||||
/// <param name="lastErrorMessage"></param>
|
||||
public void SetDeviceStatus(DateTime? activeTime = null, int? errorCount = null, string lastErrorMessage = null)
|
||||
{
|
||||
if (activeTime != null)
|
||||
ActiveTime = activeTime.Value;
|
||||
if (errorCount != null)
|
||||
ErrorCount = errorCount.Value;
|
||||
if (lastErrorMessage != null)
|
||||
LastErrorMessage = lastErrorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Foundation.Serial;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// <para></para>
|
||||
/// 采集插件,继承实现不同PLC通讯
|
||||
/// <para></para>
|
||||
/// 读取字符串,DateTime等等不确定返回字节数量的方法属性特殊方法,需使用<see cref="DeviceMethodAttribute"/>特性标识
|
||||
/// </summary>
|
||||
public abstract class CollectBase : DriverBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前采集设备
|
||||
/// </summary>
|
||||
public CollectDeviceRunTime CurDevice;
|
||||
|
||||
/// <summary>
|
||||
/// 返回是否支持读取
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract bool IsSupportRequest { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据转换器
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract IThingsGatewayBitConverter ThingsGatewayBitConverter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 结束通讯后执行的方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract Task AfterStopAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 开始通讯前执行的方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract Task BeforStartAsync(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 通道标识
|
||||
/// </summary>
|
||||
public virtual OperResult<string> GetChannelID()
|
||||
{
|
||||
var config = (CollectDriverPropertyBase)DriverPropertys;
|
||||
if (config.IsShareChannel)
|
||||
{
|
||||
switch (config.ShareChannel)
|
||||
{
|
||||
case ShareChannelEnum.SerialPort:
|
||||
return OperResult.CreateSuccessResult(config.PortName);
|
||||
case ShareChannelEnum.TcpClientEx:
|
||||
case ShareChannelEnum.UdpSession:
|
||||
var a = new IPHost($"{config.IP}:{config.Port}");
|
||||
return OperResult.CreateSuccessResult(config.ShareChannel.ToString() + a.ToString());
|
||||
}
|
||||
}
|
||||
return new("不支持共享通道");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 共享通道类型
|
||||
/// </summary>
|
||||
public virtual OperResult<object> GetShareChannel()
|
||||
{
|
||||
var config = (CollectDriverPropertyBase)DriverPropertys;
|
||||
if (config.IsShareChannel)
|
||||
{
|
||||
switch (config.ShareChannel)
|
||||
{
|
||||
case ShareChannelEnum.None:
|
||||
return new OperResult<object>("不支持共享链路");
|
||||
case ShareChannelEnum.SerialPort:
|
||||
var data = new SerialProperty()
|
||||
{
|
||||
PortName = config.PortName,
|
||||
BaudRate = config.BaudRate,
|
||||
DataBits = config.DataBits,
|
||||
Parity = config.Parity,
|
||||
StopBits = config.StopBits,
|
||||
};
|
||||
FoundataionConfig.SetValue(SerialConfigExtension.SerialProperty, data);
|
||||
var serialClient = new SerialClient();
|
||||
(serialClient).Setup(FoundataionConfig);
|
||||
return OperResult.CreateSuccessResult((object)serialClient);
|
||||
case ShareChannelEnum.TcpClientEx:
|
||||
FoundataionConfig.SetRemoteIPHost(new IPHost($"{config.IP}:{config.Port}"));
|
||||
var tcpClient = new TcpClientEx();
|
||||
(tcpClient).Setup(FoundataionConfig);
|
||||
return OperResult.CreateSuccessResult((object)tcpClient);
|
||||
case ShareChannelEnum.UdpSession:
|
||||
FoundataionConfig.SetRemoteIPHost(new IPHost($"{config.IP}:{config.Port}"));
|
||||
var udpSession = new UdpSession();
|
||||
return OperResult.CreateSuccessResult((object)udpSession);
|
||||
}
|
||||
|
||||
}
|
||||
return new OperResult<object>("不支持共享链路");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
public void Init(ILogger logger, CollectDeviceRunTime device, object client = null)
|
||||
{
|
||||
_logger = logger;
|
||||
IsLogOut = device.IsLogOut;
|
||||
CurDevice = device;
|
||||
Init(device, client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 共享链路需重新设置适配器时调用该方法
|
||||
/// </summary>
|
||||
public abstract void InitDataAdapter();
|
||||
|
||||
/// <summary>
|
||||
/// 连读打包,返回实际通讯包信息<see cref="DeviceVariableSourceRead"/>
|
||||
/// <br></br>每个驱动打包方法不一样,所以需要实现这个接口
|
||||
/// </summary>
|
||||
/// <param name="deviceVariables">设备下的全部通讯点位</param>
|
||||
/// <returns></returns>
|
||||
public abstract List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables);
|
||||
|
||||
/// <summary>
|
||||
/// 采集驱动读取
|
||||
/// </summary>
|
||||
public virtual async Task<OperResult<byte[]>> ReadSourceAsync(DeviceVariableSourceRead deviceVariableSourceRead, CancellationToken token)
|
||||
{
|
||||
if (IsSupportRequest)
|
||||
{
|
||||
OperResult<byte[]> read = await ReadAsync(deviceVariableSourceRead.Address, deviceVariableSourceRead.Length, token);
|
||||
if (read == null || !read.IsSuccess)
|
||||
{
|
||||
deviceVariableSourceRead.DeviceVariables.ForEach(it =>
|
||||
{
|
||||
var operResult = it.SetValue(null);
|
||||
if (!operResult.IsSuccess)
|
||||
{
|
||||
_logger.LogWarning("变量值更新失败:" + operResult.Message);
|
||||
}
|
||||
});
|
||||
return read;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReadWriteHelpers.DealWithReadResult(read, content =>
|
||||
{
|
||||
ReadWriteHelpers.PraseStructContent(content, deviceVariableSourceRead.DeviceVariables);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult<byte[]>("不支持默认读取方式");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入变量值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract Task<OperResult> WriteValueAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="device">设备</param>
|
||||
/// <param name="client">链路对象,如TCPClient</param>
|
||||
protected abstract void Init(CollectDeviceRunTime device, object client = null);
|
||||
|
||||
/// <summary>
|
||||
/// 底层日志输出
|
||||
/// </summary>
|
||||
protected override void Log_Out(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4)
|
||||
{
|
||||
if (arg1 >= TouchSocket.Core.LogLevel.Warning)
|
||||
{
|
||||
CurDevice.SetDeviceStatus(lastErrorMessage: arg3);
|
||||
}
|
||||
if (IsLogOut || arg1 >= TouchSocket.Core.LogLevel.Warning)
|
||||
{
|
||||
_logger.Log_Out(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回全部内容字节数组
|
||||
/// <br></br>
|
||||
/// 通常使用<see cref="IReadWrite.ReadAsync(string, int, CancellationToken)"/>可以直接返回正确信息
|
||||
/// </summary>
|
||||
protected abstract Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token);
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 插件基类,注意继承的插件的命名空间需要符合<see cref="ExportHelpers.PluginLeftName"/>前置名称
|
||||
/// </summary>
|
||||
public abstract class DriverBase : DisposableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="TouchSocket.Core.TouchSocketConfig"/>
|
||||
/// </summary>
|
||||
public TouchSocketConfig FoundataionConfig;
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
internal ILogger _logger;
|
||||
|
||||
/// <inheritdoc cref="DriverBase"/>
|
||||
public DriverBase()
|
||||
{
|
||||
FoundataionConfig = new TouchSocketConfig();
|
||||
LogMessage = new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
FoundataionConfig.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
FoundataionConfig.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
/// <summary>
|
||||
/// 调试UI Type,如果不存在,返回null
|
||||
/// </summary>
|
||||
public abstract Type DriverDebugUIType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前插件描述
|
||||
/// </summary>
|
||||
public DriverPlugin DriverPlugin { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件配置项 ,继承实现<see cref="DriverPropertyBase"/>后,返回继承类,如果不存在,返回null
|
||||
/// </summary>
|
||||
public abstract DriverPropertyBase DriverPropertys { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否输出日志
|
||||
/// </summary>
|
||||
public bool IsLogOut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 报文信息
|
||||
/// </summary>
|
||||
public ConcurrentLinkedList<string> Messages { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 底层日志,如果需要在Blazor界面中显示报文日志,需要输出字符串头部为<see cref="FoundationConst.LogMessageHeader"/>的日志
|
||||
/// </summary>
|
||||
protected internal LoggerGroup LogMessage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否连接成功,如果是上传设备,会直接影响到上传设备的运行状态,如果是采集设备并且不支持读取,需要自更新在线状态
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract bool IsConnected();
|
||||
|
||||
/// <summary>
|
||||
/// 设备报文
|
||||
/// </summary>
|
||||
internal void NewMessage(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4)
|
||||
{
|
||||
if (IsLogOut)
|
||||
{
|
||||
if (arg3.StartsWith(FoundationConst.LogMessageHeader))
|
||||
{
|
||||
Messages.Add(SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat() + " - " + arg3.Substring(0, Math.Min(arg3.Length, 200)));
|
||||
|
||||
if (Messages.Count > 2500)
|
||||
{
|
||||
Messages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 底层日志输出
|
||||
/// </summary>
|
||||
protected virtual void Log_Out(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4)
|
||||
{
|
||||
if (IsLogOut || arg1 >= TouchSocket.Core.LogLevel.Warning)
|
||||
{
|
||||
_logger.Log_Out(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
using Furion.FriendlyException;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
using ThingsGateway.Application.Extensions;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// 驱动插件服务
|
||||
/// </summary>
|
||||
public class PluginSingletonService : ISingleton
|
||||
{
|
||||
|
||||
private readonly ILogger<PluginSingletonService> _logger;
|
||||
/// <inheritdoc cref="PluginSingletonService"/>
|
||||
public PluginSingletonService(ILogger<PluginSingletonService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插件文件路径/插件程序集
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, Assembly> AssemblyDict { get; private set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 插件文件路径/插件域
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, AssemblyLoadContext> AssemblyLoadContextDict { get; private set; } = new();
|
||||
/// <summary>
|
||||
/// 插件ID/插件Type
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<long, Type> DriverPluginDict { get; private set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
public DriverBase GetDriver(DriverPlugin plugin)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
//先判断是否已经拥有插件模块
|
||||
if (DriverPluginDict.ContainsKey(plugin.Id))
|
||||
{
|
||||
var driver = (DriverBase)Activator.CreateInstance(DriverPluginDict[plugin.Id]);
|
||||
driver.DriverPlugin = plugin;
|
||||
return driver;
|
||||
}
|
||||
Assembly assembly = null;
|
||||
|
||||
_logger?.LogInformation($"添加插件文件:{plugin.FilePath}");
|
||||
|
||||
//根据路径获取dll文件
|
||||
|
||||
//主程序集路径
|
||||
var path = AppContext.BaseDirectory.CombinePathOS(plugin.FilePath);
|
||||
//全部程序集路径
|
||||
List<string> paths = new();
|
||||
Directory.GetFiles(Path.GetDirectoryName(path), "*.dll").ToList().
|
||||
ForEach(a => paths.Add(a.Replace("\\", "/")));
|
||||
|
||||
if (AssemblyDict.ContainsKey(plugin.FilePath))
|
||||
{
|
||||
assembly = AssemblyDict[plugin.FilePath];
|
||||
}
|
||||
else
|
||||
{
|
||||
//新建插件域,并注明不可卸载
|
||||
var assemblyLoadContext = new AssemblyLoadContext(plugin.Id.ToString(), false);
|
||||
//获取插件程序集
|
||||
assembly = GetAssembly(path, paths, assemblyLoadContext);
|
||||
//添加到全局对象
|
||||
AssemblyLoadContextDict.TryAdd(plugin.FilePath, assemblyLoadContext);
|
||||
AssemblyDict.TryAdd(plugin.FilePath, assembly);
|
||||
}
|
||||
|
||||
if (assembly != null)
|
||||
{
|
||||
//根据采集/上传类型获取实际插件类
|
||||
switch (plugin.DriverTypeEnum)
|
||||
{
|
||||
case DriverEnum.Collect:
|
||||
var driverType = assembly.GetTypes().Where(x => (typeof(CollectBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract).FirstOrDefault(it => it.Name == plugin.AssembleName);
|
||||
if (driverType != null)
|
||||
{
|
||||
return GetDriver(plugin, driverType);
|
||||
}
|
||||
break;
|
||||
case DriverEnum.Upload:
|
||||
var upLoadType = assembly.GetTypes().Where(x => (typeof(UpLoadBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract).FirstOrDefault(it => it.Name == plugin.AssembleName);
|
||||
if (upLoadType != null)
|
||||
{
|
||||
return GetDriver(plugin, upLoadType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Exception($"加载插件 {plugin.FilePath}-{plugin.AssembleName} 失败,{plugin.AssembleName}不存在");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"加载驱动插件 {path} 失败,文件不存在");
|
||||
}
|
||||
DriverBase GetDriver(DriverPlugin plugin, Type driverType)
|
||||
{
|
||||
var driver = (DriverBase)Activator.CreateInstance(driverType);
|
||||
_logger?.LogInformation($"加载插件 {plugin.FilePath}-{plugin.AssembleName} 成功");
|
||||
DriverPluginDict.TryAdd(plugin.Id, driverType);
|
||||
driver.DriverPlugin = plugin;
|
||||
return driver;
|
||||
}
|
||||
Assembly GetAssembly(string path, List<string> paths, AssemblyLoadContext assemblyLoadContext)
|
||||
{
|
||||
Assembly assembly = null;
|
||||
foreach (var item in paths)
|
||||
{
|
||||
using var fs = new FileStream(item, FileMode.Open);
|
||||
if (item == path)
|
||||
assembly = assemblyLoadContext.LoadFromStream(fs);
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
assemblyLoadContext.LoadFromStream(fs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"尝试加载附属程序集{item}失败,如果此程序集为非引用,比如非托管DllImport,可以忽略此警告。错误信息:{(ex.Message)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return assembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的属性值
|
||||
/// </summary>
|
||||
public List<DependencyProperty> GetDriverProperties(DriverBase driver)
|
||||
{
|
||||
var data = driver.DriverPropertys?.GetType().GetPropertiesWithCache().SelectMany(it =>
|
||||
new[] { new { property = it, devicePropertyAttribute = it.GetCustomAttribute<DevicePropertyAttribute>() } })
|
||||
.Where(x => x.devicePropertyAttribute != null).ToList()
|
||||
.SelectMany(it => new[]
|
||||
{
|
||||
new DependencyProperty(){
|
||||
PropertyName=it.property.Name,
|
||||
Description=it.devicePropertyAttribute.Description,
|
||||
Remark=it.devicePropertyAttribute.Remark,
|
||||
Value=it.property.GetValue(driver.DriverPropertys)?.ToString(),
|
||||
}
|
||||
});
|
||||
return data.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件的变量上传属性值
|
||||
/// </summary>
|
||||
public List<DependencyProperty> GetDriverVariableProperties(UpLoadBase driver)
|
||||
{
|
||||
var data = driver.VariablePropertys?.GetType().GetPropertiesWithCache()?.SelectMany(it =>
|
||||
new[] { new { property = it, devicePropertyAttribute = it.GetCustomAttribute<VariablePropertyAttribute>() } })
|
||||
?.Where(x => x.devicePropertyAttribute != null).ToList()
|
||||
?.SelectMany(it => new[]
|
||||
{
|
||||
new DependencyProperty(){
|
||||
PropertyName=it.property.Name,
|
||||
Description=it.devicePropertyAttribute.Description,
|
||||
Remark=it.devicePropertyAttribute.Remark,
|
||||
Value=it.property.GetValue(driver.VariablePropertys)?.ToString(),
|
||||
}
|
||||
});
|
||||
return data?.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件方法
|
||||
/// </summary>
|
||||
/// <param name="driver"></param>
|
||||
/// <returns></returns>
|
||||
public List<MethodInfo> GetMethod(DriverBase driver)
|
||||
{
|
||||
return driver.GetType().GetMethods().Where(
|
||||
x => x.GetCustomAttribute(typeof(DeviceMethodAttribute)) != null).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置插件的属性值
|
||||
/// </summary>
|
||||
public void SetDriverProperties(DriverBase driver, List<DependencyProperty> deviceProperties)
|
||||
{
|
||||
var pluginPropertys = driver.DriverPropertys?.GetType().GetPropertiesWithCache().Where(a => a.GetCustomAttribute(typeof(DevicePropertyAttribute)) != null)?.ToList();
|
||||
foreach (var propertyInfo in pluginPropertys ?? new())
|
||||
{
|
||||
var deviceProperty = deviceProperties.FirstOrDefault(x => x.PropertyName == propertyInfo.Name);
|
||||
if (deviceProperty == null) continue;
|
||||
var value = ReadWriteHelpers.ObjToTypeValue(propertyInfo, deviceProperty?.Value ?? "");
|
||||
propertyInfo.SetValue(driver.DriverPropertys, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试添加插件,返回插件表示类,方法完成后会完全卸载插件
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<DriverPlugin>> TestAddDriverAsync(DriverPluginAddInput plugin)
|
||||
{
|
||||
var assemblyLoadContext = new AssemblyLoadContext(YitIdHelper.NextId().ToString(), true);
|
||||
try
|
||||
{
|
||||
|
||||
var driverPlugins = new List<DriverPlugin>();
|
||||
var maxFileSize = 100 * 1024 * 1024;//最大100m
|
||||
//主程序集名称
|
||||
var mainFileName = Path.GetFileNameWithoutExtension(plugin.MainFile.Name);
|
||||
//插件文件夹相对路径
|
||||
var dir = "Plugins".CombinePathOS(mainFileName);
|
||||
//插件文件夹绝对路径
|
||||
var fullDir = AppContext.BaseDirectory.CombinePathOS(dir);
|
||||
//主程序集相对路径
|
||||
var path = dir.CombinePathOS(plugin.MainFile.Name);
|
||||
//主程序集绝对路径
|
||||
var fullPath = fullDir.CombinePathOS(plugin.MainFile.Name);
|
||||
|
||||
//主程序集相对路径
|
||||
//获取文件流
|
||||
using var stream = plugin.MainFile.OpenReadStream(maxFileSize);
|
||||
Directory.CreateDirectory(fullDir);//创建插件文件夹
|
||||
using FileStream fs = new(fullPath, FileMode.Create);
|
||||
await stream.CopyToAsync(fs);
|
||||
fs.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
//获取主程序集
|
||||
var assembly = assemblyLoadContext.LoadFromStream(fs);
|
||||
foreach (var item in plugin.OtherFiles)
|
||||
{
|
||||
using var otherStream = item.OpenReadStream(maxFileSize);
|
||||
using FileStream fs1 = new(fullDir.CombinePathOS(item.Name), FileMode.Create);
|
||||
await otherStream.CopyToAsync(fs1);
|
||||
fs1.Seek(0, SeekOrigin.Begin);
|
||||
try
|
||||
{
|
||||
assemblyLoadContext.LoadFromStream(fs1);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"尝试加载附属程序集{item}失败,如果此程序集为非引用,比如非托管DllImport,可以忽略此警告。错误信息:{(ex.Message)}");
|
||||
}
|
||||
}
|
||||
if (assembly != null)
|
||||
{
|
||||
//获取插件的相关信息
|
||||
var collectBase = assembly.GetTypes().Where(x => (typeof(CollectBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract).ToList();
|
||||
for (int i = 0; i < collectBase.Count; i++)
|
||||
{
|
||||
var item = collectBase[i];
|
||||
driverPlugins.Add(new DriverPlugin()
|
||||
{
|
||||
AssembleName = item.Name,
|
||||
DriverTypeEnum = DriverEnum.Collect,
|
||||
FilePath = path,
|
||||
FileName = mainFileName,
|
||||
});
|
||||
}
|
||||
collectBase.ForEach(a => a = null);
|
||||
collectBase.Clear();
|
||||
collectBase = null;
|
||||
var upLoadBase = assembly.GetTypes().Where(x => (typeof(UpLoadBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract).ToList();
|
||||
for (int i = 0; i < upLoadBase.Count; i++)
|
||||
{
|
||||
var item = upLoadBase[i];
|
||||
driverPlugins.Add(new DriverPlugin()
|
||||
{
|
||||
AssembleName = item.Name,
|
||||
DriverTypeEnum = DriverEnum.Upload,
|
||||
FilePath = path,
|
||||
FileName = mainFileName,
|
||||
});
|
||||
}
|
||||
upLoadBase.ForEach(a => a = null);
|
||||
upLoadBase.Clear();
|
||||
upLoadBase = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Oops.Bah("加载驱动文件失败");
|
||||
}
|
||||
if (driverPlugins.Count == 0)
|
||||
{
|
||||
throw Oops.Bah("找不到对应的驱动");
|
||||
}
|
||||
assembly = null;
|
||||
return driverPlugins;
|
||||
}
|
||||
finally
|
||||
{
|
||||
assemblyLoadContext.Unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#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 Microsoft.Extensions.Logging;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上传插件
|
||||
/// <para></para>
|
||||
/// 约定:
|
||||
/// <para></para>
|
||||
/// 如果设备属性需要密码输入,属性名称中需包含Password字符串
|
||||
/// <para></para>
|
||||
/// 如果设备属性需要大文本输入,属性名称中需包含BigText字符串
|
||||
/// <br></br>
|
||||
/// 因为自定义上传插件需求比较大,这里着重解释代码运行原理
|
||||
/// 继承<see cref="UpLoadBase"/>后,可以看到需要实现各类虚方法/属性<br></br>
|
||||
/// <see cref="UploadVariables"/> <br></br>
|
||||
/// <see cref="VariablePropertys"/><br></br>
|
||||
/// <see cref="DriverBase.DriverPropertys"/><br></br>
|
||||
/// <see cref="BeforStartAsync"/><br></br>
|
||||
/// <see cref="ExecuteAsync"/><br></br>
|
||||
/// <see cref="DriverBase.IsConnected"/><br></br>
|
||||
/// <see cref="Init(UploadDeviceRunTime)"/><br></br>
|
||||
/// 含义可看注释,下面看看网关上传插件的生命周期<br></br>
|
||||
/// 1、构造函数<see cref="UpLoadBase()"/> 传入参数服务工厂,在需要获取服务时使用<see cref="App.GetService{TService}(IServiceProvider)"/><br></br>
|
||||
/// 2、<see cref="Init(UploadDeviceRunTime)"/>初始化函数,传入上传设备参数,只执行一次,在这个方法内,一般会初始化一些必要的实例,比如new MqttClient,以及一些必要的实现属性,比如<see cref="UploadVariables"/><br></br>
|
||||
/// 3、<see cref="BeforStartAsync"/>开始前执行的方法,比如连接mqtt等,只执行一次<br></br>
|
||||
/// 4、<see cref="ExecuteAsync"/>核心执行的方法,需实现上传方法,在插件结束前会一直循环调用<br></br>
|
||||
/// 5、<see cref="DisposableObject.Dispose(bool)"/> 结束时调用的方法,实现资源释放方法<br></br>
|
||||
/// 网关的数据是如何传入到上传插件的,下面会以Mqtt上传为例<br></br>
|
||||
/// 1、如何获取采集变量值?在初始化函数中<see cref="Init(UploadDeviceRunTime)"/>获取全局设备/变量<br></br>
|
||||
/// 通过<see cref="App.GetService{TService}(IServiceProvider)"/>获取单例服务<see cref="GlobalDeviceData"/><br></br>
|
||||
/// 可以看到在这个单例服务中,已经拥有全部的采集设备与变量<br></br>
|
||||
/// 2、如何获取采集变量中的上传属性?UploadBase中封装了通用方法<see cref="GetPropertyValue(DeviceVariableRunTime, string)"/><br></br>
|
||||
/// 比如定义了变量属性Enable,只有设置为true的变量才会用作某逻辑,执行方法GetPropertyValue(tag,"Enable"),也可用硬编码传入propertyName参数<br></br>
|
||||
/// 3、如何定义自己的上传实体,第一步中获取获取单例服务<see cref="GlobalDeviceData"/>,在拥有全局变量下,可以使用<see cref="Mapster"/> 或者 手动赋值到DTO实体<br></br>
|
||||
/// 4、完整的参考可以查看MqttClient插件ThingsGateway\src\Plugins\ThingsGateway.Mqtt\ThingsGateway.Mqtt.csproj<br></br>
|
||||
/// </summary>
|
||||
public abstract class UpLoadBase : DriverBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前上传设备
|
||||
/// </summary>
|
||||
public UploadDeviceRunTime CurDevice { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回插件的上传变量,一般在<see cref="Init(UploadDeviceRunTime)"/>后初始化
|
||||
/// </summary>
|
||||
public abstract List<DeviceVariableRunTime> UploadVariables { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件配置项 ,继承实现<see cref="VariablePropertyBase"/>后,返回继承类,如果不存在,返回null
|
||||
/// </summary>
|
||||
public abstract VariablePropertyBase VariablePropertys { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 离线缓存
|
||||
/// </summary>
|
||||
protected CacheDb CacheDb { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结束通讯后执行的方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract Task AfterStopAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行的方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract Task BeforStartAsync(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 循环执行
|
||||
/// </summary>
|
||||
public abstract Task ExecuteAsync(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备的属性值
|
||||
/// </summary>
|
||||
public virtual string GetDevicePropertyValue(CollectDeviceRunTime collectDeviceRunTime, string propertyName)
|
||||
{
|
||||
|
||||
if (collectDeviceRunTime == null)
|
||||
return null;
|
||||
return collectDeviceRunTime.DevicePropertys.FirstOrDefault(a => a.PropertyName == propertyName).Value;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取变量的属性值
|
||||
/// </summary>
|
||||
public virtual string GetPropertyValue(DeviceVariableRunTime variableRunTime, string propertyName)
|
||||
{
|
||||
if (variableRunTime == null)
|
||||
return null;
|
||||
if (variableRunTime.VariablePropertys.ContainsKey(CurDevice.Id))
|
||||
{
|
||||
var data = variableRunTime.VariablePropertys[CurDevice.Id].FirstOrDefault(a =>
|
||||
a.PropertyName == propertyName);
|
||||
if (data != null)
|
||||
{
|
||||
return data.Value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
public void Init(ILogger logger, UploadDeviceRunTime device)
|
||||
{
|
||||
_logger = logger;
|
||||
IsLogOut = device.IsLogOut;
|
||||
CurDevice = device;
|
||||
CacheDb = new(CurDevice.Id.ToString());
|
||||
Init(device);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="device">设备</param>
|
||||
protected abstract void Init(UploadDeviceRunTime device);
|
||||
|
||||
/// <summary>
|
||||
/// 底层日志输出
|
||||
/// </summary>
|
||||
protected override void Log_Out(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4)
|
||||
{
|
||||
if (arg1 >= TouchSocket.Core.LogLevel.Warning)
|
||||
{
|
||||
CurDevice.SetDeviceStatus(lastErrorMessage: arg3);
|
||||
}
|
||||
if (IsLogOut || arg1 >= TouchSocket.Core.LogLevel.Warning)
|
||||
{
|
||||
_logger.Log_Out(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// 变量写入/执行变量附带方法,单例服务
|
||||
/// </summary>
|
||||
public class RpcSingletonService : ISingleton
|
||||
{
|
||||
/// <summary>
|
||||
/// 写入变量说明
|
||||
/// </summary>
|
||||
public const string WriteVariable = "写入变量";
|
||||
private readonly ILogger<RpcSingletonService> _logger;
|
||||
private readonly CollectDeviceWorker _collectDeviceHostService;
|
||||
private readonly GlobalDeviceData _globalDeviceData;
|
||||
private readonly ConcurrentQueue<RpcLog> _logQueues = new();
|
||||
/// <inheritdoc cref="RpcSingletonService"/>
|
||||
public RpcSingletonService(ILogger<RpcSingletonService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_globalDeviceData = ServiceHelper.Services.GetService<GlobalDeviceData>();
|
||||
_collectDeviceHostService = ServiceHelper.GetBackgroundService<CollectDeviceWorker>();
|
||||
Task.Factory.StartNew(RpcLogInsertAsync, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
/// <summary>
|
||||
/// 反向RPC入口方法
|
||||
/// </summary>
|
||||
/// <param name="sourceDes">触发该方法的源说明</param>
|
||||
/// <param name="item">指定键为变量名称,值为附带方法参数或写入值</param>
|
||||
/// <param name="isBlazor">如果是true,不检查<see cref="MemoryVariable.RpcWriteEnable"/>字段</param>
|
||||
/// <param name="token"><see cref="CancellationToken"/> 取消源</param>
|
||||
/// <returns></returns>
|
||||
public async Task<OperResult> InvokeDeviceMethodAsync(string sourceDes, KeyValuePair<string, string> item, bool isBlazor = false, CancellationToken token = default)
|
||||
{
|
||||
//避免并发过高,这里延时10ms
|
||||
await Task.Delay(10, token);
|
||||
OperResult data = new();
|
||||
var tag = _globalDeviceData.AllVariables.FirstOrDefault(it => it.Name == item.Key);
|
||||
if (tag == null) return new OperResult("不存在变量:" + item.Key);
|
||||
if (tag.ProtectTypeEnum == ProtectTypeEnum.ReadOnly) return new OperResult("只读变量");
|
||||
if (!tag.RpcWriteEnable && !isBlazor) return new OperResult("不允许远程写入");
|
||||
|
||||
if (tag.IsMemoryVariable == true)
|
||||
{
|
||||
return tag.SetValue(item.Value);
|
||||
}
|
||||
var dev = _collectDeviceHostService.CollectDeviceCores.FirstOrDefault(it => it.Device.Id == tag.DeviceId);
|
||||
if (dev == null) return new OperResult("系统错误,不存在对应采集设备,请稍候重试");
|
||||
if (dev.Device.DeviceStatus == DeviceStatusEnum.OffLine) return new OperResult("设备已离线");
|
||||
if (dev.Device.DeviceStatus == DeviceStatusEnum.Pause) return new OperResult("设备已暂停");
|
||||
if (string.IsNullOrEmpty(tag.OtherMethod))
|
||||
{
|
||||
//写入变量
|
||||
JToken tagValue;
|
||||
try
|
||||
{
|
||||
tagValue = JToken.Parse(item.Value);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
tagValue = JToken.Parse("\"" + item.Value + "\"");
|
||||
}
|
||||
|
||||
data = await dev.InVokeWriteAsync(tag, tagValue, token);
|
||||
_logQueues.Enqueue(
|
||||
new RpcLog()
|
||||
{
|
||||
LogTime = SysDateTimeExtensions.CurrentDateTime,
|
||||
OperateMessage = data.Exception,
|
||||
IsSuccess = data.IsSuccess,
|
||||
OperateMethod = WriteVariable,
|
||||
OperateObject = tag.Name,
|
||||
OperateSource = sourceDes,
|
||||
ParamJson = item.Value,
|
||||
ResultJson = data.Message
|
||||
}
|
||||
);
|
||||
if (!data.IsSuccess)
|
||||
{
|
||||
_logger.LogWarning($"写入变量[{tag.Name}]失败:{data.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//执行变量附带的方法
|
||||
var method = dev.DeviceVariableMethodSources.FirstOrDefault(it => it.DeviceVariable == tag);
|
||||
try
|
||||
{
|
||||
data = await dev.InvokeMethodAsync(method, false, item.Value, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
data = new OperResult<string>(ex);
|
||||
}
|
||||
_logQueues.Enqueue(
|
||||
new RpcLog()
|
||||
{
|
||||
LogTime = SysDateTimeExtensions.CurrentDateTime,
|
||||
OperateMessage = data.Exception,
|
||||
IsSuccess = data.IsSuccess,
|
||||
OperateMethod = tag.OtherMethod,
|
||||
OperateObject = tag.Name,
|
||||
OperateSource = sourceDes,
|
||||
ParamJson = item.Value?.ToString(),
|
||||
ResultJson = data.Message
|
||||
}
|
||||
);
|
||||
if (!data.IsSuccess)
|
||||
{
|
||||
_logger.LogWarning($"执行变量[{tag.Name}]方法[{tag.OtherMethod}]失败:{data.Message}");
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private async Task RpcLogInsertAsync()
|
||||
{
|
||||
var db = DbContext.Db.CopyNew();
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = _logQueues.ToListWithDequeue();
|
||||
db.InsertableWithAttr(data).ExecuteCommand();//入库
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
await Task.Delay(3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#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 ThingsGateway.Admin.Application;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 系统配置种子数据
|
||||
/// </summary>
|
||||
public class DriverPluginSeedData : ISqlSugarEntitySeedData<DriverPlugin>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<DriverPlugin> SeedData()
|
||||
{
|
||||
return SeedDataUtil.GetSeedData<DriverPlugin>("driver_plugin.json");
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
{
|
||||
"RECORDS": [
|
||||
{
|
||||
"Id": 319003388334342,
|
||||
"FileName": "ThingsGateway.Modbus",
|
||||
"AssembleName": "ModbusRtu",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Modbus/ThingsGateway.Modbus.dll",
|
||||
"CreateTime": "2023/2/23 15:18:52",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": "2023/3/2 12:40:28",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": 212725263002001,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 389003388313861,
|
||||
"FileName": "ThingsGateway.Modbus",
|
||||
"AssembleName": "ModbusRtuOverTcp",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Modbus/ThingsGateway.Modbus.dll",
|
||||
"CreateTime": "2023/2/23 15:18:52",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": "2023/3/2 12:40:28",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": 212725263002001,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 389003388334341,
|
||||
"FileName": "ThingsGateway.Modbus",
|
||||
"AssembleName": "ModbusRtuOverUdp",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Modbus/ThingsGateway.Modbus.dll",
|
||||
"CreateTime": "2023/2/23 15:18:52",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": "2023/3/2 12:40:28",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": 212725263002001,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 389003388334342,
|
||||
"FileName": "ThingsGateway.Modbus",
|
||||
"AssembleName": "ModbusTcp",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Modbus/ThingsGateway.Modbus.dll",
|
||||
"CreateTime": "2023/2/23 15:18:52",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": "2023/3/2 12:40:28",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": 212725263002001,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 389003388334343,
|
||||
"FileName": "ThingsGateway.Modbus",
|
||||
"AssembleName": "ModbusUdp",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Modbus/ThingsGateway.Modbus.dll",
|
||||
"CreateTime": "2023/2/23 15:18:52",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": "2023/3/2 12:40:28",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": 212725263002001,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 391441718112517,
|
||||
"FileName": "ThingsGateway.Modbus",
|
||||
"AssembleName": "ModbusServer",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Modbus/ThingsGateway.Modbus.dll",
|
||||
"CreateTime": "2023/3/2 12:40:28",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087020826885,
|
||||
"FileName": "ThingsGateway.OPCUA",
|
||||
"AssembleName": "OPCUAClient",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.OPCUA/ThingsGateway.OPCUA.dll",
|
||||
"CreateTime": "2023/8/6 18:21:47",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087020826886,
|
||||
"FileName": "ThingsGateway.OPCUA",
|
||||
"AssembleName": "OPCUAServer",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.OPCUA/ThingsGateway.OPCUA.dll",
|
||||
"CreateTime": "2023/8/6 18:21:47",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087111926021,
|
||||
"FileName": "ThingsGateway.OPCDA",
|
||||
"AssembleName": "OPCDAClient",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.OPCDA/ThingsGateway.OPCDA.dll",
|
||||
"CreateTime": "2023/8/6 18:22:09",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087153508613,
|
||||
"FileName": "ThingsGateway.RabbitMQ",
|
||||
"AssembleName": "RabbitMQClient",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.RabbitMQ/ThingsGateway.RabbitMQ.dll",
|
||||
"CreateTime": "2023/8/6 18:22:19",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087210762501,
|
||||
"FileName": "ThingsGateway.Siemens",
|
||||
"AssembleName": "S7_1200",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Siemens/ThingsGateway.Siemens.dll",
|
||||
"CreateTime": "2023/8/6 18:22:33",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087210762502,
|
||||
"FileName": "ThingsGateway.Siemens",
|
||||
"AssembleName": "S7_1500",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Siemens/ThingsGateway.Siemens.dll",
|
||||
"CreateTime": "2023/8/6 18:22:33",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087210762503,
|
||||
"FileName": "ThingsGateway.Siemens",
|
||||
"AssembleName": "S7_200",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Siemens/ThingsGateway.Siemens.dll",
|
||||
"CreateTime": "2023/8/6 18:22:33",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087210762504,
|
||||
"FileName": "ThingsGateway.Siemens",
|
||||
"AssembleName": "S7_200SMART",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Siemens/ThingsGateway.Siemens.dll",
|
||||
"CreateTime": "2023/8/6 18:22:33",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087210762505,
|
||||
"FileName": "ThingsGateway.Siemens",
|
||||
"AssembleName": "S7_300",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Siemens/ThingsGateway.Siemens.dll",
|
||||
"CreateTime": "2023/8/6 18:22:33",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087210762506,
|
||||
"FileName": "ThingsGateway.Siemens",
|
||||
"AssembleName": "S7_400",
|
||||
"DriverTypeEnum": "Collect",
|
||||
"FilePath": "Plugins/ThingsGateway.Siemens/ThingsGateway.Siemens.dll",
|
||||
"CreateTime": "2023/8/6 18:22:33",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087273050373,
|
||||
"FileName": "ThingsGateway.Mqtt",
|
||||
"AssembleName": "IotSharpClient",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Mqtt/ThingsGateway.Mqtt.dll",
|
||||
"CreateTime": "2023/8/6 18:22:48",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087273050374,
|
||||
"FileName": "ThingsGateway.Mqtt",
|
||||
"AssembleName": "MqttClient",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Mqtt/ThingsGateway.Mqtt.dll",
|
||||
"CreateTime": "2023/8/6 18:22:48",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087273050375,
|
||||
"FileName": "ThingsGateway.Mqtt",
|
||||
"AssembleName": "MqttServer",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Mqtt/ThingsGateway.Mqtt.dll",
|
||||
"CreateTime": "2023/8/6 18:22:48",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087330930949,
|
||||
"FileName": "ThingsGateway.Kafka",
|
||||
"AssembleName": "KafkaProducer",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Kafka/ThingsGateway.Kafka.dll",
|
||||
"CreateTime": "2023/8/6 18:23:02",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"RECORDS": [
|
||||
{
|
||||
"Id": "230000000000",
|
||||
"Category": "THINGSGATEWAY_ALARMCONFIG_BASE",
|
||||
"ConfigKey": "CONFIG_ALARM_ENABLE",
|
||||
"ConfigValue": "False",
|
||||
"Remark": "转储使能",
|
||||
"SortCode": "1",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:50:38.341",
|
||||
"UpdateUser": "admin",
|
||||
"UpdateUserId": "201725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "230000000001",
|
||||
"Category": "THINGSGATEWAY_ALARMCONFIG_BASE",
|
||||
"ConfigKey": "CONFIG_ALARM_DBTYPE",
|
||||
"ConfigValue": "SqlServer",
|
||||
"Remark": "数据库类型",
|
||||
"SortCode": "2",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:50:38.343",
|
||||
"UpdateUser": "admin",
|
||||
"UpdateUserId": "201725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "230000000002",
|
||||
"Category": "THINGSGATEWAY_ALARMCONFIG_BASE",
|
||||
"ConfigKey": "CONFIG_ALARM_CONNSTR",
|
||||
"ConfigValue": "server=.;uid=sa;pwd=111111;database=test",
|
||||
"Remark": "连接字符串",
|
||||
"SortCode": "2",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:50:38.343",
|
||||
"UpdateUser": "admin",
|
||||
"UpdateUserId": "201725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "240000000000",
|
||||
"Category": "THINGSGATEWAY_HISCONFIG_BASE",
|
||||
"ConfigKey": "CONFIG_HIS_ENABLE",
|
||||
"ConfigValue": "False",
|
||||
"Remark": "转储使能",
|
||||
"SortCode": "1",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-02-26 17:38:37.741",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "240000000001",
|
||||
"Category": "THINGSGATEWAY_HISCONFIG_BASE",
|
||||
"ConfigKey": "CONFIG_HIS_DBTYPE",
|
||||
"ConfigValue": "QuestDB",
|
||||
"Remark": "数据库类型",
|
||||
"SortCode": "2",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-02-26 17:38:37.744",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "240000000002",
|
||||
"Category": "THINGSGATEWAY_HISCONFIG_BASE",
|
||||
"ConfigKey": "CONFIG_HIS_CONNSTR",
|
||||
"ConfigValue": "host=localhost;port=8812;username=admin;password=quest;database=qdb;ServerCompatibilityMode=NoTypeLoading;",
|
||||
"Remark": "连接字符串",
|
||||
"SortCode": "2",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-02-26 17:38:37.745",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,492 +0,0 @@
|
||||
{
|
||||
"RECORDS": [
|
||||
{
|
||||
"Id": "200001",
|
||||
"Title": "网关配置",
|
||||
"Icon": "mdi-cog",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "0",
|
||||
"SortCode": "2",
|
||||
"TargetType": "None",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:43:27.6423987",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001001",
|
||||
"Title": "采集设备",
|
||||
"Icon": "mdi-database-cog-outline",
|
||||
"Name": "gatewayCollectDevice",
|
||||
"Component": "/gatewayconfig/collectdevice",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "200001",
|
||||
"SortCode": "2",
|
||||
"TargetType": "SELF",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:58:03.8016224",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001002",
|
||||
"Title": "插件管理",
|
||||
"Icon": "mdi-database-cog-outline",
|
||||
"Name": "gatewayplugin",
|
||||
"Component": "/gatewayconfig/plugin",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "200001",
|
||||
"SortCode": "1",
|
||||
"TargetType": "SELF",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:58:00.8434735",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001011",
|
||||
"Title": "驱动调试",
|
||||
"Icon": "mdi-database-cog-outline",
|
||||
"Name": "gatewaydriverdebug",
|
||||
"Component": "/gatewayconfig/driverdebug",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "200001",
|
||||
"SortCode": "1",
|
||||
"TargetType": "SELF",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:58:09.8844904",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001003",
|
||||
"Title": "变量管理",
|
||||
"Icon": "mdi-database-cog-outline",
|
||||
"Name": "gatewayvariable",
|
||||
"Component": "/gatewayconfig/devicevariable",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "200001",
|
||||
"SortCode": "3",
|
||||
"TargetType": "SELF",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:58:09.8844904",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001903",
|
||||
"Title": "中间变量",
|
||||
"Icon": "mdi-database-sync-outline",
|
||||
"Component": "/gatewayconfig/memoryvariable",
|
||||
"Category": "MENU",
|
||||
"ParentId": "200001",
|
||||
"SortCode": "3",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 01:02:12.089",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:01:49.2309339",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001004",
|
||||
"Title": "运行状态",
|
||||
"Icon": "mdi-transit-connection-horizontal",
|
||||
"Component": "/gatewayruntime/devicestatus",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "389850957095173",
|
||||
"SortCode": "1",
|
||||
"TargetType": "SELF",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 21:23:45.8478018",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001005",
|
||||
"Title": "实时数据",
|
||||
"Icon": "mdi-database-refresh-outline",
|
||||
"Component": "/gatewayruntime/devicevariable",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "389850957095173",
|
||||
"SortCode": "2",
|
||||
"TargetType": "SELF",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:59:12.0176321",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "389850957095173",
|
||||
"Title": "网关状态",
|
||||
"Icon": "mdi-transit-connection-variant",
|
||||
"Category": "MENU",
|
||||
"ParentId": "0",
|
||||
"SortCode": "3",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-02-26 00:47:38.342",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:55:58.2367985",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "389854423286021",
|
||||
"Title": "硬件信息",
|
||||
"Icon": "mdi-memory",
|
||||
"Component": "/gatewayruntime/hardwareinfo",
|
||||
"Category": "MENU",
|
||||
"ParentId": "389850957095173",
|
||||
"SortCode": "4",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 01:01:44.580",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 21:24:02.3314076",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "389854535966981",
|
||||
"Title": "历史数据",
|
||||
"Icon": "mdi-database-sync-outline",
|
||||
"Component": "/gatewayruntime/historyvalue",
|
||||
"Category": "MENU",
|
||||
"ParentId": "389850957095173",
|
||||
"SortCode": "5",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 01:02:12.089",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:01:49.2309339",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"Id": "389854579716357",
|
||||
"Title": "实时报警",
|
||||
"Icon": "mdi-alarm-light-outline",
|
||||
"Component": "/gatewayruntime/realalarm",
|
||||
"Category": "MENU",
|
||||
"ParentId": "389850957095173",
|
||||
"SortCode": "3",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 01:02:22.771",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:59:32.6305265",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "389854617587973",
|
||||
"Title": "历史报警",
|
||||
"Icon": "mdi-database-sync-outline",
|
||||
"Component": "/gatewayruntime/hisalarm",
|
||||
"Category": "MENU",
|
||||
"ParentId": "389850957095173",
|
||||
"SortCode": "6",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 01:02:32.016",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:01:52.9110511",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "390053150736645",
|
||||
"Title": "其他配置",
|
||||
"Icon": "mdi-database-cog-outline",
|
||||
"Component": "/gatewayconfig/config",
|
||||
"Category": "MENU",
|
||||
"ParentId": "200001",
|
||||
"SortCode": "4",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 14:30:22.022",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:50:26.0961596",
|
||||
"UpdateUser": "admin",
|
||||
"UpdateUserId": "201725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "390107241025797",
|
||||
"Title": "网关日志",
|
||||
"Icon": "mdi-database-search-outline",
|
||||
"Category": "MENU",
|
||||
"ParentId": "0",
|
||||
"SortCode": "3",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-02-26 18:10:27.657",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:56:30.9492488",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "390107473895685",
|
||||
"Title": "Rpc日志",
|
||||
"Icon": "mdi-database-search-outline",
|
||||
"Component": "/gatewaylog/rpclog",
|
||||
"Category": "MENU",
|
||||
"ParentId": "390107241025797",
|
||||
"SortCode": "1",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 18:11:24.513",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:02:10.321289",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "390107521245445",
|
||||
"Title": "后台日志",
|
||||
"Icon": "mdi-database-search-outline",
|
||||
"Component": "/gatewaylog/backendlog",
|
||||
"Category": "MENU",
|
||||
"ParentId": "390107241025797",
|
||||
"SortCode": "1",
|
||||
"TargetType": "SELF",
|
||||
"CreateTime": "2023-02-26 18:11:36.074",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 18:02:13.1842573",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "200001101",
|
||||
"Title": "上传设备",
|
||||
"Icon": "mdi-database-cog-outline",
|
||||
"Name": "gatewayUploadDevice",
|
||||
"Component": "/gatewayconfig/uploaddevice",
|
||||
"Category": "MENU",
|
||||
"Code": "system",
|
||||
"ParentId": "200001",
|
||||
"SortCode": "2",
|
||||
"TargetType": "SELF",
|
||||
"IsDelete": false,
|
||||
"UpdateTime": "2023-03-03 17:58:06.9967903",
|
||||
"UpdateUser": "superAdmin",
|
||||
"UpdateUserId": "212725263002001"
|
||||
},
|
||||
{
|
||||
"Id": "391876432142597",
|
||||
"Title": "清空",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayrpclogclear",
|
||||
"ParentId": "390107473895685",
|
||||
"SortCode": "1",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:09:19.0789355",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391876531253509",
|
||||
"Title": "清空",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewaybackendlogclear",
|
||||
"ParentId": "390107521245445",
|
||||
"SortCode": "2",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:09:43.2483009",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391876932448517",
|
||||
"Title": "采集设备暂停",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewaydevicepause",
|
||||
"ParentId": "200001004",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:11:21.2313178",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391876995248389",
|
||||
"Title": "设备重启",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewaydevicerestart",
|
||||
"ParentId": "200001004",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:11:36.5638931",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877277679877",
|
||||
"Title": "添加",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewaypluginadd",
|
||||
"ParentId": "200001002",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:12:45.5166707",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877356581125",
|
||||
"Title": "添加",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewaycollectdeviceadd",
|
||||
"ParentId": "200001001",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:13:04.7786123",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877402190085",
|
||||
"Title": "修改",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewaycollectdeviceedit",
|
||||
"ParentId": "200001001",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:13:15.9140326",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877432721669",
|
||||
"Title": "删除",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewaycollectdevicedelete",
|
||||
"ParentId": "200001001",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:13:23.3685636",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877521359109",
|
||||
"Title": "添加",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayvariableadd",
|
||||
"ParentId": "200001003",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:13:45.0088195",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877554565381",
|
||||
"Title": "修改",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayvariableedit",
|
||||
"ParentId": "200001003",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:13:53.1151901",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877580439813",
|
||||
"Title": "删除",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayvariabledelete",
|
||||
"ParentId": "200001003",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:13:59.4322888",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877764051205",
|
||||
"Title": "添加",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayuploaddeviceadd",
|
||||
"ParentId": "200001101",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:14:44.259772",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877800583429",
|
||||
"Title": "修改",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayuploaddeviceedit",
|
||||
"ParentId": "200001101",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:14:53.178319",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391877824180485",
|
||||
"Title": "删除",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayuploaddevicedelete",
|
||||
"ParentId": "200001101",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:14:58.9389864",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391878193139973",
|
||||
"Title": "报警配置保存",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayalarmconfig",
|
||||
"ParentId": "390053150736645",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:16:29.0170337",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
},
|
||||
{
|
||||
"Id": "391878242271493",
|
||||
"Title": "历史配置保存",
|
||||
"Category": "BUTTON",
|
||||
"Code": "gatewayhisconfig",
|
||||
"ParentId": "390053150736645",
|
||||
"TargetType": "None",
|
||||
"CreateTime": "2023-03-03 18:16:41.0119884",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": "212725263002001",
|
||||
"IsDelete": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"RECORDS": [
|
||||
{
|
||||
"Id": "257755263002001",
|
||||
"Account": "admin",
|
||||
"Password": "7DA385A25A98388E",
|
||||
"UserEnable": "true",
|
||||
"SortCode": "1",
|
||||
"IsDelete": "false",
|
||||
"PermissionCodeList": [ "/openApi/collectInfo/collectDeviceList", "/openApi/collectInfo/collectVariableList", "/openApi/collectInfo/realAlarmList", "/openApi/rpc/writeDeviceMethod", "/openApi/rpc/writeVariables", "/openApi/rpc/configDeviceThread", "/openApi/rpc/upDeviceThread" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#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 ThingsGateway.Admin.Application;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 资源表种子数据
|
||||
/// </summary>
|
||||
public class SysResourceSeedData : ISqlSugarEntitySeedData<SysResource>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<SysResource> SeedData()
|
||||
{
|
||||
return SeedDataUtil.GetSeedData<SysResource>("gateway_resource.json");
|
||||
}
|
||||
}
|
||||
@@ -1,523 +0,0 @@
|
||||
#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 Furion.DependencyInjection;
|
||||
using Furion.FriendlyException;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using MiniExcelLibs;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Dynamic;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Application.Extensions;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <inheritdoc cref="ICollectDeviceService"/>
|
||||
[Injection(Proxy = typeof(OperDispatchProxy))]
|
||||
public class CollectDeviceService : DbRepository<CollectDevice>, ICollectDeviceService
|
||||
{
|
||||
private readonly IDriverPluginService _driverPluginService;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
/// <inheritdoc cref="ICollectDeviceService"/>
|
||||
public CollectDeviceService(
|
||||
IDriverPluginService driverPluginService,
|
||||
IFileService fileService
|
||||
)
|
||||
{
|
||||
_fileService = fileService;
|
||||
|
||||
_driverPluginService = driverPluginService;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("添加采集设备")]
|
||||
public async Task AddAsync(CollectDevice input)
|
||||
{
|
||||
var account_Id = GetIdByName(input.Name);
|
||||
if (account_Id > 0)
|
||||
throw Oops.Bah($"存在重复的名称:{input.Name}");
|
||||
await InsertAsync(input);//添加数据
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_CollectDevice);//cache删除
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("复制采集设备")]
|
||||
public async Task CopyDevAsync(IEnumerable<CollectDevice> input)
|
||||
{
|
||||
var newDevs = input.Adapt<List<CollectDevice>>();
|
||||
newDevs.ForEach(a =>
|
||||
{
|
||||
a.Id = YitIdHelper.NextId();
|
||||
a.Name = $"Copy-{a.Name}-{a.Id}";
|
||||
});
|
||||
|
||||
var result = await InsertRangeAsync(newDevs);//添加数据
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_CollectDevice);//cache删除
|
||||
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("复制采集设备与变量")]
|
||||
public async Task CopyDevAndVarAsync(IEnumerable<CollectDevice> input)
|
||||
{
|
||||
var variableService = App.GetService<IVariableService>();
|
||||
List<DeviceVariable> variables = new();
|
||||
var newDevs = input.Adapt<List<CollectDevice>>();
|
||||
foreach (var item in newDevs)
|
||||
{
|
||||
var newId = YitIdHelper.NextId();
|
||||
var deviceVariables = await Context.Queryable<DeviceVariable>().Where(a => a.DeviceId == item.Id).ToListAsync();
|
||||
deviceVariables.ForEach(b =>
|
||||
{
|
||||
b.Id = YitIdHelper.NextId();
|
||||
b.DeviceId = newId;
|
||||
b.Name = $"Copy-{b.Name}-{b.Id}";
|
||||
});
|
||||
variables.AddRange(deviceVariables);
|
||||
item.Id = newId;
|
||||
item.Name = $"Copy-{item.Name}-{newId}";
|
||||
}
|
||||
|
||||
var result = await itenant.UseTranAsync(async () =>
|
||||
{
|
||||
await InsertRangeAsync(newDevs);//添加数据
|
||||
await Context.Insertable(variables).ExecuteCommandAsync();//添加数据
|
||||
});
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_CollectDevice);//cache删除
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Oops.Oh(result.ErrorMessage);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public long? GetIdByName(string name)
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
return data.FirstOrDefault(it => it.Name == name)?.Id;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public string GetNameById(long id)
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
return data.FirstOrDefault(it => it.Id == id)?.Name;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public List<DeviceTree> GetTree()
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
var trees = data.GetTree();
|
||||
return trees;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("删除采集设备")]
|
||||
public async Task DeleteAsync(params long[] input)
|
||||
{
|
||||
//获取所有ID
|
||||
if (input.Length > 0)
|
||||
{
|
||||
var result = await DeleteByIdsAsync(input.Cast<object>().ToArray());
|
||||
var variableService = App.GetService<IVariableService>();
|
||||
await Context.Deleteable<DeviceVariable>(it => input.Contains(it.DeviceId)).ExecuteCommandAsync();
|
||||
variableService.DeleteVariableFromCache();
|
||||
if (result)
|
||||
{
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_CollectDevice);//cache删除
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("编辑采集设备")]
|
||||
public async Task EditAsync(CollectDeviceEditInput input)
|
||||
{
|
||||
var account_Id = GetIdByName(input.Name);
|
||||
if (account_Id > 0 && account_Id != input.Id)
|
||||
throw Oops.Bah($"存在重复的名称:{input.Name}");
|
||||
|
||||
if (await Context.Updateable(input.Adapt<CollectDevice>()).ExecuteCommandAsync() > 0)//修改数据
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_CollectDevice);//cache删除
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<SqlSugarPagedList<CollectDevice>> PageAsync(CollectDevicePageInput input)
|
||||
{
|
||||
var query = GetPage(input);
|
||||
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
|
||||
return pageInfo;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
private ISugarQueryable<CollectDevice> GetPage(CollectDevicePageInput input)
|
||||
{
|
||||
long? pluginid = 0;
|
||||
if (!string.IsNullOrEmpty(input.PluginName))
|
||||
{
|
||||
pluginid = _driverPluginService.GetCacheList(false).FirstOrDefault(it => it.AssembleName.Contains(input.PluginName))?.Id;
|
||||
}
|
||||
ISugarQueryable<CollectDevice> query = Context.Queryable<CollectDevice>()
|
||||
.WhereIF(!string.IsNullOrEmpty(input.Name), u => u.Name.Contains(input.Name))
|
||||
.WhereIF(!string.IsNullOrEmpty(input.DeviceGroup), u => u.DeviceGroup == input.DeviceGroup)
|
||||
.WhereIF(!string.IsNullOrEmpty(input.PluginName), u => u.PluginId == (pluginid ?? 0));
|
||||
for (int i = 0; i < input.SortField.Count; i++)
|
||||
{
|
||||
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
|
||||
}
|
||||
query = query.OrderBy(it => it.Id, OrderByType.Desc);//排序
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CollectDevice GetDeviceById(long Id)
|
||||
{
|
||||
var data = GetCacheList();
|
||||
return data.FirstOrDefault(it => it.Id == Id);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public List<CollectDevice> GetCacheList(bool isMapster = true)
|
||||
{
|
||||
//先从Cache拿
|
||||
var collectDevice = CacheStatic.Cache.Get<List<CollectDevice>>(ThingsGatewayCacheConst.Cache_CollectDevice, isMapster);
|
||||
if (collectDevice == null)
|
||||
{
|
||||
collectDevice = Context.Queryable<CollectDevice>().ToList();
|
||||
if (collectDevice != null)
|
||||
{
|
||||
//插入Cache
|
||||
CacheStatic.Cache.Set(ThingsGatewayCacheConst.Cache_CollectDevice, collectDevice, isMapster);
|
||||
}
|
||||
}
|
||||
return collectDevice;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<CollectDeviceRunTime>> GetCollectDeviceRuntimeAsync(long devId = 0)
|
||||
{
|
||||
if (devId == 0)
|
||||
{
|
||||
var devices = GetCacheList(false).Where(a => a.Enable).ToList();
|
||||
var runtime = devices.Adapt<List<CollectDeviceRunTime>>().ToDictionary(a => a.Id);
|
||||
var variableService = App.GetService<IVariableService>();
|
||||
var collectVariableRunTimes = await variableService.GetDeviceVariableRuntimeAsync();
|
||||
ConcurrentDictionary<long, DriverPlugin> driverPlugins = new(_driverPluginService.GetCacheList(false).ToDictionary(a => a.Id));
|
||||
runtime.Values.ParallelForEach(device =>
|
||||
{
|
||||
driverPlugins.TryGetValue(device.PluginId, out var driverPlugin);
|
||||
device.PluginName = driverPlugin?.AssembleName;
|
||||
device.DeviceVariableRunTimes = collectVariableRunTimes.Where(a => a.DeviceId == device.Id).ToList();
|
||||
});
|
||||
|
||||
collectVariableRunTimes.ParallelForEach(variable =>
|
||||
{
|
||||
if (runtime.TryGetValue(variable.DeviceId, out var device))
|
||||
{
|
||||
variable.CollectDeviceRunTime = device;
|
||||
variable.DeviceName = device.Name;
|
||||
}
|
||||
});
|
||||
return runtime.Values.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var device = GetCacheList(false).Where(a => a.Enable).ToList().FirstOrDefault(it => it.Id == devId);
|
||||
var runtime = device.Adapt<CollectDeviceRunTime>();
|
||||
var variableService = App.GetService<IVariableService>();
|
||||
if (runtime == null) return new() { runtime };
|
||||
var pluginName = _driverPluginService.GetNameById(device.PluginId);
|
||||
var collectVariableRunTimes = await variableService.GetDeviceVariableRuntimeAsync(devId);
|
||||
runtime.PluginName = pluginName;
|
||||
runtime.DeviceVariableRunTimes = collectVariableRunTimes;
|
||||
|
||||
collectVariableRunTimes.ParallelForEach(variable =>
|
||||
{
|
||||
variable.CollectDeviceRunTime = runtime;
|
||||
variable.DeviceName = runtime.Name;
|
||||
});
|
||||
return new() { runtime };
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region 导入导出
|
||||
/// <inheritdoc/>
|
||||
public async Task<MemoryStream> ExportFileAsync(CollectDeviceInput input)
|
||||
{
|
||||
var query = GetPage(input.Adapt<CollectDevicePageInput>());
|
||||
var data = await query.ToListAsync();
|
||||
return await ExportFileAsync(data);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("导出采集设备表", IsRecordPar = false)]
|
||||
public async Task<MemoryStream> ExportFileAsync(List<CollectDevice> devDatas = null)
|
||||
{
|
||||
devDatas ??= GetCacheList(false);
|
||||
|
||||
//总数据
|
||||
Dictionary<string, object> sheets = new();
|
||||
//设备页
|
||||
List<Dictionary<string, object>> devExports = new();
|
||||
//设备附加属性,转成Dict<表名,List<Dict<列名,列数据>>>的形式
|
||||
Dictionary<string, List<Dictionary<string, object>>> devicePropertys = new();
|
||||
var driverPluginDicts = _driverPluginService.GetCacheList(false).ToDictionary(a => a.Id);
|
||||
var deviceDicts = devDatas.ToDictionary(a => a.Id);
|
||||
foreach (var devData in devDatas)
|
||||
{
|
||||
#region 设备sheet
|
||||
//设备页
|
||||
var data = devData.GetType().GetPropertiesWithCache().Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>() == null);
|
||||
Dictionary<string, object> devExport = new();
|
||||
foreach (var item in data)
|
||||
{
|
||||
//描述
|
||||
var desc = item.FindDisplayAttribute();
|
||||
//数据源增加
|
||||
devExport.Add(desc ?? item.Name, item.GetValue(devData)?.ToString());
|
||||
}
|
||||
driverPluginDicts.TryGetValue(devData.PluginId, out var driverPlugin);
|
||||
deviceDicts.TryGetValue(devData.RedundantDeviceId, out var redundantDevice);
|
||||
|
||||
//设备实体没有包含插件名称,手动插入
|
||||
devExport.Add(ExportHelpers.PluginName, driverPlugin.AssembleName);
|
||||
//设备实体没有包含冗余设备名称,手动插入
|
||||
devExport.Add(ExportHelpers.RedundantDeviceName, redundantDevice?.Name);
|
||||
|
||||
//添加完整设备信息
|
||||
devExports.Add(devExport);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件sheet
|
||||
//插件属性
|
||||
//单个设备的行数据
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
//没有包含设备名称,手动插入
|
||||
if (devData.DevicePropertys.Count > 0)
|
||||
{
|
||||
driverInfo.Add(ExportHelpers.DeviceName, devData.Name);
|
||||
}
|
||||
foreach (var item in devData.DevicePropertys ?? new())
|
||||
{
|
||||
//添加对应属性数据
|
||||
driverInfo.Add(item.Description, item.Value);
|
||||
}
|
||||
|
||||
//插件名称去除首部ThingsGateway.作为表名
|
||||
var pluginName = driverPlugin.AssembleName.Replace(ExportHelpers.PluginLeftName, "");
|
||||
if (devicePropertys.ContainsKey(pluginName))
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys[pluginName].Add(driverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys.Add(pluginName, new() { driverInfo });
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
//添加设备页
|
||||
sheets.Add(ExportHelpers.CollectDeviceSheetName, devExports);
|
||||
//添加插件属性页
|
||||
foreach (var item in devicePropertys)
|
||||
{
|
||||
sheets.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
await memoryStream.SaveAsAsync(sheets);
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file)
|
||||
{
|
||||
_fileService.ImportVerification(file);
|
||||
using var fs = new MemoryStream();
|
||||
using var stream = file.OpenReadStream(512000000);
|
||||
await stream.CopyToAsync(fs);
|
||||
var sheetNames = MiniExcel.GetSheetNames(fs);
|
||||
var deviceDicts = GetCacheList(false).ToDictionary(a => a.Name);
|
||||
var pluginDicts = _driverPluginService.GetCacheList(false).ToDictionary(a => a.AssembleName);
|
||||
|
||||
//导入检验结果
|
||||
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
|
||||
//设备页
|
||||
ImportPreviewOutput<CollectDevice> deviceImportPreview = new();
|
||||
foreach (var sheetName in sheetNames)
|
||||
{
|
||||
//单页数据
|
||||
var rows = fs.Query(useHeaderRow: true, sheetName: sheetName).Cast<IDictionary<string, object>>();
|
||||
#region 采集设备sheet
|
||||
if (sheetName == ExportHelpers.CollectDeviceSheetName)
|
||||
{
|
||||
int row = 1;
|
||||
ImportPreviewOutput<CollectDevice> importPreviewOutput = new();
|
||||
ImportPreviews.Add(sheetName, importPreviewOutput);
|
||||
deviceImportPreview = importPreviewOutput;
|
||||
List<CollectDevice> devices = new();
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var device = ((ExpandoObject)item).ConvertToEntity<CollectDevice>(true);
|
||||
#region 特殊转化名称
|
||||
//转化插件名称
|
||||
var hasPlugin = item.TryGetValue(ExportHelpers.PluginName, out var pluginObj);
|
||||
|
||||
if (pluginObj == null || !pluginDicts.TryGetValue(pluginObj.ToString(), out var plugin))
|
||||
{
|
||||
//找不到对应的插件
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, $"{ExportHelpers.PluginName}不存在"));
|
||||
return;
|
||||
}
|
||||
//转化冗余设备名称
|
||||
var hasRedundant = item.TryGetValue(ExportHelpers.PluginName, out var redundantObj);
|
||||
|
||||
#endregion
|
||||
//插件ID、设备ID、冗余设备ID都需要手动补录
|
||||
device.PluginId = plugin.Id;
|
||||
if (hasRedundant && deviceDicts.TryGetValue(redundantObj.ToString(), out var rendundantDevice))
|
||||
{
|
||||
device.RedundantDeviceId = rendundantDevice.Id;
|
||||
}
|
||||
device.Id = deviceDicts.TryGetValue(device.Name, out var collectDevice) ? collectDevice.Id : YitIdHelper.NextId();
|
||||
|
||||
devices.Add(device);
|
||||
importPreviewOutput.Results.Add((row++, true, "成功"));
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, ex.Message));
|
||||
return;
|
||||
}
|
||||
});
|
||||
importPreviewOutput.Data = devices.ToDictionary(a => a.Name);
|
||||
|
||||
}
|
||||
#endregion
|
||||
else
|
||||
{
|
||||
int row = 1;
|
||||
ImportPreviewOutput<string> importPreviewOutput = new();
|
||||
ImportPreviews.Add(sheetName, importPreviewOutput);
|
||||
//插件属性需加上前置名称
|
||||
//var newName = ExportHelpers.PluginLeftName + sheetName;
|
||||
var newName = sheetName;
|
||||
var pluginId = _driverPluginService.GetIdByName(newName);
|
||||
if (pluginId == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, $"插件{newName}不存在"));
|
||||
continue;
|
||||
}
|
||||
|
||||
var driverPlugin = _driverPluginService.GetDriverPluginById(pluginId.Value);
|
||||
var pluginSingletonService = App.GetService<PluginSingletonService>();
|
||||
var driver = (DriverBase)pluginSingletonService.GetDriver(driverPlugin);
|
||||
var propertys = driver.DriverPropertys.GetType().GetPropertiesWithCache()
|
||||
.Where(a => a.GetCustomAttribute<DevicePropertyAttribute>() != null)
|
||||
.ToDictionary(a => a.FindDisplayAttribute(a => a.GetCustomAttribute<DevicePropertyAttribute>()?.Description));
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
List<DependencyProperty> devices = new();
|
||||
foreach (var keyValuePair in item)
|
||||
{
|
||||
if (propertys.TryGetValue(keyValuePair.Key, out var propertyInfo))
|
||||
{
|
||||
devices.Add(new()
|
||||
{
|
||||
PropertyName = propertyInfo.Name,
|
||||
Description = keyValuePair.Key.ToString(),
|
||||
Value = keyValuePair.Value?.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
//转化插件名称
|
||||
|
||||
var value = item[ExportHelpers.DeviceName];
|
||||
|
||||
deviceImportPreview.Data[value.ToString()].DevicePropertys = devices;
|
||||
importPreviewOutput.Results.Add((row++, true, "成功"));
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, ex.Message));
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return ImportPreviews;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("导入采集设备表", IsRecordPar = false)]
|
||||
public async Task ImportAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
{
|
||||
var collectDevices = new List<CollectDevice>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == ExportHelpers.CollectDeviceSheetName)
|
||||
{
|
||||
var collectDeviceImports = ((ImportPreviewOutput<CollectDevice>)item.Value).Data;
|
||||
collectDevices = collectDeviceImports.Values.Adapt<List<CollectDevice>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Context.Storageable(collectDevices).ExecuteCommandAsync();
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_CollectDevice);//cache删除
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 采集设备服务
|
||||
/// </summary>
|
||||
public interface ICollectDeviceService : ITransient
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加设备
|
||||
/// </summary>
|
||||
Task AddAsync(CollectDevice input);
|
||||
/// <summary>
|
||||
/// 复制设备
|
||||
/// </summary>
|
||||
Task CopyDevAsync(IEnumerable<CollectDevice> input);
|
||||
/// <summary>
|
||||
/// 复制设备与变量
|
||||
/// </summary>
|
||||
Task CopyDevAndVarAsync(IEnumerable<CollectDevice> input);
|
||||
/// <summary>
|
||||
/// 删除设备
|
||||
/// </summary>
|
||||
Task DeleteAsync(params long[] input);
|
||||
/// <summary>
|
||||
/// 编辑设备
|
||||
/// </summary>
|
||||
Task EditAsync(CollectDeviceEditInput input);
|
||||
/// <summary>
|
||||
/// 导出Excel
|
||||
/// </summary>
|
||||
Task<MemoryStream> ExportFileAsync(List<CollectDevice> devDatas = null);
|
||||
/// <summary>
|
||||
/// 导出Excel
|
||||
/// </summary>
|
||||
Task<MemoryStream> ExportFileAsync(CollectDeviceInput input);
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
List<CollectDevice> GetCacheList(bool isMapster = true);
|
||||
/// <summary>
|
||||
/// 获取设备运行状态
|
||||
/// </summary>
|
||||
Task<List<CollectDeviceRunTime>> GetCollectDeviceRuntimeAsync(long devId = 0);
|
||||
/// <summary>
|
||||
/// 根据ID获取设备
|
||||
/// </summary>
|
||||
CollectDevice GetDeviceById(long Id);
|
||||
/// <summary>
|
||||
/// 根据名称获取ID
|
||||
/// </summary>
|
||||
long? GetIdByName(string name);
|
||||
/// <summary>
|
||||
/// 根据ID获取名称
|
||||
/// </summary>
|
||||
string GetNameById(long id);
|
||||
/// <summary>
|
||||
/// 获取设备组或名称的树节点
|
||||
/// </summary>
|
||||
List<DeviceTree> GetTree();
|
||||
/// <summary>
|
||||
/// 导入
|
||||
/// </summary>
|
||||
Task ImportAsync(Dictionary<string, ImportPreviewOutputBase> input);
|
||||
/// <summary>
|
||||
/// 分页查询
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<CollectDevice>> PageAsync(CollectDevicePageInput input);
|
||||
/// <summary>
|
||||
/// 导入验证
|
||||
/// </summary>
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file);
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
#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 Furion.DependencyInjection;
|
||||
using Furion.FriendlyException;
|
||||
|
||||
using System.Data;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <inheritdoc cref="IDriverPluginService"/>
|
||||
[Injection(Proxy = typeof(OperDispatchProxy))]
|
||||
public partial class DriverPluginService : DbRepository<DriverPlugin>, IDriverPluginService
|
||||
{
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("添加/更新插件")]
|
||||
public async Task AddAsync(DriverPluginAddInput input)
|
||||
{
|
||||
var pluginService = App.GetService<PluginSingletonService>();
|
||||
var datas = await pluginService.TestAddDriverAsync(input);
|
||||
|
||||
var driverPlugins = GetCacheList();
|
||||
foreach (var item in datas)
|
||||
{
|
||||
var data = driverPlugins.FirstOrDefault(a => a.AssembleName == item.AssembleName);
|
||||
if (data != null)
|
||||
{
|
||||
item.Id = data.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Id = YitIdHelper.NextId();
|
||||
}
|
||||
}
|
||||
var delete = driverPlugins.Where(a => a.FilePath == datas.FirstOrDefault()?.FilePath).ToList();
|
||||
//事务
|
||||
var result = await itenant.UseTranAsync(async () =>
|
||||
{
|
||||
await Context.Deleteable(delete).ExecuteCommandAsync();
|
||||
await Context.Storageable(datas).ExecuteCommandAsync();
|
||||
});
|
||||
if (result.IsSuccess)//如果成功了
|
||||
{
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_DriverPlugin);//cache删除
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Oops.Oh(result.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<DriverPlugin> GetCacheList(bool isMapster = true)
|
||||
{
|
||||
//先从Cache拿
|
||||
var driverPlugins = CacheStatic.Cache.Get<List<DriverPlugin>>(ThingsGatewayCacheConst.Cache_DriverPlugin, isMapster);
|
||||
if (driverPlugins == null)
|
||||
{
|
||||
driverPlugins = Context.Queryable<DriverPlugin>()
|
||||
.Select((u) => new DriverPlugin { Id = u.Id.SelectAll() })
|
||||
.ToList();
|
||||
if (driverPlugins != null)
|
||||
{
|
||||
//插入Cache
|
||||
CacheStatic.Cache.Set(ThingsGatewayCacheConst.Cache_DriverPlugin, driverPlugins, isMapster);
|
||||
}
|
||||
}
|
||||
return driverPlugins;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DriverPlugin GetDriverPluginById(long Id)
|
||||
{
|
||||
var data = GetCacheList();
|
||||
return data.FirstOrDefault(it => it.Id == Id);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<DriverPluginCategory> GetDriverPluginChildrenList(DriverEnum? driverTypeEnum = null)
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
if (driverTypeEnum != null)
|
||||
{
|
||||
data = data.Where(a => a.DriverTypeEnum == driverTypeEnum).ToList();
|
||||
}
|
||||
var driverPluginCategories = data.GroupBy(a => a.FileName).Select(it =>
|
||||
{
|
||||
var childrens = new List<DriverPluginCategory>();
|
||||
foreach (var item in it)
|
||||
{
|
||||
childrens.Add(new DriverPluginCategory
|
||||
{
|
||||
Id = item.Id,
|
||||
Name = item.AssembleName,
|
||||
}
|
||||
);
|
||||
}
|
||||
return new DriverPluginCategory
|
||||
{
|
||||
Id = YitIdHelper.NextId(),
|
||||
Name = it.Key,
|
||||
Children = childrens,
|
||||
};
|
||||
});
|
||||
return driverPluginCategories.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public long? GetIdByName(string name)
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
return data.FirstOrDefault(it => it.AssembleName == name)?.Id;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public string GetNameById(long id)
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
return data.FirstOrDefault(it => it.Id == id)?.AssembleName;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task<SqlSugarPagedList<DriverPlugin>> PageAsync(DriverPluginPageInput input)
|
||||
{
|
||||
var query = Context.Queryable<DriverPlugin>()
|
||||
.WhereIF(!string.IsNullOrEmpty(input.Name), u => u.AssembleName.Contains(input.Name))//根据关键字查询
|
||||
.WhereIF(!string.IsNullOrEmpty(input.FileName), u => u.FileName.Contains(input.FileName));//根据关键字查询
|
||||
for (int i = 0; i < input.SortField.Count; i++)
|
||||
{
|
||||
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
|
||||
}
|
||||
query = query.OrderBy(it => it.Id, OrderByType.Desc);//排序
|
||||
|
||||
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
#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 System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 上传设备添加DTO
|
||||
/// </summary>
|
||||
public class UploadDeviceAddInput : UploadDeviceEditInput
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[Required(ErrorMessage = "不能为空")]
|
||||
public override string Name { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[MinValue(1, ErrorMessage = "插件不能为空")]
|
||||
public override long PluginId { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public override bool IsLogOut { get; set; } = true;
|
||||
/// <inheritdoc/>
|
||||
public override bool Enable { get; set; } = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 上传设备修改DTO
|
||||
/// </summary>
|
||||
public class UploadDeviceEditInput : UploadDevice
|
||||
{
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Required(ErrorMessage = "不能为空")]
|
||||
public override string Name { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[MinValue(1, ErrorMessage = "插件不能为空")]
|
||||
public override long PluginId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传设备分页查询
|
||||
/// </summary>
|
||||
public class UploadDevicePageInput : BasePageInput
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[Description("设备名称")]
|
||||
public string Name { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("插件名称")]
|
||||
public string PluginName { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("设备组")]
|
||||
public string DeviceGroup { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// 上传设备分页查询
|
||||
/// </summary>
|
||||
public class UploadDeviceInput
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[Description("设备名称")]
|
||||
public string Name { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("插件名称")]
|
||||
public string PluginName { get; set; }
|
||||
/// <inheritdoc/>
|
||||
[Description("设备组")]
|
||||
public string DeviceGroup { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 上传设备服务
|
||||
/// </summary>
|
||||
public interface IUploadDeviceService : ITransient
|
||||
{
|
||||
/// <summary>
|
||||
/// Sql连接对象
|
||||
/// </summary>
|
||||
public ISqlSugarClient Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 添加上传设备
|
||||
/// </summary>
|
||||
Task AddAsync(UploadDevice input);
|
||||
/// <summary>
|
||||
/// 复制设备
|
||||
/// </summary>
|
||||
Task CopyDevAsync(IEnumerable<UploadDevice> input);
|
||||
/// <summary>
|
||||
/// 删除设备
|
||||
/// </summary>
|
||||
Task DeleteAsync(params long[] input);
|
||||
/// <summary>
|
||||
/// 编辑设备
|
||||
/// </summary>
|
||||
Task EditAsync(UploadDeviceEditInput input);
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
Task<MemoryStream> ExportFileAsync(List<UploadDevice> devDatas = null);
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
Task<MemoryStream> ExportFileAsync(UploadDeviceInput input);
|
||||
/// <summary>
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
List<UploadDevice> GetCacheList(bool isMapster = true);
|
||||
/// <summary>
|
||||
/// 根据ID获取设备
|
||||
/// </summary>
|
||||
UploadDevice GetDeviceById(long Id);
|
||||
/// <summary>
|
||||
/// 根据名称获取ID
|
||||
/// </summary>
|
||||
long? GetIdByName(string name);
|
||||
/// <summary>
|
||||
/// 根据ID获取名称
|
||||
/// </summary>
|
||||
string GetNameById(long id);
|
||||
/// <summary>
|
||||
/// 获取上传设备运行状态
|
||||
/// </summary>
|
||||
List<UploadDeviceRunTime> GetUploadDeviceRuntime(long devId = 0);
|
||||
/// <summary>
|
||||
/// 导入
|
||||
/// </summary>
|
||||
Task ImportAsync(Dictionary<string, ImportPreviewOutputBase> input);
|
||||
/// <summary>
|
||||
/// 分页
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<UploadDevice>> PageAsync(UploadDevicePageInput input);
|
||||
/// <summary>
|
||||
/// 导入验证
|
||||
/// </summary>
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file);
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
#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 Furion.DependencyInjection;
|
||||
using Furion.FriendlyException;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using MiniExcelLibs;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Dynamic;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Application.Extensions;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <inheritdoc cref="IUploadDeviceService"/>
|
||||
[Injection(Proxy = typeof(OperDispatchProxy))]
|
||||
public class UploadDeviceService : DbRepository<UploadDevice>, IUploadDeviceService
|
||||
{
|
||||
private readonly IDriverPluginService _driverPluginService;
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
/// <inheritdoc cref="IUploadDeviceService"/>
|
||||
public UploadDeviceService(
|
||||
IDriverPluginService driverPluginService,
|
||||
IFileService fileService
|
||||
)
|
||||
{
|
||||
_fileService = fileService;
|
||||
_driverPluginService = driverPluginService;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("添加上传设备")]
|
||||
public async Task AddAsync(UploadDevice input)
|
||||
{
|
||||
var account_Id = GetIdByName(input.Name);
|
||||
if (account_Id > 0)
|
||||
throw Oops.Bah($"存在重复的名称:{input.Name}");
|
||||
await InsertAsync(input);//添加数据
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_UploadDevice);//cache删除
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("复制上传设备")]
|
||||
public async Task CopyDevAsync(IEnumerable<UploadDevice> input)
|
||||
{
|
||||
var newId = YitIdHelper.NextId();
|
||||
var newDevs = input.Adapt<List<UploadDevice>>();
|
||||
newDevs.ForEach(a =>
|
||||
{
|
||||
a.Id = newId;
|
||||
a.Name = "Copy-" + a.Name + "-" + newId.ToString();
|
||||
});
|
||||
|
||||
var result = await InsertRangeAsync(newDevs);//添加数据
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_UploadDevice);//cache删除
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("删除上传设备")]
|
||||
public async Task DeleteAsync(params long[] input)
|
||||
{
|
||||
//获取所有ID
|
||||
if (input.Length > 0)
|
||||
{
|
||||
var result = await DeleteByIdsAsync(input.Cast<object>().ToArray());
|
||||
if (result)
|
||||
{
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_UploadDevice);//cache删除
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("编辑上传设备")]
|
||||
public async Task EditAsync(UploadDeviceEditInput input)
|
||||
{
|
||||
var account_Id = GetIdByName(input.Name);
|
||||
if (account_Id > 0 && account_Id != input.Id)
|
||||
throw Oops.Bah($"存在重复的名称:{input.Name}");
|
||||
|
||||
if (await Context.Updateable(input.Adapt<UploadDevice>()).ExecuteCommandAsync() > 0)//修改数据
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_UploadDevice);//cache删除
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc cref="IUploadDeviceService"/>
|
||||
public List<UploadDevice> GetCacheList(bool isMapster = true)
|
||||
{
|
||||
//先从Cache拿
|
||||
var uploadDevice = CacheStatic.Cache.Get<List<UploadDevice>>(ThingsGatewayCacheConst.Cache_UploadDevice, isMapster);
|
||||
if (uploadDevice == null)
|
||||
{
|
||||
uploadDevice = Context.Queryable<UploadDevice>().ToList();
|
||||
if (uploadDevice != null)
|
||||
{
|
||||
//插入Cache
|
||||
CacheStatic.Cache.Set(ThingsGatewayCacheConst.Cache_UploadDevice, uploadDevice, isMapster);
|
||||
}
|
||||
}
|
||||
return uploadDevice;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public UploadDevice GetDeviceById(long Id)
|
||||
{
|
||||
var data = GetCacheList();
|
||||
return data.FirstOrDefault(it => it.Id == Id);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public long? GetIdByName(string name)
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
return data.FirstOrDefault(it => it.Name == name)?.Id;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public string GetNameById(long id)
|
||||
{
|
||||
var data = GetCacheList(false);
|
||||
return data.FirstOrDefault(it => it.Id == id)?.Name;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public List<UploadDeviceRunTime> GetUploadDeviceRuntime(long devId = 0)
|
||||
{
|
||||
ConcurrentDictionary<long, DriverPlugin> driverPlugins = new(_driverPluginService.GetCacheList(false).ToDictionary(a => a.Id));
|
||||
if (devId == 0)
|
||||
{
|
||||
var devices = GetCacheList(false).Where(a => a.Enable).ToList();
|
||||
var runtime = devices.Adapt<List<UploadDeviceRunTime>>();
|
||||
foreach (var device in runtime)
|
||||
{
|
||||
driverPlugins.TryGetValue(device.PluginId, out var driverPlugin);
|
||||
device.PluginName = driverPlugin?.AssembleName;
|
||||
}
|
||||
return runtime;
|
||||
}
|
||||
else
|
||||
{
|
||||
var devices = GetCacheList(false).Where(a => a.Enable).ToList();
|
||||
devices = devices.Where(it => it.Id == devId).ToList();
|
||||
var runtime = devices.Adapt<List<UploadDeviceRunTime>>();
|
||||
foreach (var device in runtime)
|
||||
{
|
||||
driverPlugins.TryGetValue(device.PluginId, out var driverPlugin);
|
||||
device.PluginName = driverPlugin?.AssembleName;
|
||||
}
|
||||
return runtime;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<SqlSugarPagedList<UploadDevice>> PageAsync(UploadDevicePageInput input)
|
||||
{
|
||||
var query = GetPage(input);
|
||||
var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
|
||||
return pageInfo;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
private ISugarQueryable<UploadDevice> GetPage(UploadDevicePageInput input)
|
||||
{
|
||||
long? pluginid = 0;
|
||||
if (!string.IsNullOrEmpty(input.PluginName))
|
||||
{
|
||||
pluginid = _driverPluginService.GetCacheList(false).FirstOrDefault(it => it.AssembleName.Contains(input.PluginName))?.Id;
|
||||
}
|
||||
var query = Context.Queryable<UploadDevice>()
|
||||
.WhereIF(!string.IsNullOrEmpty(input.Name), u => u.Name.Contains(input.Name))
|
||||
.WhereIF(!string.IsNullOrEmpty(input.PluginName), u => u.PluginId == (pluginid ?? 0))
|
||||
.WhereIF(!string.IsNullOrEmpty(input.DeviceGroup), u => u.DeviceGroup == input.DeviceGroup);
|
||||
for (int i = 0; i < input.SortField.Count; i++)
|
||||
{
|
||||
query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
|
||||
}
|
||||
query = query.OrderBy(it => it.Id, OrderByType.Desc);//排序
|
||||
|
||||
return query;
|
||||
}
|
||||
#region 导入导出
|
||||
/// <inheritdoc/>
|
||||
public async Task<MemoryStream> ExportFileAsync(UploadDeviceInput input)
|
||||
{
|
||||
var query = GetPage(input.Adapt<UploadDevicePageInput>());
|
||||
var data = await query.ToListAsync();
|
||||
return await ExportFileAsync(data);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("导出上传设备表", IsRecordPar = false)]
|
||||
public async Task<MemoryStream> ExportFileAsync(List<UploadDevice> devDatas = null)
|
||||
{
|
||||
devDatas ??= GetCacheList(false);
|
||||
|
||||
//总数据
|
||||
Dictionary<string, object> sheets = new();
|
||||
//设备页
|
||||
List<Dictionary<string, object>> devExports = new();
|
||||
//设备附加属性,转成Dict<表名,List<Dict<列名,列数据>>>的形式
|
||||
Dictionary<string, List<Dictionary<string, object>>> devicePropertys = new();
|
||||
|
||||
foreach (var devData in devDatas)
|
||||
{
|
||||
#region 设备sheet
|
||||
//设备页
|
||||
var data = devData.GetType().GetPropertiesWithCache().Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>() == null);
|
||||
Dictionary<string, object> devExport = new();
|
||||
foreach (var item in data)
|
||||
{
|
||||
//描述
|
||||
var desc = item.FindDisplayAttribute();
|
||||
//数据源增加
|
||||
devExport.Add(desc ?? item.Name, item.GetValue(devData)?.ToString());
|
||||
}
|
||||
//设备实体没有包含插件名称,手动插入
|
||||
devExport.Add(ExportHelpers.PluginName, _driverPluginService.GetNameById(devData.PluginId));
|
||||
|
||||
//添加完整设备信息
|
||||
devExports.Add(devExport);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 插件sheet
|
||||
//插件属性
|
||||
//单个设备的行数据
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
//没有包含设备名称,手动插入
|
||||
if (devData.DevicePropertys.Count > 0)
|
||||
{
|
||||
driverInfo.Add(ExportHelpers.DeviceName, devData.Name);
|
||||
}
|
||||
foreach (var item in devData.DevicePropertys ?? new())
|
||||
{
|
||||
//添加对应属性数据
|
||||
driverInfo.Add(item.Description, item.Value);
|
||||
}
|
||||
|
||||
//插件名称去除首部ThingsGateway.作为表名
|
||||
var pluginName = _driverPluginService.GetNameById(devData.PluginId).Replace(ExportHelpers.PluginLeftName, "");
|
||||
if (devicePropertys.ContainsKey(pluginName))
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys[pluginName].Add(driverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys.Add(pluginName, new() { driverInfo });
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
//添加设备页
|
||||
sheets.Add(ExportHelpers.UploadDeviceSheetName, devExports);
|
||||
//添加插件属性页
|
||||
foreach (var item in devicePropertys)
|
||||
{
|
||||
sheets.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
await memoryStream.SaveAsAsync(sheets);
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("导入上传设备表", IsRecordPar = false)]
|
||||
public async Task ImportAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
{
|
||||
var uploadDevices = new List<UploadDevice>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == ExportHelpers.UploadDeviceSheetName)
|
||||
{
|
||||
var uploadDeviceImports = ((ImportPreviewOutput<UploadDevice>)item.Value).Data;
|
||||
uploadDevices = uploadDeviceImports.Values.Adapt<List<UploadDevice>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Context.Storageable(uploadDevices).ExecuteCommandAsync();
|
||||
CacheStatic.Cache.Remove(ThingsGatewayCacheConst.Cache_UploadDevice);//cache删除
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file)
|
||||
{
|
||||
_fileService.ImportVerification(file);
|
||||
using var fs = new MemoryStream();
|
||||
using var stream = file.OpenReadStream(512000000);
|
||||
await stream.CopyToAsync(fs);
|
||||
var sheetNames = MiniExcel.GetSheetNames(fs);
|
||||
var deviceDicts = GetCacheList(false).ToDictionary(a => a.Name);
|
||||
var pluginDicts = _driverPluginService.GetCacheList(false).ToDictionary(a => a.AssembleName);
|
||||
|
||||
//导入检验结果
|
||||
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
|
||||
//设备页
|
||||
ImportPreviewOutput<UploadDevice> deviceImportPreview = new();
|
||||
foreach (var sheetName in sheetNames)
|
||||
{
|
||||
//单页数据
|
||||
var rows = (fs.Query(useHeaderRow: true, sheetName: sheetName)).Cast<IDictionary<string, object>>();
|
||||
#region 上传设备sheet
|
||||
if (sheetName == ExportHelpers.UploadDeviceSheetName)
|
||||
{
|
||||
int row = 1;
|
||||
ImportPreviewOutput<UploadDevice> importPreviewOutput = new();
|
||||
ImportPreviews.Add(sheetName, importPreviewOutput);
|
||||
deviceImportPreview = importPreviewOutput;
|
||||
|
||||
List<UploadDevice> devices = new();
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var device = ((ExpandoObject)item).ConvertToEntity<UploadDevice>(true);
|
||||
|
||||
#region 特殊转化名称
|
||||
//转化插件名称
|
||||
var hasPlugin = item.TryGetValue(ExportHelpers.PluginName, out var pluginObj);
|
||||
|
||||
if (pluginObj == null || !pluginDicts.TryGetValue(pluginObj.ToString(), out var plugin))
|
||||
{
|
||||
//找不到对应的插件
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, $"{ExportHelpers.PluginName}不存在"));
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
//插件ID、设备ID都需要手动补录
|
||||
device.PluginId = plugin.Id;
|
||||
device.Id = deviceDicts.TryGetValue(device.Name, out var uploadDevice) ? uploadDevice.Id : YitIdHelper.NextId();
|
||||
|
||||
devices.Add(device);
|
||||
importPreviewOutput.Results.Add((row++, true, "成功"));
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, ex.Message));
|
||||
return;
|
||||
}
|
||||
});
|
||||
importPreviewOutput.Data = devices.ToDictionary(a => a.Name);
|
||||
|
||||
}
|
||||
#endregion
|
||||
else
|
||||
{
|
||||
int row = 1;
|
||||
ImportPreviewOutput<string> importPreviewOutput = new();
|
||||
ImportPreviews.Add(sheetName, importPreviewOutput);
|
||||
//插件属性需加上前置名称
|
||||
//var newName = ExportHelpers.PluginLeftName + sheetName;
|
||||
var newName = sheetName;
|
||||
var pluginId = _driverPluginService.GetIdByName(newName);
|
||||
if (pluginId == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, $"插件{newName}不存在"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var driverPlugin = _driverPluginService.GetDriverPluginById(pluginId.Value);
|
||||
var pluginSingletonService = App.GetService<PluginSingletonService>();
|
||||
var driver = pluginSingletonService.GetDriver(driverPlugin);
|
||||
var propertys = driver.DriverPropertys.GetType().GetPropertiesWithCache()
|
||||
.Where(a => a.GetCustomAttribute<DevicePropertyAttribute>() != null)
|
||||
.ToDictionary(a => a.FindDisplayAttribute(a => a.GetCustomAttribute<DevicePropertyAttribute>()?.Description));
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
List<DependencyProperty> devices = new();
|
||||
foreach (var keyValuePair in item)
|
||||
{
|
||||
if (propertys.TryGetValue(keyValuePair.Key, out var propertyInfo))
|
||||
{
|
||||
devices.Add(new()
|
||||
{
|
||||
PropertyName = propertyInfo.Name,
|
||||
Description = keyValuePair.Key.ToString(),
|
||||
Value = keyValuePair.Value?.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
//转化设备名称
|
||||
var value = item[ExportHelpers.DeviceName];
|
||||
|
||||
deviceImportPreview.Data[value.ToString()].DevicePropertys = devices;
|
||||
importPreviewOutput.Results.Add((row++, true, "成功"));
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((row++, false, ex.Message));
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return ImportPreviews;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
#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.DependencyInjection;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 变量数据服务
|
||||
/// </summary>
|
||||
public interface IVariableService : ITransient
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加变量
|
||||
/// </summary>
|
||||
Task AddAsync(DeviceVariable input);
|
||||
/// <summary>
|
||||
/// 添加变量
|
||||
/// </summary>
|
||||
Task AddBatchAsync(List<DeviceVariable> input);
|
||||
/// <summary>
|
||||
/// 清空设备变量
|
||||
/// </summary>
|
||||
Task ClearDeviceVariableAsync();
|
||||
/// <summary>
|
||||
/// 清空中间变量
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task ClearMemoryVariableAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 删除变量
|
||||
/// </summary>
|
||||
Task DeleteAsync(params long[] input);
|
||||
/// <summary>
|
||||
/// 删除变量缓存
|
||||
/// </summary>
|
||||
void DeleteVariableFromCache();
|
||||
/// <summary>
|
||||
/// 编辑变量
|
||||
/// </summary>
|
||||
Task EditAsync(DeviceVariable input);
|
||||
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
Task<MemoryStream> ExportFileAsync(List<DeviceVariable> collectDeviceVariables = null, string deviceName = null);
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
Task<MemoryStream> ExportFileAsync(MemoryVariableInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取变量运行状态
|
||||
/// </summary>
|
||||
Task<List<DeviceVariableRunTime>> GetDeviceVariableRuntimeAsync(long devId = 0);
|
||||
|
||||
/// <summary>
|
||||
/// 获取中间变量运行态
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<DeviceVariableRunTime>> GetMemoryVariableRuntimeAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 导入
|
||||
/// </summary>
|
||||
Task ImportAsync(Dictionary<string, ImportPreviewOutputBase> input);
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
/// <param name="devDatas"></param>
|
||||
/// <returns></returns>
|
||||
Task<MemoryStream> MemoryVariableExportFileAsync(List<MemoryVariable> devDatas = null);
|
||||
/// <summary>
|
||||
/// 导入
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> MemoryVariablePreviewAsync(IBrowserFile file);
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询
|
||||
/// </summary>
|
||||
Task<SqlSugarPagedList<DeviceVariable>> PageAsync(VariablePageInput input);
|
||||
/// <summary>
|
||||
/// 导入验证
|
||||
/// </summary>
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile file);
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="SeedData\Json\driver_plugin.json" />
|
||||
<None Remove="SeedData\Json\gatewayopenapi_user.json" />
|
||||
<None Remove="SeedData\Json\gateway_relation.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="SeedData\Json\driver_plugin.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\gateway_config.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\gateway_resource.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\gatewayopenapi_user.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="SeedData\Json\gateway_relation.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CodingSeb.ExpressionEvaluator" Version="1.4.39" />
|
||||
<PackageReference Include="Hardware.Info" Version="11.1.1.1" />
|
||||
<PackageReference Include="CS-Script" Version="4.8.1" />
|
||||
<!--CS-Script与Furion冲突,直接安装覆盖版本-->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,683 +0,0 @@
|
||||
#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.Logging.Extensions;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Application.Extensions;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
using UAParser;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备采集报警后台服务
|
||||
/// </summary>
|
||||
public class AlarmWorker : BackgroundService
|
||||
{
|
||||
private readonly GlobalDeviceData _globalDeviceData;
|
||||
private readonly ILogger<AlarmWorker> _logger;
|
||||
/// <inheritdoc cref="AlarmWorker"/>
|
||||
public AlarmWorker(ILogger<AlarmWorker> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_globalDeviceData = ServiceHelper.Services.GetService<GlobalDeviceData>();
|
||||
}
|
||||
/// <summary>
|
||||
/// 报警变化事件
|
||||
/// </summary>
|
||||
public event VariableChangeEventHandler OnAlarmChanged;
|
||||
/// <summary>
|
||||
/// 设备状态变化事件
|
||||
/// </summary>
|
||||
public event DelegateOnDeviceChanged OnDeviceStatusChanged;
|
||||
/// <summary>
|
||||
/// 实时报警列表
|
||||
/// </summary>
|
||||
public ConcurrentList<DeviceVariableRunTime> RealAlarmDeviceVariables { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 服务状态
|
||||
/// </summary>
|
||||
public OperResult StatuString { get; set; } = new OperResult("初始化");
|
||||
private ConcurrentQueue<DeviceVariableRunTime> DeviceVariables { get; set; } = new();
|
||||
private ConcurrentQueue<HistoryAlarm> HisAlarmDeviceVariables { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 获取数据库链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<OperResult<SqlSugarClient>> GetAlarmDbAsync()
|
||||
{
|
||||
var ConfigService = ServiceHelper.Services.GetService<IConfigService>();
|
||||
var alarmEnable = (await ConfigService.GetByConfigKeyAsync(ThingsGatewayConfigConst.ThingGateway_AlarmConfig_Base, ThingsGatewayConfigConst.Config_Alarm_Enable))?.ConfigValue?.ToBoolean();
|
||||
var alarmDbType = (await ConfigService.GetByConfigKeyAsync(ThingsGatewayConfigConst.ThingGateway_AlarmConfig_Base, ThingsGatewayConfigConst.Config_Alarm_DbType))?.ConfigValue;
|
||||
var alarmConnstr = (await ConfigService.GetByConfigKeyAsync(ThingsGatewayConfigConst.ThingGateway_AlarmConfig_Base, ThingsGatewayConfigConst.Config_Alarm_ConnStr))?.ConfigValue;
|
||||
|
||||
if (!(alarmEnable == true))
|
||||
{
|
||||
return new OperResult<SqlSugarClient>("历史报警已配置为Disable");
|
||||
}
|
||||
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
DbType type = DbType.SqlServer;
|
||||
if (!string.IsNullOrEmpty(alarmDbType))
|
||||
{
|
||||
if (Enum.TryParse<DbType>(alarmDbType, ignoreCase: true, out var result))
|
||||
{
|
||||
type = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult<SqlSugarClient>("数据库类型转换失败");
|
||||
}
|
||||
}
|
||||
|
||||
var sqlSugarClient = new SqlSugarClient(new ConnectionConfig()
|
||||
{
|
||||
ConnectionString = alarmConnstr,//连接字符串
|
||||
DbType = type,//数据库类型
|
||||
IsAutoCloseConnection = true, //不设成true要手动close
|
||||
ConfigureExternalServices = configureExternalServices,
|
||||
MoreSettings = new ConnMoreSettings
|
||||
{
|
||||
SqlServerCodeFirstNvarchar = true,//设置默认nvarchar
|
||||
TableEnumIsString = true,
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
return OperResult.CreateSuccessResult(sqlSugarClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取bool报警类型
|
||||
/// </summary>
|
||||
private static AlarmEnum GetBoolAlarmCode(DeviceVariableRunTime tag, out string limit, out string expressions, out string text)
|
||||
{
|
||||
limit = string.Empty;
|
||||
expressions = string.Empty;
|
||||
text = string.Empty;
|
||||
if (tag.BoolCloseAlarmEnable && tag.Value.ToBoolean() == false)
|
||||
{
|
||||
limit = false.ToString();
|
||||
expressions = tag.BoolCloseRestrainExpressions;
|
||||
text = tag.BoolCloseAlarmText;
|
||||
return AlarmEnum.Close;
|
||||
}
|
||||
if (tag.BoolOpenAlarmEnable && tag.Value.ToBoolean() == true)
|
||||
{
|
||||
limit = true.ToString();
|
||||
expressions = tag.BoolOpenRestrainExpressions;
|
||||
text = tag.BoolOpenAlarmText;
|
||||
return AlarmEnum.Open;
|
||||
}
|
||||
return AlarmEnum.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取value报警类型
|
||||
/// </summary>
|
||||
private static AlarmEnum GetDecimalAlarmDegree(DeviceVariableRunTime tag, out string limit, out string expressions, out string text)
|
||||
{
|
||||
limit = string.Empty;
|
||||
expressions = string.Empty;
|
||||
text = string.Empty;
|
||||
|
||||
if (tag.HHAlarmEnable && tag.Value.ToDecimal() > tag.HHAlarmCode.ToDecimal())
|
||||
{
|
||||
limit = tag.HHAlarmCode.ToString();
|
||||
expressions = tag.HHRestrainExpressions;
|
||||
text = tag.HHAlarmText;
|
||||
return AlarmEnum.HH;
|
||||
}
|
||||
|
||||
if (tag.HAlarmEnable && tag.Value.ToDecimal() > tag.HAlarmCode.ToDecimal())
|
||||
{
|
||||
limit = tag.HAlarmCode.ToString();
|
||||
expressions = tag.HRestrainExpressions;
|
||||
text = tag.HAlarmText;
|
||||
return AlarmEnum.H;
|
||||
}
|
||||
|
||||
if (tag.LAlarmEnable && tag.Value.ToDecimal() < tag.LAlarmCode.ToDecimal())
|
||||
{
|
||||
limit = tag.LAlarmCode.ToString();
|
||||
expressions = tag.LRestrainExpressions;
|
||||
text = tag.LAlarmText;
|
||||
return AlarmEnum.L;
|
||||
}
|
||||
if (tag.LLAlarmEnable && tag.Value.ToDecimal() < tag.LLAlarmCode.ToDecimal())
|
||||
{
|
||||
limit = tag.LLAlarmCode.ToString();
|
||||
expressions = tag.LLRestrainExpressions;
|
||||
text = tag.LLAlarmText;
|
||||
return AlarmEnum.LL;
|
||||
}
|
||||
return AlarmEnum.None;
|
||||
}
|
||||
#region worker服务
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken token)
|
||||
{
|
||||
_logger?.LogInformation("报警服务启动");
|
||||
await base.StartAsync(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken token)
|
||||
{
|
||||
_logger?.LogInformation("报警服务停止");
|
||||
return base.StopAsync(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Delay(5000, stoppingToken);
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(60000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 核心实现
|
||||
/// <summary>
|
||||
/// 循环线程取消标识
|
||||
/// </summary>
|
||||
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
|
||||
/// <summary>
|
||||
/// 全部重启锁
|
||||
/// </summary>
|
||||
private readonly EasyLock restartLock = new();
|
||||
|
||||
private Task HisAlarmTask;
|
||||
private bool IsExited;
|
||||
private Task RealAlarmTask;
|
||||
private CacheDb CacheDb { get; set; }
|
||||
/// <summary>
|
||||
/// 重启
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task RestartAsync()
|
||||
{
|
||||
await StopAsync();
|
||||
await StartAsync();
|
||||
}
|
||||
|
||||
internal async Task StartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
foreach (var item in _globalDeviceData.CollectDevices)
|
||||
{
|
||||
item.DeviceStatusChange += DeviceStatusChange;
|
||||
item.DeviceVariableRunTimes?.ForEach(v => { v.VariableCollectChange += DeviceVariableChange; });
|
||||
}
|
||||
StoppingTokens.Add(new());
|
||||
await InitAsync();
|
||||
if (RealAlarmTask.Status == TaskStatus.Created)
|
||||
RealAlarmTask.Start();
|
||||
if (HisAlarmTask.Status == TaskStatus.Created)
|
||||
HisAlarmTask.Start();
|
||||
IsExited = false;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
internal async Task StopAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
IsExited = true;
|
||||
|
||||
foreach (var device in _globalDeviceData.CollectDevices)
|
||||
{
|
||||
device.DeviceStatusChange -= DeviceStatusChange;
|
||||
device.DeviceVariableRunTimes?.ForEach(v => { v.VariableCollectChange -= DeviceVariableChange; });
|
||||
}
|
||||
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
if (RealAlarmTask != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation($"实时报警线程停止中");
|
||||
await RealAlarmTask.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
_logger?.LogInformation($"实时报警线程已停止");
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
_logger?.LogWarning($"实时报警线程停止超时,已强制取消");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, "等待实时报警线程停止错误");
|
||||
}
|
||||
|
||||
RealAlarmTask?.SafeDispose();
|
||||
}
|
||||
|
||||
|
||||
if (HisAlarmTask != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation($"历史报警线程停止中");
|
||||
await HisAlarmTask.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
_logger?.LogInformation($"历史报警线程已停止");
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
_logger?.LogWarning($"历史报警线程停止超时,已强制取消");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, "等待历史报警线程停止错误");
|
||||
}
|
||||
}
|
||||
|
||||
HisAlarmTask?.SafeDispose();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.SafeDispose();
|
||||
}
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void AlarmAnalysis(DeviceVariableRunTime item)
|
||||
{
|
||||
string limit;
|
||||
string ex;
|
||||
string text;
|
||||
AlarmEnum alarmEnum;
|
||||
if (item.DataTypeEnum.GetSystemType() == typeof(bool))
|
||||
{
|
||||
alarmEnum = GetBoolAlarmCode(item, out limit, out ex, out text);
|
||||
}
|
||||
else
|
||||
{
|
||||
alarmEnum = GetDecimalAlarmDegree(item, out limit, out ex, out text);
|
||||
}
|
||||
if (alarmEnum == AlarmEnum.None)
|
||||
{
|
||||
//需恢复报警,如果存在的话
|
||||
AlarmChange(item, null, text, EventEnum.Finish, alarmEnum);
|
||||
}
|
||||
else
|
||||
{
|
||||
//需更新报警,不管是否存在
|
||||
if (!string.IsNullOrEmpty(ex))
|
||||
{
|
||||
var data = ex.GetExpressionsResult(item.Value);
|
||||
if (data is bool result)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
AlarmChange(item, limit, text, EventEnum.Alarm, alarmEnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AlarmChange(item, limit, text, EventEnum.Alarm, alarmEnum);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
private void AlarmChange(DeviceVariableRunTime item, object limit, string text, EventEnum eventEnum, AlarmEnum alarmEnum)
|
||||
{
|
||||
if (eventEnum == EventEnum.Finish)
|
||||
{
|
||||
//实时报警没有找到的话直接返回
|
||||
if (!RealAlarmDeviceVariables.Any(it => it.Id == item.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (eventEnum == EventEnum.Alarm)
|
||||
{
|
||||
var variable = RealAlarmDeviceVariables.FirstOrDefault(it => it.Id == item.Id);
|
||||
if (variable != null)
|
||||
{
|
||||
if (item.AlarmTypeEnum == alarmEnum)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (eventEnum == EventEnum.Alarm)
|
||||
{
|
||||
item.AlarmTypeEnum = alarmEnum;
|
||||
item.EventTypeEnum = eventEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.AlarmText = text;
|
||||
item.AlarmTime = SysDateTimeExtensions.CurrentDateTime;
|
||||
item.EventTime = SysDateTimeExtensions.CurrentDateTime;
|
||||
}
|
||||
else if (eventEnum == EventEnum.Finish)
|
||||
{
|
||||
var oldAlarm = RealAlarmDeviceVariables.FirstOrDefault(it => it.Id == item.Id);
|
||||
item.AlarmTypeEnum = oldAlarm.AlarmTypeEnum;
|
||||
item.EventTypeEnum = eventEnum;
|
||||
item.AlarmLimit = oldAlarm.AlarmLimit;
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.AlarmText = text;
|
||||
item.EventTime = SysDateTimeExtensions.CurrentDateTime;
|
||||
}
|
||||
else if (eventEnum == EventEnum.Check)
|
||||
{
|
||||
item.AlarmTypeEnum = alarmEnum;
|
||||
item.EventTypeEnum = eventEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.AlarmText = text;
|
||||
item.EventTime = SysDateTimeExtensions.CurrentDateTime;
|
||||
}
|
||||
|
||||
OnAlarmChanged?.Invoke(item.Adapt<DeviceVariableRunTime>());
|
||||
if (!IsExited)
|
||||
{
|
||||
HisAlarmDeviceVariables.Enqueue(item.Adapt<HistoryAlarm>());
|
||||
}
|
||||
|
||||
if (eventEnum == EventEnum.Alarm)
|
||||
{
|
||||
RealAlarmDeviceVariables.RemoveWhere(it => it.Id == item.Id);
|
||||
RealAlarmDeviceVariables.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
RealAlarmDeviceVariables.RemoveWhere(it => it.Id == item.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DeviceStatusChange(CollectDeviceRunTime device)
|
||||
{
|
||||
OnDeviceStatusChanged?.Invoke(device.Adapt<CollectDeviceRunTime>());
|
||||
}
|
||||
|
||||
private void DeviceVariableChange(DeviceVariableRunTime variable)
|
||||
{
|
||||
//这里不能序列化变量,报警服务需改变同一个变量指向的属性
|
||||
DeviceVariables.Enqueue(variable);
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
private async Task InitAsync()
|
||||
{
|
||||
CacheDb = new("HistoryAlarmCache");
|
||||
CancellationTokenSource stoppingToken = StoppingTokens.Last();
|
||||
RealAlarmTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
_logger?.LogInformation($"实时报警线程开始");
|
||||
while (!stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(500, stoppingToken.Token);
|
||||
var list = DeviceVariables.ToListWithDequeue();
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
if (!item.AlarmEnable) continue;
|
||||
AlarmAnalysis(item);
|
||||
}
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, $"实时报警循环异常");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
|
||||
HisAlarmTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
_logger?.LogInformation($"历史报警线程开始");
|
||||
await Task.Yield();//返回线程控制,不再阻塞
|
||||
try
|
||||
{
|
||||
await Task.Delay(500, stoppingToken.Token);
|
||||
|
||||
var result = await GetAlarmDbAsync();
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
_logger?.LogWarning($"历史报警线程即将退出:" + result.Message);
|
||||
StatuString = new OperResult($"已退出:{result.Message}");
|
||||
IsExited = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var sqlSugarClient = result.Content;
|
||||
bool isSuccess = true;
|
||||
/***创建/更新单个表***/
|
||||
try
|
||||
{
|
||||
await sqlSugarClient.Queryable<HistoryAlarm>().FirstAsync(stoppingToken.Token);
|
||||
isSuccess = true;
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
IsExited = true;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_logger.LogWarning("连接历史报警表失败,尝试初始化表");
|
||||
sqlSugarClient.CodeFirst.InitTables(typeof(HistoryAlarm));
|
||||
isSuccess = true;
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
isSuccess = false;
|
||||
StatuString = new OperResult(ex);
|
||||
_logger.LogWarning(ex, "连接历史报警数据库失败");
|
||||
}
|
||||
}
|
||||
|
||||
while (!stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(500, stoppingToken.Token);
|
||||
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
//缓存值
|
||||
var cacheData = await CacheDb.GetCacheData();
|
||||
if (cacheData.Count > 0)
|
||||
{
|
||||
var data = cacheData.SelectMany(a => a.CacheStr.FromJson<List<HistoryAlarm>>()).ToList();
|
||||
try
|
||||
{
|
||||
var count = await sqlSugarClient.Insertable(data).ExecuteCommandAsync(stoppingToken.Token);
|
||||
await CacheDb.DeleteCacheData(cacheData.Select(a => a.Id).ToArray());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (isSuccess)
|
||||
_logger.LogWarning(ex, "写入历史报警失败");
|
||||
}
|
||||
}
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
|
||||
var list = HisAlarmDeviceVariables.ToListWithDequeue();
|
||||
if (list.Count != 0)
|
||||
{
|
||||
////Sql保存
|
||||
list.ForEach(it =>
|
||||
{
|
||||
it.Id = YitIdHelper.NextId();
|
||||
});
|
||||
//插入
|
||||
try
|
||||
{
|
||||
await sqlSugarClient.Insertable(list).ExecuteCommandAsync(stoppingToken.Token);
|
||||
isSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (isSuccess)
|
||||
_logger.LogWarning(ex, "写入历史报警失败");
|
||||
|
||||
var cacheDatas = list.ChunkTrivialBetter(500);
|
||||
foreach (var a in cacheDatas)
|
||||
{
|
||||
await CacheDb.AddCacheData("", a.ToJson(), 50000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (isSuccess)
|
||||
_logger?.LogWarning($"历史报警循环异常:" + ex.Message);
|
||||
StatuString = new OperResult(ex);
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
IsExited = true;
|
||||
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
IsExited = true;
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
IsExited = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsExited = true;
|
||||
_logger?.LogError($"历史报警异常:" + ex.Message);
|
||||
}
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -1,770 +0,0 @@
|
||||
#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.FriendlyException;
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Application.Extensions;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Foundation.Extension.Byte;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备子线程服务
|
||||
/// </summary>
|
||||
public class CollectDeviceCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 特殊方法变量
|
||||
/// </summary>
|
||||
public List<DeviceVariableMethodSource> DeviceVariableMethodReads = new();
|
||||
/// <summary>
|
||||
/// 特殊方法变量,不参与轮询执行
|
||||
/// </summary>
|
||||
public List<DeviceVariableMethodSource> DeviceVariableMethodSources = new();
|
||||
/// <summary>
|
||||
/// 变量打包
|
||||
/// </summary>
|
||||
public List<DeviceVariableSourceRead> DeviceVariableSourceReads = new();
|
||||
/// <summary>
|
||||
/// 全局插件服务
|
||||
/// </summary>
|
||||
private readonly PluginSingletonService _pluginService;
|
||||
/// <summary>
|
||||
/// 读写锁
|
||||
/// </summary>
|
||||
private readonly EasyLock easyLock = new();
|
||||
/// <summary>
|
||||
/// 当前设备信息
|
||||
/// </summary>
|
||||
private CollectDeviceRunTime _device;
|
||||
|
||||
/// <summary>
|
||||
/// 当前的驱动插件实例
|
||||
/// </summary>
|
||||
private CollectBase _driver;
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
private ILogger _logger;
|
||||
/// <summary>
|
||||
/// 是否初始化成功
|
||||
/// </summary>
|
||||
private bool isInitSuccess = true;
|
||||
|
||||
/// <inheritdoc cref="CollectDeviceCore"/>
|
||||
public CollectDeviceCore()
|
||||
{
|
||||
_pluginService = ServiceHelper.Services.GetService<PluginSingletonService>();
|
||||
GlobalDeviceData = ServiceHelper.Services.GetService<GlobalDeviceData>();
|
||||
DriverPluginService = ServiceHelper.Services.GetService<IDriverPluginService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备
|
||||
/// </summary>
|
||||
public CollectDeviceRunTime Device => _device;
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备Id
|
||||
/// </summary>
|
||||
public long DeviceId => (long)(_device?.Id.ToLong());
|
||||
|
||||
/// <summary>
|
||||
/// 当前插件
|
||||
/// </summary>
|
||||
public CollectBase Driver => _driver;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化成功
|
||||
/// </summary>
|
||||
public bool IsInitSuccess => isInitSuccess;
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
public ILogger Logger => _logger;
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备全部特殊方法,执行初始化后获取正确值
|
||||
/// </summary>
|
||||
public List<MethodInfo> Methods { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备全部设备属性,执行初始化后获取正确值
|
||||
/// </summary>
|
||||
public List<DependencyProperty> Propertys { get; private set; }
|
||||
|
||||
private IDriverPluginService DriverPluginService { get; set; }
|
||||
|
||||
private GlobalDeviceData GlobalDeviceData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 暂停采集
|
||||
/// </summary>
|
||||
public void PasueThread(bool keepRun)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
var str = keepRun == false ? "设备线程采集暂停" : "设备线程采集继续";
|
||||
_logger?.LogInformation($"{str}:{_device.Name}");
|
||||
this.Device.KeepRun = keepRun;
|
||||
}
|
||||
}
|
||||
|
||||
#region 插件处理
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private CollectBase CreatDriver()
|
||||
{
|
||||
|
||||
var driverPlugin = DriverPluginService.GetDriverPluginById(_device.PluginId);
|
||||
if (driverPlugin != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_driver = (CollectBase)_pluginService.GetDriver(driverPlugin);
|
||||
if (_driver == null)
|
||||
{
|
||||
throw Oops.Oh($"创建插件失败");
|
||||
}
|
||||
Methods = _pluginService.GetMethod(_driver);
|
||||
Propertys = _pluginService.GetDriverProperties(_driver);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw Oops.Oh($"创建插件失败:{ex.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Oops.Oh($"找不到驱动{driverPlugin?.AssembleName}");
|
||||
}
|
||||
//设置插件配置项
|
||||
SetPluginProperties(_device.DevicePropertys);
|
||||
return _driver;
|
||||
|
||||
}
|
||||
|
||||
private void InitDriver(object client)
|
||||
{
|
||||
//初始化插件
|
||||
_driver.Init(_logger, _device, client);
|
||||
//变量打包
|
||||
LoadSourceReads(_device.DeviceVariableRunTimes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置驱动插件的属性值
|
||||
/// </summary>
|
||||
private void SetPluginProperties(List<DependencyProperty> deviceProperties)
|
||||
{
|
||||
if (deviceProperties == null) return;
|
||||
_pluginService.SetDriverProperties(_driver, deviceProperties);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 核心读写
|
||||
|
||||
/// <summary>
|
||||
/// 是否多个设备共享链路,由外部传入
|
||||
/// </summary>
|
||||
public bool IsShareChannel;
|
||||
|
||||
/// <summary>
|
||||
/// 线程开始时执行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal async Task BeforeActionAsync(CancellationToken token, object client = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_device == null)
|
||||
{
|
||||
_logger?.LogError(nameof(CollectDeviceRunTime) + "设备不能为null");
|
||||
isInitSuccess = false;
|
||||
return;
|
||||
}
|
||||
if (_driver == null)
|
||||
{
|
||||
_logger?.LogWarning(_device.Name + " - 插件不能为null");
|
||||
isInitSuccess = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_logger?.LogInformation($"{_device.Name}采集设备线程开始");
|
||||
|
||||
InitDriver(client);
|
||||
Device.SourceVariableCount = DeviceVariableSourceReads.Count;
|
||||
Device.MethodVariableCount = DeviceVariableMethodReads.Count;
|
||||
|
||||
try
|
||||
{
|
||||
if (Device.KeepRun == true)
|
||||
{
|
||||
await _driver.BeforStartAsync(token);
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, _device.Name);
|
||||
Device.SetDeviceStatus(null, Device.ErrorCount + 1, ex.Message);
|
||||
}
|
||||
isInitSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, _device.Name);
|
||||
isInitSuccess = false;
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime, 999, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束后
|
||||
/// </summary>
|
||||
internal async Task FinishActionAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation($"{_device.Name}采集线程停止中");
|
||||
await _driver?.AfterStopAsync();
|
||||
|
||||
_driver?.SafeDispose();
|
||||
_logger?.LogInformation($"{_device.Name}采集线程已停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, $"{Device.Name} 释放失败");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInitSuccess = false;
|
||||
GlobalDeviceData.CollectDevices.RemoveWhere(it => it.Id == Device.Id);
|
||||
easyLock.SafeDispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
internal bool Init(CollectDeviceRunTime device)
|
||||
{
|
||||
if (device == null)
|
||||
{
|
||||
_logger?.LogError(nameof(CollectDeviceRunTime) + "设备不能为null");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
bool isUpDevice = Device != device;
|
||||
_device = device;
|
||||
_logger = ServiceHelper.Services.GetService<ILoggerFactory>().CreateLogger("采集设备:" + _device.Name);
|
||||
//更新插件信息
|
||||
CreatDriver();
|
||||
//全局数据更新
|
||||
if (isUpDevice)
|
||||
{
|
||||
GlobalDeviceData.CollectDevices.RemoveWhere(it => it.Id == device.Id);
|
||||
GlobalDeviceData.CollectDevices.Add(device);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, device.Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行一次读取
|
||||
/// </summary>
|
||||
internal async Task<ThreadRunReturn> RunActionAsync(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_device == null)
|
||||
{
|
||||
_logger?.LogError(nameof(CollectDeviceRunTime) + "设备不能为null");
|
||||
return ThreadRunReturn.Continue;
|
||||
}
|
||||
if (_driver == null)
|
||||
{
|
||||
_logger?.LogWarning(_device.Name + " - 插件不能为null");
|
||||
return ThreadRunReturn.Continue;
|
||||
}
|
||||
|
||||
if (Device.KeepRun == false)
|
||||
{
|
||||
//采集暂停
|
||||
return ThreadRunReturn.Continue;
|
||||
}
|
||||
|
||||
if (DeviceVariableSourceReads.Count == 0 && Device.DeviceVariableRunTimes.Where(a => string.IsNullOrEmpty(a.OtherMethod)).Any())
|
||||
{
|
||||
//无采集变量
|
||||
return ThreadRunReturn.Continue;
|
||||
}
|
||||
if (token.IsCancellationRequested)
|
||||
return ThreadRunReturn.Break;
|
||||
|
||||
int deviceMethodsVariableSuccessNum = 0;
|
||||
int deviceMethodsVariableFailedNum = 0;
|
||||
int deviceSourceVariableSuccessNum = 0;
|
||||
int deviceSourceVariableFailedNum = 0;
|
||||
|
||||
//支持读取
|
||||
if (_driver.IsSupportRequest)
|
||||
{
|
||||
foreach (var deviceVariableSourceRead in DeviceVariableSourceReads)
|
||||
{
|
||||
if (Device?.KeepRun == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
//连读变量
|
||||
if (deviceVariableSourceRead.CheckIfRequestAndUpdateTime(SysDateTimeExtensions.CurrentDateTime))
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
var read = await _driver.ReadSourceAsync(deviceVariableSourceRead, token);
|
||||
if (read != null && read.IsSuccess)
|
||||
{
|
||||
_logger?.LogTrace(_device.Name + " - " + " - 采集[" + deviceVariableSourceRead.Address + " - " + deviceVariableSourceRead.Length + "] 数据成功" + read.Content?.ToHexString(" "));
|
||||
deviceVariableSourceRead.LastSuccess = true;
|
||||
deviceSourceVariableSuccessNum += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogWarning(_device.Name + " - " + " - 采集[" + deviceVariableSourceRead.Address + " -" + deviceVariableSourceRead.Length + "] 数据失败 - " + read?.Message);
|
||||
deviceVariableSourceRead.LastSuccess = false;
|
||||
deviceSourceVariableFailedNum += 1;
|
||||
Device.SetDeviceStatus(null, Device.ErrorCount + deviceSourceVariableFailedNum, read?.Message);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var deviceVariableMethodRead in DeviceVariableMethodReads)
|
||||
{
|
||||
if (Device?.KeepRun == false)
|
||||
continue;
|
||||
if (token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
//连读变量
|
||||
if (deviceVariableMethodRead.CheckIfRequestAndUpdateTime(SysDateTimeExtensions.CurrentDateTime))
|
||||
{
|
||||
|
||||
var read = await InvokeMethodAsync(deviceVariableMethodRead, token);
|
||||
if (read.IsSuccess)
|
||||
{
|
||||
_logger?.LogTrace(_device.Name + "执行方法[" + deviceVariableMethodRead.MethodInfo.Name + "] 成功" + read.Content.ToJson());
|
||||
deviceMethodsVariableSuccessNum += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogWarning(_device.Name + "执行方法[" + deviceVariableMethodRead.MethodInfo.Name + "] 失败" + read?.Message);
|
||||
deviceMethodsVariableFailedNum += 1;
|
||||
Device.SetDeviceStatus(null, Device.ErrorCount + deviceSourceVariableFailedNum, read.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (deviceMethodsVariableFailedNum == 0 && deviceSourceVariableFailedNum == 0 && (deviceMethodsVariableSuccessNum != 0 || deviceSourceVariableSuccessNum != 0))
|
||||
{
|
||||
//只有成功读取一次,失败次数都会清零
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (deviceMethodsVariableFailedNum == 0 && deviceSourceVariableFailedNum == 0 && deviceMethodsVariableSuccessNum == 0 && deviceSourceVariableSuccessNum == 0)
|
||||
{
|
||||
//这次没有执行读取
|
||||
//判断是否已连接
|
||||
if (_driver.IsConnected())
|
||||
{
|
||||
//更新设备活动时间
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else //插件自更新设备状态与变量状态
|
||||
{
|
||||
//获取设备连接状态
|
||||
if (_driver.IsConnected())
|
||||
{
|
||||
//更新设备活动时间
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime, 999);
|
||||
}
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
return ThreadRunReturn.Break;
|
||||
|
||||
//正常返回None
|
||||
return ThreadRunReturn.None;
|
||||
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return ThreadRunReturn.Break;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return ThreadRunReturn.Break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, $"采集线程循环异常{_device.Name}");
|
||||
Device.SetDeviceStatus(null, Device.ErrorCount + 1, ex.Message);
|
||||
return ThreadRunReturn.None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备变量打包列表/特殊方法列表
|
||||
/// </summary>
|
||||
/// <param name="collectVariableRunTimes"></param>
|
||||
private void LoadSourceReads(List<DeviceVariableRunTime> collectVariableRunTimes)
|
||||
{
|
||||
if (_device == null)
|
||||
{
|
||||
_logger?.LogError(nameof(CollectDeviceRunTime) + "设备不能为null");
|
||||
return;
|
||||
}
|
||||
if (_driver == null)
|
||||
{
|
||||
_logger?.LogWarning(_device.Name + " - 插件不能为null");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
//连读打包
|
||||
var tags = collectVariableRunTimes.Where(it =>
|
||||
it.ProtectTypeEnum != ProtectTypeEnum.WriteOnly &&
|
||||
string.IsNullOrEmpty(it.OtherMethod)
|
||||
&& !string.IsNullOrEmpty(it.VariableAddress)).ToList();
|
||||
DeviceVariableSourceReads = _driver.LoadSourceRead(tags);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new($"连读打包失败,请查看变量地址是否正确", ex);
|
||||
}
|
||||
var variablesMethod = collectVariableRunTimes.Where(it => !string.IsNullOrEmpty(it.OtherMethod));
|
||||
{
|
||||
var tag = variablesMethod.Where(it => it.ProtectTypeEnum != ProtectTypeEnum.WriteOnly);
|
||||
List<DeviceVariableMethodSource> variablesMethodResult1 = GetMethod(tag, true);
|
||||
DeviceVariableMethodReads = variablesMethodResult1;
|
||||
}
|
||||
|
||||
{
|
||||
var tag = variablesMethod.Where(it => it.ProtectTypeEnum != ProtectTypeEnum.ReadOnly);
|
||||
List<DeviceVariableMethodSource> variablesMethodResult2 = GetMethod(tag, false);
|
||||
DeviceVariableMethodSources = variablesMethodResult2;
|
||||
}
|
||||
|
||||
List<DeviceVariableMethodSource> GetMethod(IEnumerable<DeviceVariableRunTime> tag, bool isRead)
|
||||
{
|
||||
var variablesMethodResult = new List<DeviceVariableMethodSource>();
|
||||
foreach (var item in tag)
|
||||
{
|
||||
var methodResult = new DeviceVariableMethodSource(item.IntervalTime);
|
||||
var method = Methods.FirstOrDefault(it => it.Name == item.OtherMethod);
|
||||
if (method != null)
|
||||
{
|
||||
methodResult.MethodInfo = new Method(method);
|
||||
methodResult.MethodStr = item.VariableAddress;
|
||||
|
||||
if (isRead)
|
||||
{
|
||||
//获取实际执行的参数列表
|
||||
var ps = methodResult.MethodInfo.Info.GetParameters();
|
||||
methodResult.MethodObj = new object[ps.Length];
|
||||
|
||||
if (!string.IsNullOrEmpty(methodResult.MethodStr))
|
||||
{
|
||||
string[] strs = methodResult.MethodStr?.Trim()?.Split(';');
|
||||
try
|
||||
{
|
||||
int index = 0;
|
||||
for (int i = 0; i < ps.Length; i++)
|
||||
{
|
||||
if (typeof(CancellationToken).IsAssignableFrom(ps[i].ParameterType))
|
||||
{
|
||||
methodResult.HasTokenObj = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//得到对于的方法参数值
|
||||
methodResult.MethodObj[i] = methodResult.Converter.ConvertFrom(strs[index], ps[i].ParameterType);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new($"特殊方法初始化失败,请查看变量地址/传入参数是否正确", ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
methodResult.DeviceVariable = item;
|
||||
variablesMethodResult.Add(methodResult);
|
||||
}
|
||||
}
|
||||
|
||||
return variablesMethodResult;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
#region 写入与方法
|
||||
|
||||
/// <summary>
|
||||
/// 执行特殊方法
|
||||
/// </summary>
|
||||
internal async Task<OperResult<string>> InvokeMethodAsync(DeviceVariableMethodSource deviceVariableMethodSource, bool isRead, string value, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
OperResult<string> result = new();
|
||||
var method = deviceVariableMethodSource.MethodInfo;
|
||||
if (method == null)
|
||||
{
|
||||
result.ResultCode = ResultCode.Error;
|
||||
result.Message = $"{deviceVariableMethodSource.DeviceVariable.Name}找不到执行方法{deviceVariableMethodSource.DeviceVariable.OtherMethod}";
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (!isRead)
|
||||
{
|
||||
//获取执行参数
|
||||
var ps = method.Info.GetParameters();
|
||||
deviceVariableMethodSource.MethodObj = new object[ps.Length];
|
||||
|
||||
if (!string.IsNullOrEmpty(deviceVariableMethodSource.MethodStr) || !string.IsNullOrEmpty(value))
|
||||
{
|
||||
string[] strs1 = deviceVariableMethodSource.MethodStr?.Trim()?.Split(';');
|
||||
string[] strs2 = value?.Trim()?.Split(';');
|
||||
//通过分号分割,并且合并参数
|
||||
var strs = strs1?.SpliceArray(strs2);
|
||||
int index = 0;
|
||||
for (int i = 0; i < ps.Length; i++)
|
||||
{
|
||||
if (typeof(CancellationToken).IsAssignableFrom(ps[i].ParameterType))
|
||||
{
|
||||
deviceVariableMethodSource.MethodObj[i] = token;
|
||||
}
|
||||
else
|
||||
{
|
||||
//得到对于的方法参数值
|
||||
deviceVariableMethodSource.MethodObj[i] = deviceVariableMethodSource.Converter.ConvertFrom(strs[index], ps[i].ParameterType);
|
||||
index++;
|
||||
if (strs.Length <= i)
|
||||
{
|
||||
result.ResultCode = ResultCode.Error;
|
||||
result.Message = $"{deviceVariableMethodSource.DeviceVariable.Name} 执行方法 {deviceVariableMethodSource.DeviceVariable.OtherMethod} 参数不足{deviceVariableMethodSource.MethodStr}";
|
||||
//参数数量不符
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (deviceVariableMethodSource.HasTokenObj)
|
||||
{
|
||||
//获取执行参数
|
||||
var ps = method.Info.GetParameters();
|
||||
var newObjs = deviceVariableMethodSource.MethodObj.ToList();
|
||||
if (!string.IsNullOrEmpty(deviceVariableMethodSource.MethodStr) || !string.IsNullOrEmpty(value))
|
||||
{
|
||||
for (int i = 0; i < ps.Length; i++)
|
||||
{
|
||||
if (typeof(CancellationToken).IsAssignableFrom(ps[i].ParameterType))
|
||||
{
|
||||
newObjs.Insert(i, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
deviceVariableMethodSource.MethodObj = newObjs.ToArray();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
object data = null;
|
||||
switch (method.TaskType)
|
||||
{
|
||||
case TaskReturnType.None:
|
||||
data = method.Invoke(_driver, deviceVariableMethodSource.MethodObj);
|
||||
break;
|
||||
case TaskReturnType.Task:
|
||||
await method.InvokeAsync(_driver, deviceVariableMethodSource.MethodObj);
|
||||
break;
|
||||
case TaskReturnType.TaskObject:
|
||||
//执行方法
|
||||
data = await method.InvokeObjectAsync(_driver, deviceVariableMethodSource.MethodObj);
|
||||
break;
|
||||
}
|
||||
|
||||
result = data?.Adapt<OperResult<string>>();
|
||||
if (method.HasReturn && result != null && result.IsSuccess)
|
||||
{
|
||||
var content = deviceVariableMethodSource.Converter.ConvertTo(result.Content?.ToString()?.Replace($"\0", ""));
|
||||
var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(content);
|
||||
if (!operResult.IsSuccess)
|
||||
{
|
||||
_logger?.LogWarning(operResult.Message, ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(null);
|
||||
if (!operResult.IsSuccess)
|
||||
{
|
||||
_logger?.LogWarning(operResult.Message, ToString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.ResultCode = ResultCode.Error;
|
||||
result.Message = $"{deviceVariableMethodSource.DeviceVariable.Name}执行{deviceVariableMethodSource.DeviceVariable.OtherMethod} 方法失败:{ex.Message}";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (new OperResult<string>(ex));
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行变量写入
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal async Task<OperResult> InVokeWriteAsync(DeviceVariableRunTime deviceVariable, JToken value, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
if (IsShareChannel) _driver.InitDataAdapter();
|
||||
|
||||
if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions))
|
||||
{
|
||||
var jToken = value;
|
||||
object rawdata;
|
||||
if (jToken is JValue jValue)
|
||||
{
|
||||
rawdata = jValue.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
rawdata = jToken.ToString();
|
||||
}
|
||||
object data;
|
||||
try
|
||||
{
|
||||
data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata);
|
||||
var result = await _driver.WriteValueAsync(deviceVariable, JToken.FromObject(data), token);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(deviceVariable.Name + " 转换写入表达式失败:" + ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await _driver.WriteValueAsync(deviceVariable, value, token);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (new OperResult(ex));
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行轮询特殊方法,并设置变量值
|
||||
/// </summary>
|
||||
private async Task<OperResult<string>> InvokeMethodAsync(DeviceVariableMethodSource deviceVariableMethodRead, CancellationToken token)
|
||||
{
|
||||
var data = await InvokeMethodAsync(deviceVariableMethodRead, true, string.Empty, token);
|
||||
return data;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// 采集设备线程管理
|
||||
/// </summary>
|
||||
public class CollectDeviceThread : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 链路标识
|
||||
/// </summary>
|
||||
public readonly string ChangelID;
|
||||
|
||||
/// <summary>
|
||||
/// CancellationTokenSources
|
||||
/// </summary>
|
||||
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
|
||||
|
||||
/// <summary>
|
||||
/// 线程
|
||||
/// </summary>
|
||||
protected Task DeviceTask;
|
||||
|
||||
/// <summary>
|
||||
/// 启停锁
|
||||
/// </summary>
|
||||
protected EasyLock easyLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public CollectDeviceThread(string changelID)
|
||||
{
|
||||
ChangelID = changelID;
|
||||
}
|
||||
/// <summary>
|
||||
/// 默认等待间隔时间
|
||||
/// </summary>
|
||||
public static int CycleInterval { get; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 采集设备List,在CollectDeviceThread开始前应该初始化内容
|
||||
/// </summary>
|
||||
public ConcurrentList<CollectDeviceCore> CollectDeviceCores { get; private set; } = new();
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await StopThreadAsync();
|
||||
easyLock.SafeDispose();
|
||||
CollectDeviceCores.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始采集
|
||||
/// </summary>
|
||||
public virtual async Task StartThreadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
|
||||
StoppingTokens.Add(new());
|
||||
//初始化采集线程
|
||||
await InitTaskAsync();
|
||||
if (DeviceTask.Status == TaskStatus.Created)
|
||||
DeviceTask?.Start();
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 停止采集前,提前取消Token
|
||||
/// </summary>
|
||||
public virtual async Task BeforeStopThreadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
|
||||
if (DeviceTask == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止采集
|
||||
/// </summary>
|
||||
public virtual async Task StopThreadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
|
||||
if (DeviceTask == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
try
|
||||
{
|
||||
await DeviceTask.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
foreach (var device in CollectDeviceCores)
|
||||
{
|
||||
device.Logger?.LogInformation($"{device.Device.Name}采集线程停止超时,已强制取消");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CollectDeviceCores.FirstOrDefault()?.Logger?.LogError(ex, $"{CollectDeviceCores.FirstOrDefault()?.Device?.Name}采集线程停止错误");
|
||||
}
|
||||
foreach (CancellationTokenSource token in StoppingTokens)
|
||||
{
|
||||
token?.SafeDispose();
|
||||
}
|
||||
DeviceTask?.SafeDispose();
|
||||
DeviceTask = null;
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
protected async Task InitTaskAsync()
|
||||
{
|
||||
CancellationTokenSource stoppingToken = StoppingTokens.Last();
|
||||
DeviceTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
var channelResult = CollectDeviceCores.FirstOrDefault().Driver.GetShareChannel();
|
||||
LoggerGroup log = CollectDeviceCores.FirstOrDefault().Driver.LogMessage;
|
||||
foreach (var device in CollectDeviceCores)
|
||||
{
|
||||
if (device.Driver == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//添加通道报文到每个设备
|
||||
var data = new EasyLogger(device.Driver.NewMessage) { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
log.AddLogger(data);
|
||||
//传入是否共享通道
|
||||
device.IsShareChannel = CollectDeviceCores.Count > 1;
|
||||
if (channelResult.IsSuccess)
|
||||
{
|
||||
await device.BeforeActionAsync(stoppingToken.Token, channelResult.Content);
|
||||
}
|
||||
else
|
||||
{
|
||||
await device.BeforeActionAsync(stoppingToken.Token);
|
||||
}
|
||||
}
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
foreach (var device in CollectDeviceCores)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested)
|
||||
break;
|
||||
//初始化成功才能执行
|
||||
if (device.IsInitSuccess)
|
||||
{
|
||||
//如果是共享通道类型,需要每次转换时切换适配器
|
||||
if (device.IsShareChannel) device.Driver.InitDataAdapter();
|
||||
|
||||
var result = await device.RunActionAsync(stoppingToken.Token);
|
||||
if (result == ThreadRunReturn.None)
|
||||
{
|
||||
await Task.Delay(CycleInterval);
|
||||
}
|
||||
else if (result == ThreadRunReturn.Continue)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
else if (result == ThreadRunReturn.Break)
|
||||
{
|
||||
//当线程返回Break,直接跳出循环
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken.Token);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
//注意插件结束函数不能使用取消传播作为条件
|
||||
foreach (var device in CollectDeviceCores)
|
||||
{
|
||||
//如果插件还没释放,执行一次结束函数
|
||||
if (!device.Driver.DisposedValue)
|
||||
await device.FinishActionAsync();
|
||||
}
|
||||
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
@@ -1,567 +0,0 @@
|
||||
#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.FriendlyException;
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Application.Extensions;
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备采集后台服务
|
||||
/// </summary>
|
||||
public class CollectDeviceWorker : BackgroundService
|
||||
{
|
||||
private readonly ICollectDeviceService _collectDeviceService;
|
||||
private readonly ILogger<CollectDeviceWorker> _logger;
|
||||
private readonly PluginSingletonService _pluginService;
|
||||
/// <inheritdoc/>
|
||||
public CollectDeviceWorker(ILogger<CollectDeviceWorker> logger, IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceHelper.Services = serviceProvider;
|
||||
_logger = logger;
|
||||
_pluginService = ServiceHelper.Services.GetService<PluginSingletonService>();
|
||||
_collectDeviceService = ServiceHelper.Services.GetService<ICollectDeviceService>();
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取未停止的采集设备List
|
||||
/// </summary>
|
||||
public List<CollectDeviceCore> CollectDeviceCores => CollectDeviceThreads
|
||||
.Where(a => !a.StoppingTokens.Any(b => b.IsCancellationRequested))
|
||||
.SelectMany(a => a.CollectDeviceCores).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// 设备子线程列表
|
||||
/// </summary>
|
||||
private ConcurrentList<CollectDeviceThread> CollectDeviceThreads { get; set; } = new();
|
||||
|
||||
#region 设备创建更新结束
|
||||
/// <summary>
|
||||
/// 全部重启锁
|
||||
/// </summary>
|
||||
private readonly EasyLock restartLock = new();
|
||||
/// <summary>
|
||||
/// 单个重启锁
|
||||
/// </summary>
|
||||
private readonly EasyLock singleRestartLock = new();
|
||||
/// <summary>
|
||||
/// 控制设备线程启停
|
||||
/// </summary>
|
||||
/// <param name="deviceId">传入0时全部设备都会执行</param>
|
||||
/// <param name="isStart"></param>
|
||||
/// <returns></returns>
|
||||
public void ConfigDeviceThread(long deviceId, bool isStart)
|
||||
{
|
||||
if (deviceId == 0)
|
||||
CollectDeviceCores.ForEach(a => a.PasueThread(isStart));
|
||||
else
|
||||
CollectDeviceCores.FirstOrDefault(it => it.DeviceId == deviceId)?.PasueThread(isStart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重启采集服务
|
||||
/// </summary>
|
||||
public async Task RestartDeviceThreadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
await singleRestartLock.WaitAsync();
|
||||
//停止其他后台服务
|
||||
await StopOtherHostService();
|
||||
//停止全部采集线程
|
||||
await RemoveAllDeviceThreadAsync();
|
||||
//创建全部采集线程
|
||||
await CreatAllDeviceThreadsAsync();
|
||||
//开始全部采集线程
|
||||
await StartAllDeviceThreadsAsync();
|
||||
//开始其他后台服务
|
||||
await StartOtherHostService();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
singleRestartLock.Release();
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 更新设备线程,切换为冗余通道
|
||||
/// </summary>
|
||||
public async Task UpDeviceRedundantThreadAsync(long devId)
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (singleRestartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await singleRestartLock.WaitAsync();
|
||||
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var devThread = CollectDeviceThreads.FirstOrDefault(it => it.CollectDeviceCores.Any(a => a.DeviceId == devId));
|
||||
var devCore = devThread.CollectDeviceCores.FirstOrDefault(a => a.DeviceId == devId);
|
||||
if (devThread == null) { throw Oops.Bah($"更新设备线程失败,不存在{devId}为id的设备"); }
|
||||
//这里先停止采集,操作会使线程取消,需要重新恢复线程
|
||||
await devThread.StopThreadAsync();
|
||||
|
||||
var dev = devCore.Device;
|
||||
|
||||
if (dev.IsRedundant)
|
||||
{
|
||||
if (dev.Redundant == RedundantEnum.Standby)
|
||||
{
|
||||
var newDev = (await _collectDeviceService.GetCollectDeviceRuntimeAsync(devId)).FirstOrDefault();
|
||||
if (dev == null)
|
||||
{
|
||||
_logger.LogError($"更新设备线程失败,不存在{devId}为id的设备");
|
||||
}
|
||||
else
|
||||
{
|
||||
dev.DevicePropertys = newDev.DevicePropertys;
|
||||
dev.Redundant = RedundantEnum.Primary;
|
||||
_logger?.LogInformation(dev.Name + "切换到主通道");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var Redundantdev = (await _collectDeviceService.GetCollectDeviceRuntimeAsync(dev.RedundantDeviceId)).FirstOrDefault();
|
||||
if (Redundantdev == null)
|
||||
{
|
||||
_logger.LogError($"更新设备线程失败,不存在{devId}为id的设备");
|
||||
}
|
||||
else
|
||||
{
|
||||
dev.DevicePropertys = Redundantdev.DevicePropertys;
|
||||
dev.Redundant = RedundantEnum.Standby;
|
||||
_logger?.LogInformation(dev.Name + "切换到备用通道");
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//初始化
|
||||
devCore.Init(dev);
|
||||
//线程管理器移除后,如果不存在其他设备,也删除线程管理器
|
||||
devThread.CollectDeviceCores.Remove(devCore);
|
||||
if (devThread.CollectDeviceCores.Count == 0)
|
||||
{
|
||||
CollectDeviceThreads.Remove(devThread);
|
||||
}
|
||||
//需判断是否同一通道
|
||||
var newDevThread = DeviceThread(devCore);
|
||||
await newDevThread.StartThreadAsync();
|
||||
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
singleRestartLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新设备线程
|
||||
/// </summary>
|
||||
public async Task UpDeviceThreadAsync(long devId, bool isUpdateDb = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (singleRestartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await singleRestartLock.WaitAsync();
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
//如果是组态更改过了,需要重新获取变量/设备运行态的值,其他服务需要先停止
|
||||
if (isUpdateDb)
|
||||
await StopOtherHostService();
|
||||
var devThread = CollectDeviceThreads.FirstOrDefault(it => it.CollectDeviceCores.Any(a => a.DeviceId == devId));
|
||||
var devCore = devThread.CollectDeviceCores.FirstOrDefault(a => a.DeviceId == devId);
|
||||
if (devThread == null) { throw Oops.Bah($"更新设备线程失败,不存在{devId}为id的设备"); }
|
||||
//这里先停止采集,操作会使线程取消,需要重新恢复线程
|
||||
await devThread.StopThreadAsync();
|
||||
|
||||
CollectDeviceRunTime dev = isUpdateDb ? (await _collectDeviceService.GetCollectDeviceRuntimeAsync(devId)).FirstOrDefault() : devCore.Device;
|
||||
|
||||
if (dev == null)
|
||||
{
|
||||
//线程管理器移除后,如果不存在其他设备,也删除线程管理器
|
||||
devThread.CollectDeviceCores.Remove(devCore);
|
||||
if (devThread.CollectDeviceCores.Count == 0)
|
||||
{
|
||||
CollectDeviceThreads.Remove(devThread);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//初始化
|
||||
devCore.Init(dev);
|
||||
//线程管理器移除后,如果不存在其他设备,也删除线程管理器
|
||||
devThread.CollectDeviceCores.Remove(devCore);
|
||||
|
||||
if (devThread.CollectDeviceCores.Count == 0)
|
||||
{
|
||||
CollectDeviceThreads.Remove(devThread);
|
||||
}
|
||||
|
||||
//需判断是否同一通道
|
||||
var newDevThread = DeviceThread(devCore);
|
||||
await newDevThread.StartThreadAsync();
|
||||
|
||||
//如果是组态更改过了,需要重新获取变量/设备运行态的值
|
||||
if (isUpdateDb)
|
||||
await StartOtherHostService();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
singleRestartLock.Release();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private
|
||||
/// <summary>
|
||||
/// 创建设备采集线程
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task CreatAllDeviceThreadsAsync()
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
_logger.LogInformation("正在获取采集组态信息");
|
||||
var collectDeviceRunTimes = (await _collectDeviceService.GetCollectDeviceRuntimeAsync());
|
||||
_logger.LogInformation("获取采集组态信息完成");
|
||||
foreach (var collectDeviceRunTime in collectDeviceRunTimes.Where(a => !collectDeviceRunTimes.Any(b => a.Id == b.RedundantDeviceId && b.IsRedundant)))
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
CollectDeviceCore deviceCollectCore = new();
|
||||
deviceCollectCore.Init(collectDeviceRunTime);
|
||||
DeviceThread(deviceCollectCore);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, collectDeviceRunTime.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据通道生成/获取线程管理器
|
||||
/// </summary>
|
||||
/// <param name="deviceCollectCore"></param>
|
||||
/// <returns></returns>
|
||||
private CollectDeviceThread DeviceThread(CollectDeviceCore deviceCollectCore)
|
||||
{
|
||||
if (deviceCollectCore.Driver == null)
|
||||
return null;
|
||||
var changelID = deviceCollectCore.Driver.GetChannelID();
|
||||
if (changelID.IsSuccess)
|
||||
{
|
||||
foreach (var collectDeviceThread in CollectDeviceThreads)
|
||||
{
|
||||
if (collectDeviceThread.ChangelID == changelID.Content)
|
||||
{
|
||||
collectDeviceThread.CollectDeviceCores.Add(deviceCollectCore);
|
||||
return collectDeviceThread;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NewDeviceThread(deviceCollectCore, changelID.Content);
|
||||
|
||||
CollectDeviceThread NewDeviceThread(CollectDeviceCore deviceCollectCore, string changelID)
|
||||
{
|
||||
CollectDeviceThread deviceThread = new(changelID);
|
||||
deviceThread.CollectDeviceCores.Add(deviceCollectCore);
|
||||
CollectDeviceThreads.Add(deviceThread);
|
||||
return deviceThread;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除设备线程,并且释放资源
|
||||
/// </summary>
|
||||
private async Task RemoveAllDeviceThreadAsync()
|
||||
{
|
||||
await CollectDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await deviceThread.BeforeStopThreadAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, deviceThread.ToString());
|
||||
}
|
||||
}, 10);
|
||||
|
||||
await CollectDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await deviceThread.DisposeAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, deviceThread.ToString());
|
||||
}
|
||||
}, 10);
|
||||
CollectDeviceThreads.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始设备采集线程
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task StartAllDeviceThreadsAsync()
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
foreach (var item in CollectDeviceThreads)
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await item.StartThreadAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动其他后台服务
|
||||
/// </summary>
|
||||
private async Task StartOtherHostService()
|
||||
{
|
||||
var alarmHostService = ServiceHelper.GetBackgroundService<AlarmWorker>();
|
||||
var historyValueService = ServiceHelper.GetBackgroundService<HistoryValueWorker>();
|
||||
var uploadDeviceHostService = ServiceHelper.GetBackgroundService<UploadDeviceWorker>();
|
||||
var memoryVariableWorker = ServiceHelper.GetBackgroundService<MemoryVariableWorker>();
|
||||
await alarmHostService.StartAsync();
|
||||
await historyValueService.StartAsync();
|
||||
await uploadDeviceHostService.StartAsync();
|
||||
await memoryVariableWorker.StartAsync();
|
||||
}
|
||||
/// <summary>
|
||||
/// 停止其他后台服务
|
||||
/// </summary>
|
||||
private async Task StopOtherHostService()
|
||||
{
|
||||
var alarmHostService = ServiceHelper.GetBackgroundService<AlarmWorker>();
|
||||
var historyValueService = ServiceHelper.GetBackgroundService<HistoryValueWorker>();
|
||||
var uploadDeviceHostService = ServiceHelper.GetBackgroundService<UploadDeviceWorker>();
|
||||
var memoryVariableWorker = ServiceHelper.GetBackgroundService<MemoryVariableWorker>();
|
||||
await alarmHostService.StopAsync();
|
||||
await historyValueService.StopAsync();
|
||||
await uploadDeviceHostService.StopAsync();
|
||||
await memoryVariableWorker.StopAsync();
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 设备信息获取
|
||||
/// <summary>
|
||||
/// GetDebugUI
|
||||
/// </summary>
|
||||
/// <param name="driverId"></param>
|
||||
/// <returns></returns>
|
||||
public Type GetDebugUI(long driverId)
|
||||
{
|
||||
var driverPluginService = ServiceHelper.Services.GetService<IDriverPluginService>();
|
||||
var driverPlugin = driverPluginService.GetDriverPluginById(driverId);
|
||||
var driver = _pluginService.GetDriver(driverPlugin);
|
||||
driver?.SafeDispose();
|
||||
return driver.DriverDebugUIType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备方法
|
||||
/// </summary>
|
||||
/// <param name="devId"></param>
|
||||
/// <returns></returns>
|
||||
public List<string> GetDeviceMethods(long devId)
|
||||
{
|
||||
var driverPluginService = ServiceHelper.Services.GetService<IDriverPluginService>();
|
||||
var driverId = _collectDeviceService.GetDeviceById(devId).PluginId;
|
||||
var driverPlugin = driverPluginService.GetDriverPluginById(driverId);
|
||||
var driver = (CollectBase)_pluginService.GetDriver(driverPlugin);
|
||||
var Propertys = _pluginService.GetMethod(driver);
|
||||
driver?.SafeDispose();
|
||||
return Propertys.Select(it => it.Name).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备属性
|
||||
/// </summary>
|
||||
/// <param name="driverId"></param>
|
||||
/// <param name="devId"></param>
|
||||
/// <returns></returns>
|
||||
public List<DependencyProperty> GetDevicePropertys(long driverId, long devId = 0)
|
||||
{
|
||||
var driverPluginService = ServiceHelper.Services.GetService<IDriverPluginService>();
|
||||
var driverPlugin = driverPluginService.GetDriverPluginById(driverId);
|
||||
var driver = _pluginService.GetDriver(driverPlugin);
|
||||
var Propertys = _pluginService.GetDriverProperties(driver);
|
||||
if (devId != 0)
|
||||
{
|
||||
var devcore = CollectDeviceCores.FirstOrDefault(it => it.Device.Id == devId);
|
||||
devcore?.Device?.DevicePropertys?.ForEach(it =>
|
||||
{
|
||||
var dependencyProperty = Propertys.FirstOrDefault(a => a.PropertyName == it.PropertyName);
|
||||
if (dependencyProperty != null)
|
||||
{
|
||||
dependencyProperty.Value = it.Value;
|
||||
}
|
||||
});
|
||||
}
|
||||
driver?.SafeDispose();
|
||||
return Propertys;
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region worker服务
|
||||
/// <summary>
|
||||
/// 在软件关闭时取消
|
||||
/// </summary>
|
||||
private CancellationToken _stoppingToken;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken token)
|
||||
{
|
||||
var hardwareInfoService = ServiceHelper.Services.GetService<HardwareInfoService>();
|
||||
hardwareInfoService.Init();
|
||||
await base.StartAsync(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StopAsync(CancellationToken token)
|
||||
{
|
||||
using var stoppingToken = new CancellationTokenSource();
|
||||
_stoppingToken = stoppingToken.Token;
|
||||
stoppingToken.Cancel();
|
||||
//停止其他后台服务
|
||||
await StopOtherHostService();
|
||||
//停止全部采集线程
|
||||
await RemoveAllDeviceThreadAsync();
|
||||
await base.StopAsync(token);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
//重启采集线程,会启动其他后台服务
|
||||
await RestartDeviceThreadAsync();
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
//检测设备采集线程假死
|
||||
var num = CollectDeviceCores.Count;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
CollectDeviceCore devcore = CollectDeviceCores[i];
|
||||
if (devcore.Device != null)
|
||||
{
|
||||
//超过30分钟,或者(初始化失败并超过10分钟)会重启
|
||||
if (
|
||||
(devcore.Device.ActiveTime != DateTime.MinValue &&
|
||||
devcore.Device.ActiveTime.AddMinutes(30) <= SysDateTimeExtensions.CurrentDateTime)
|
||||
|| (devcore.IsInitSuccess == false && devcore.Device.ActiveTime.AddMinutes(10) <= SysDateTimeExtensions.CurrentDateTime)
|
||||
)
|
||||
{
|
||||
//如果线程处于暂停状态,跳过
|
||||
if (devcore.Device.DeviceStatus == DeviceStatusEnum.Pause)
|
||||
continue;
|
||||
//如果初始化失败
|
||||
if (!devcore.IsInitSuccess)
|
||||
_logger?.LogWarning(devcore.Device.Name + "初始化失败,重启线程中");
|
||||
else
|
||||
_logger?.LogWarning(devcore.Device.Name + "采集线程假死,重启线程中");
|
||||
//重启线程
|
||||
await UpDeviceThreadAsync(devcore.Device.Id, false);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogTrace(devcore.Device.Name + "线程检测正常");
|
||||
}
|
||||
|
||||
|
||||
if (devcore.Device.DeviceStatus == DeviceStatusEnum.OffLine)
|
||||
{
|
||||
if (devcore.Device.IsRedundant && _collectDeviceService.GetCacheList(false).Any(a => a.Id == devcore.Device.RedundantDeviceId))
|
||||
{
|
||||
await UpDeviceRedundantThreadAsync(devcore.Device.Id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//每5分钟检测一次
|
||||
await Task.Delay(300000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,484 +0,0 @@
|
||||
#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.Logging.Extensions;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
using UAParser;
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 实时数据库后台服务
|
||||
/// </summary>
|
||||
public class HistoryValueWorker : BackgroundService
|
||||
{
|
||||
private readonly GlobalDeviceData _globalDeviceData;
|
||||
private readonly ILogger<HistoryValueWorker> _logger;
|
||||
/// <inheritdoc cref="HistoryValueWorker"/>
|
||||
public HistoryValueWorker(ILogger<HistoryValueWorker> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_globalDeviceData = ServiceHelper.Services.GetService<GlobalDeviceData>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务状态
|
||||
/// </summary>
|
||||
public OperResult StatuString { get; set; } = new OperResult("初始化");
|
||||
|
||||
private ConcurrentQueue<HistoryValue> ChangeDeviceVariables { get; set; } = new();
|
||||
private ConcurrentQueue<HistoryValue> DeviceVariables { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 获取数据库链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<OperResult<SqlSugarClient>> GetHisDbAsync()
|
||||
{
|
||||
var ConfigService = ServiceHelper.Services.GetService<IConfigService>();
|
||||
var hisEnable = (await ConfigService.GetByConfigKeyAsync(ThingsGatewayConfigConst.ThingGateway_HisConfig_Base, ThingsGatewayConfigConst.Config_His_Enable))?.ConfigValue?.ToBoolean();
|
||||
var hisDbType = (await ConfigService.GetByConfigKeyAsync(ThingsGatewayConfigConst.ThingGateway_HisConfig_Base, ThingsGatewayConfigConst.Config_His_DbType))?.ConfigValue;
|
||||
var hisConnstr = (await ConfigService.GetByConfigKeyAsync(ThingsGatewayConfigConst.ThingGateway_HisConfig_Base, ThingsGatewayConfigConst.Config_His_ConnStr))?.ConfigValue;
|
||||
|
||||
if (!(hisEnable == true))
|
||||
{
|
||||
return new OperResult<SqlSugarClient>("历史数据已配置为Disable");
|
||||
}
|
||||
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
DbType type = DbType.QuestDB;
|
||||
if (!string.IsNullOrEmpty(hisDbType))
|
||||
{
|
||||
if (Enum.TryParse<DbType>(hisDbType, ignoreCase: true, out var result))
|
||||
{
|
||||
type = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult<SqlSugarClient>("数据库类型转换失败");
|
||||
}
|
||||
}
|
||||
|
||||
var sqlSugarClient = new SqlSugarClient(new ConnectionConfig()
|
||||
{
|
||||
ConnectionString = hisConnstr,//连接字符串
|
||||
DbType = type,//数据库类型
|
||||
IsAutoCloseConnection = true, //不设成true要手动close
|
||||
ConfigureExternalServices = configureExternalServices,
|
||||
});
|
||||
return OperResult.CreateSuccessResult(sqlSugarClient);
|
||||
}
|
||||
|
||||
#region worker服务
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken token)
|
||||
{
|
||||
_logger?.LogInformation("历史服务启动");
|
||||
await base.StartAsync(token);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken token)
|
||||
{
|
||||
_logger?.LogInformation("历史服务停止");
|
||||
return base.StopAsync(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Delay(5000, stoppingToken);
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(60000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region core
|
||||
/// <summary>
|
||||
/// 循环线程取消标识
|
||||
/// </summary>
|
||||
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
|
||||
/// <summary>
|
||||
/// 全部重启锁
|
||||
/// </summary>
|
||||
private readonly EasyLock restartLock = new();
|
||||
|
||||
private Task HistoryValueTask;
|
||||
private bool IsExited;
|
||||
|
||||
/// <summary>
|
||||
/// 离线缓存
|
||||
/// </summary>
|
||||
protected CacheDb CacheDb { get; set; }
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
private async Task InitAsync()
|
||||
{
|
||||
CacheDb = new("HistoryValueCache");
|
||||
CancellationTokenSource stoppingToken = StoppingTokens.Last();
|
||||
HistoryValueTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
_logger?.LogInformation($"历史数据线程开始");
|
||||
await Task.Yield();//返回线程控制,不再阻塞
|
||||
try
|
||||
{
|
||||
var result = await GetHisDbAsync();
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
_logger?.LogWarning($"历史数据线程即将退出:" + result.Message);
|
||||
StatuString = new OperResult($"已退出:{result.Message}");
|
||||
IsExited = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var sqlSugarClient = result.Content;
|
||||
bool LastIsSuccess = true;
|
||||
/***创建/更新单个表***/
|
||||
try
|
||||
{
|
||||
await sqlSugarClient.Queryable<HistoryValue>().FirstAsync(stoppingToken.Token);
|
||||
LastIsSuccess = true;
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
IsExited = true;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_logger.LogWarning("连接历史数据表失败,尝试初始化表");
|
||||
sqlSugarClient.CodeFirst.InitTables(typeof(HistoryValue));
|
||||
LastIsSuccess = true;
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LastIsSuccess = false;
|
||||
StatuString = new OperResult(ex);
|
||||
_logger.LogWarning(ex, "连接历史数据库失败");
|
||||
}
|
||||
}
|
||||
IsExited = false;
|
||||
|
||||
while (!stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(500, stoppingToken.Token);
|
||||
|
||||
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
//缓存值
|
||||
var cacheData = await CacheDb.GetCacheData();
|
||||
var data = cacheData.SelectMany(a => a.CacheStr.FromJson<List<HistoryValue>>()).ToList();
|
||||
try
|
||||
{
|
||||
var count = await sqlSugarClient.Insertable(data).ExecuteCommandAsync(stoppingToken.Token);
|
||||
await CacheDb.DeleteCacheData(cacheData.Select(a => a.Id).ToArray());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LastIsSuccess)
|
||||
_logger.LogWarning(ex, "写入历史数据失败");
|
||||
}
|
||||
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
var collectList = DeviceVariables.ToListWithDequeue();
|
||||
if (collectList.Count != 0)
|
||||
{
|
||||
////Sql保存
|
||||
var collecthis = collectList;
|
||||
int count = 0;
|
||||
//插入
|
||||
try
|
||||
{
|
||||
count = await sqlSugarClient.Insertable(collecthis).ExecuteCommandAsync(stoppingToken.Token);
|
||||
LastIsSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LastIsSuccess)
|
||||
_logger.LogWarning(ex, "写入历史数据失败");
|
||||
var cacheDatas = collecthis.ChunkTrivialBetter(500);
|
||||
foreach (var a in cacheDatas)
|
||||
{
|
||||
await CacheDb.AddCacheData("", a.ToJson(), 50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
var changeList = ChangeDeviceVariables.ToListWithDequeue();
|
||||
if (changeList.Count != 0)
|
||||
{
|
||||
////Sql保存
|
||||
var changehis = changeList;
|
||||
int count = 0;
|
||||
//插入
|
||||
try
|
||||
{
|
||||
count = await sqlSugarClient.Insertable(changehis).ExecuteCommandAsync(stoppingToken.Token);
|
||||
LastIsSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LastIsSuccess)
|
||||
_logger.LogWarning(ex, "写入历史数据失败");
|
||||
var cacheDatas = changehis.ChunkTrivialBetter(500);
|
||||
foreach (var a in cacheDatas)
|
||||
{
|
||||
await CacheDb.AddCacheData("", a.ToJson(), 50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LastIsSuccess)
|
||||
_logger?.LogWarning(ex, $"历史数据循环异常");
|
||||
StatuString = new OperResult(ex);
|
||||
LastIsSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
IsExited = true;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
IsExited = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsExited = true;
|
||||
_logger?.LogError(ex, $"历史数据循环异常");
|
||||
}
|
||||
IsExited = true;
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重启
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task RestartAsync()
|
||||
{
|
||||
await StopAsync();
|
||||
await StartAsync();
|
||||
}
|
||||
internal async Task StartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
foreach (var device in _globalDeviceData.CollectDevices)
|
||||
{
|
||||
device.DeviceVariableRunTimes?.Where(a => a.HisEnable == true)?.ForEach(v => { v.VariableCollectChange += DeviceVariableCollectChange; });
|
||||
device.DeviceVariableRunTimes?.Where(a => a.HisEnable == true)?.ForEach(v => { v.VariableValueChange += DeviceVariableValueChange; });
|
||||
}
|
||||
StoppingTokens.Add(new());
|
||||
await InitAsync();
|
||||
if (HistoryValueTask.Status == TaskStatus.Created)
|
||||
HistoryValueTask.Start();
|
||||
IsExited = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task StopAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
IsExited = true;
|
||||
|
||||
foreach (var device in _globalDeviceData.CollectDevices)
|
||||
{
|
||||
device.DeviceVariableRunTimes?.Where(a => a.HisEnable == true)?.ForEach(v => { v.VariableCollectChange -= DeviceVariableCollectChange; });
|
||||
device.DeviceVariableRunTimes?.Where(a => a.HisEnable == true)?.ForEach(v => { v.VariableValueChange -= DeviceVariableValueChange; });
|
||||
}
|
||||
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
|
||||
if (HistoryValueTask != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation($"历史数据线程停止中");
|
||||
await HistoryValueTask.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
_logger?.LogInformation($"历史数据线程已停止");
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
_logger?.LogWarning($"历史数据线程停止超时,已强制取消");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, "等待历史数据线程停止错误");
|
||||
}
|
||||
}
|
||||
|
||||
HistoryValueTask?.SafeDispose();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.SafeDispose();
|
||||
}
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
private void DeviceVariableCollectChange(DeviceVariableRunTime variable)
|
||||
{
|
||||
if (variable.HisType == HisType.Collect && !IsExited)
|
||||
{
|
||||
DeviceVariables.Enqueue(variable.Adapt<HistoryValue>());
|
||||
}
|
||||
}
|
||||
private void DeviceVariableValueChange(DeviceVariableRunTime variable)
|
||||
{
|
||||
if (variable.HisType == HisType.Change && !IsExited)
|
||||
{
|
||||
ChangeDeviceVariables.Enqueue(variable.Adapt<HistoryValue>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
/// <summary>
|
||||
/// <see cref="HistoryValue"/> Master规则
|
||||
/// </summary>
|
||||
public class HistoryValueMapper : IRegister
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void Register(TypeAdapterConfig config)
|
||||
{
|
||||
config.ForType<DeviceVariableRunTime, HistoryValue>()
|
||||
.Map(dest => dest.Value, (src) => ValueReturn(src))
|
||||
.Map(dest => dest.CollectTime, (src) => src.CollectTime.ToUniversalTime());//注意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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
#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.Logging.Extensions;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 实时数据库后台服务
|
||||
/// </summary>
|
||||
public class MemoryVariableWorker : BackgroundService
|
||||
{
|
||||
private readonly GlobalDeviceData _globalDeviceData;
|
||||
private readonly ILogger<MemoryVariableWorker> _logger;
|
||||
/// <inheritdoc cref="MemoryVariableWorker"/>
|
||||
public MemoryVariableWorker(ILogger<MemoryVariableWorker> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_globalDeviceData = ServiceHelper.Services.GetService<GlobalDeviceData>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务状态
|
||||
/// </summary>
|
||||
public OperResult StatuString { get; set; } = new OperResult("初始化");
|
||||
|
||||
#region worker服务
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken token)
|
||||
{
|
||||
_logger?.LogInformation("中间变量服务启动");
|
||||
await base.StartAsync(token);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken token)
|
||||
{
|
||||
_logger?.LogInformation("中间变量服务停止");
|
||||
return base.StopAsync(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Delay(5000, stoppingToken);
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(60000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region core
|
||||
/// <summary>
|
||||
/// 循环线程取消标识
|
||||
/// </summary>
|
||||
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
|
||||
private Task MemoryWorkerTask;
|
||||
/// <summary>
|
||||
/// 全部重启锁
|
||||
/// </summary>
|
||||
private readonly EasyLock restartLock = new();
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
public async Task InitAsync()
|
||||
{
|
||||
CancellationTokenSource stoppingToken = StoppingTokens.Last();
|
||||
MemoryWorkerTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
_logger?.LogInformation($"中间变量计算线程开始");
|
||||
try
|
||||
{
|
||||
var variableService = ServiceHelper.Services.GetService<IVariableService>();
|
||||
var data = await variableService.GetMemoryVariableRuntimeAsync();
|
||||
_globalDeviceData.MemoryVariables = new(data);
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
while (!stoppingToken.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(500, stoppingToken.Token);
|
||||
|
||||
if (stoppingToken.Token.IsCancellationRequested)
|
||||
break;
|
||||
var isSuccess = true;
|
||||
foreach (var item in _globalDeviceData.MemoryVariables)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item.ReadExpressions) && item.ProtectTypeEnum != ProtectTypeEnum.WriteOnly)
|
||||
{
|
||||
//变量内部已经做了表达式转换,直接赋值0
|
||||
var operResult = item.SetValue(0);
|
||||
if (!operResult.IsSuccess)
|
||||
{
|
||||
if (StatuString.IsSuccess)
|
||||
_logger?.LogWarning(operResult.Message, ToString());
|
||||
isSuccess = false;
|
||||
StatuString = operResult;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (isSuccess)
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, $"历史数据循环异常");
|
||||
StatuString = new OperResult(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, $"中间变量计算线程循环异常");
|
||||
}
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
internal async Task StartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
|
||||
StoppingTokens.Add(new());
|
||||
//初始化线程
|
||||
await InitAsync();
|
||||
if (MemoryWorkerTask.Status == TaskStatus.Created)
|
||||
MemoryWorkerTask.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task StopAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
if (MemoryWorkerTask != null)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation($"中间变量计算线程停止中");
|
||||
await MemoryWorkerTask.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
_logger?.LogInformation($"中间变量计算线程已停止");
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
_logger?.LogInformation($"中间变量计算线程停止超时,已强制取消");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogInformation(ex, "等待线程停止错误");
|
||||
}
|
||||
}
|
||||
|
||||
MemoryWorkerTask?.SafeDispose();
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.SafeDispose();
|
||||
}
|
||||
MemoryWorkerTask = null;
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
#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.FriendlyException;
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备子线程服务
|
||||
/// </summary>
|
||||
public class UploadDeviceCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 全局插件服务
|
||||
/// </summary>
|
||||
private readonly PluginSingletonService _pluginService;
|
||||
/// <summary>
|
||||
/// 读写锁
|
||||
/// </summary>
|
||||
private readonly EasyLock easyLock = new();
|
||||
/// <summary>
|
||||
/// 当前设备信息
|
||||
/// </summary>
|
||||
private UploadDeviceRunTime _device;
|
||||
|
||||
/// <summary>
|
||||
/// 当前的驱动插件实例
|
||||
/// </summary>
|
||||
private UpLoadBase _driver;
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
private ILogger _logger;
|
||||
/// <summary>
|
||||
/// 是否初始化成功
|
||||
/// </summary>
|
||||
private bool isInitSuccess = true;
|
||||
|
||||
/// <inheritdoc cref="UploadDeviceCore"/>
|
||||
public UploadDeviceCore()
|
||||
{
|
||||
_pluginService = ServiceHelper.Services.GetService<PluginSingletonService>();
|
||||
DriverPluginService = ServiceHelper.Services.GetService<IDriverPluginService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备
|
||||
/// </summary>
|
||||
public UploadDeviceRunTime Device => _device;
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备Id
|
||||
/// </summary>
|
||||
public long DeviceId => (long)(_device?.Id.ToLong());
|
||||
|
||||
/// <summary>
|
||||
/// 当前插件
|
||||
/// </summary>
|
||||
public UpLoadBase Driver => _driver;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化成功
|
||||
/// </summary>
|
||||
public bool IsInitSuccess => isInitSuccess;
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
public ILogger Logger => _logger;
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备全部设备属性,执行初始化后获取正确值
|
||||
/// </summary>
|
||||
public List<DependencyProperty> Propertys { get; private set; }
|
||||
|
||||
private IDriverPluginService DriverPluginService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 暂停上传
|
||||
/// </summary>
|
||||
public void PasueThread(bool keepRun)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
var str = keepRun == false ? "设备线程上传暂停" : "设备线程上传继续";
|
||||
_logger?.LogInformation($"{str}:{_device.Name}");
|
||||
this.Device.KeepRun = keepRun;
|
||||
}
|
||||
}
|
||||
|
||||
#region 插件处理
|
||||
|
||||
/// <summary>
|
||||
/// 获取插件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private UpLoadBase CreatDriver()
|
||||
{
|
||||
|
||||
var driverPlugin = DriverPluginService.GetDriverPluginById(_device.PluginId);
|
||||
if (driverPlugin != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_driver = (UpLoadBase)_pluginService.GetDriver(driverPlugin);
|
||||
Propertys = _pluginService.GetDriverProperties(_driver);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw Oops.Oh($"创建插件失败:{ex.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Oops.Oh($"找不到驱动{driverPlugin.AssembleName}");
|
||||
}
|
||||
//设置插件配置项
|
||||
SetPluginProperties(_device.DevicePropertys);
|
||||
return _driver;
|
||||
|
||||
}
|
||||
|
||||
private void InitDriver()
|
||||
{
|
||||
//初始化插件
|
||||
_driver.Init(_logger, _device);
|
||||
//变量打包
|
||||
_device.UploadVariableCount = _driver.UploadVariables?.Count ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置驱动插件的属性值
|
||||
/// </summary>
|
||||
private void SetPluginProperties(List<DependencyProperty> deviceProperties)
|
||||
{
|
||||
if (deviceProperties == null) return;
|
||||
_pluginService.SetDriverProperties(_driver, deviceProperties);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 核心读写
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 线程开始时执行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal async Task BeforeActionAsync(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_device == null)
|
||||
{
|
||||
_logger?.LogError(nameof(UploadDeviceRunTime) + "设备不能为null");
|
||||
isInitSuccess = false;
|
||||
return;
|
||||
}
|
||||
if (_driver == null)
|
||||
{
|
||||
_logger?.LogWarning(_device.Name + " - 插件不能为null");
|
||||
isInitSuccess = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_logger?.LogInformation($"{_device.Name}上传设备线程开始");
|
||||
|
||||
|
||||
InitDriver();
|
||||
|
||||
try
|
||||
{
|
||||
if (Device.KeepRun == true)
|
||||
{
|
||||
await _driver.BeforStartAsync(token);
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, _device.Name);
|
||||
Device.SetDeviceStatus(null, Device.ErrorCount + 1, ex.Message);
|
||||
}
|
||||
isInitSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, _device.Name);
|
||||
isInitSuccess = false;
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime, 999, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束后
|
||||
/// </summary>
|
||||
internal async Task FinishActionAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation($"{_device.Name}上传线程停止中");
|
||||
await _driver?.AfterStopAsync();
|
||||
_driver?.SafeDispose();
|
||||
_logger?.LogInformation($"{_device.Name}上传线程已停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, $"{Device.Name} 释放失败");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isInitSuccess = false;
|
||||
easyLock.SafeDispose();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
internal bool Init(UploadDeviceRunTime device)
|
||||
{
|
||||
if (device == null)
|
||||
{
|
||||
_logger?.LogError(nameof(UploadDeviceRunTime) + "设备不能为null");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
bool isUpDevice = Device != device;
|
||||
_device = device;
|
||||
_logger = ServiceHelper.Services.GetService<ILoggerFactory>().CreateLogger("上传设备:" + _device.Name);
|
||||
//更新插件信息
|
||||
CreatDriver();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, device.Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行一次读取
|
||||
/// </summary>
|
||||
internal async Task<ThreadRunReturn> RunActionAsync(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_device == null)
|
||||
{
|
||||
_logger?.LogError(nameof(UploadDeviceRunTime) + "设备不能为null");
|
||||
return ThreadRunReturn.Continue;
|
||||
}
|
||||
if (_driver == null)
|
||||
{
|
||||
_logger?.LogWarning(_device.Name + " - 插件不能为null");
|
||||
return ThreadRunReturn.Continue;
|
||||
}
|
||||
|
||||
if (Device.KeepRun == false)
|
||||
{
|
||||
//上传暂停
|
||||
return ThreadRunReturn.Continue;
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
return ThreadRunReturn.Break;
|
||||
await _driver.ExecuteAsync(token);
|
||||
|
||||
//获取设备连接状态
|
||||
if (_driver.IsConnected())
|
||||
{
|
||||
//更新设备活动时间
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime, 999);
|
||||
}
|
||||
if (token.IsCancellationRequested)
|
||||
return ThreadRunReturn.Break;
|
||||
|
||||
//正常返回None
|
||||
return ThreadRunReturn.None;
|
||||
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return ThreadRunReturn.Break;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return ThreadRunReturn.Break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, $"上传线程循环异常{_device.Name}");
|
||||
Device.SetDeviceStatus(null, Device.ErrorCount + 1, ex.Message);
|
||||
return ThreadRunReturn.None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
/// <summary>
|
||||
/// 上传设备线程管理
|
||||
/// </summary>
|
||||
public class UploadDeviceThread : IAsyncDisposable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// CancellationTokenSources
|
||||
/// </summary>
|
||||
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
|
||||
|
||||
/// <summary>
|
||||
/// 线程
|
||||
/// </summary>
|
||||
protected Task DeviceTask;
|
||||
|
||||
/// <summary>
|
||||
/// 启停锁
|
||||
/// </summary>
|
||||
protected EasyLock easyLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// 默认等待间隔时间
|
||||
/// </summary>
|
||||
public static int CycleInterval { get; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 上传设备List,在CollectDeviceThread开始前应该初始化内容
|
||||
/// </summary>
|
||||
public ConcurrentList<UploadDeviceCore> UploadDeviceCores { get; private set; } = new();
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await StopThreadAsync();
|
||||
easyLock.SafeDispose();
|
||||
UploadDeviceCores.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始上传
|
||||
/// </summary>
|
||||
public virtual async Task StartThreadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
|
||||
StoppingTokens.Add(new());
|
||||
//初始化上传线程
|
||||
await InitTaskAsync();
|
||||
if (DeviceTask.Status == TaskStatus.Created)
|
||||
DeviceTask?.Start();
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 停止采集前,提前取消Token
|
||||
/// </summary>
|
||||
public virtual async Task BeforeStopThreadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
|
||||
if (DeviceTask == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止上传
|
||||
/// </summary>
|
||||
public virtual async Task StopThreadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
|
||||
if (DeviceTask == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var token in StoppingTokens)
|
||||
{
|
||||
token.Cancel();
|
||||
}
|
||||
try
|
||||
{
|
||||
await DeviceTask.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
foreach (var device in UploadDeviceCores)
|
||||
{
|
||||
device.Logger?.LogInformation($"{device.Device.Name}上传线程停止超时,已强制取消");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UploadDeviceCores.FirstOrDefault()?.Logger?.LogError(ex, $"{UploadDeviceCores.FirstOrDefault()?.Device?.Name}上传线程停止错误");
|
||||
}
|
||||
foreach (CancellationTokenSource token in StoppingTokens)
|
||||
{
|
||||
token?.SafeDispose();
|
||||
}
|
||||
DeviceTask?.SafeDispose();
|
||||
DeviceTask = null;
|
||||
StoppingTokens.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
protected async Task InitTaskAsync()
|
||||
{
|
||||
CancellationTokenSource stoppingToken = StoppingTokens.Last();
|
||||
DeviceTask = await Task.Factory.StartNew(async () =>
|
||||
{
|
||||
LoggerGroup log = UploadDeviceCores.FirstOrDefault().Driver.LogMessage;
|
||||
foreach (var device in UploadDeviceCores)
|
||||
{
|
||||
if (device.Driver == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//添加通道报文到每个设备
|
||||
var data = new EasyLogger(device.Driver.NewMessage) { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
log.AddLogger(data);
|
||||
await device.BeforeActionAsync(stoppingToken.Token);
|
||||
}
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
foreach (var device in UploadDeviceCores)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested)
|
||||
break;
|
||||
//初始化成功才能执行
|
||||
if (device.IsInitSuccess)
|
||||
{
|
||||
|
||||
var result = await device.RunActionAsync(stoppingToken.Token);
|
||||
if (result == ThreadRunReturn.None)
|
||||
{
|
||||
await Task.Delay(CycleInterval);
|
||||
}
|
||||
else if (result == ThreadRunReturn.Continue)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
else if (result == ThreadRunReturn.Break)
|
||||
{
|
||||
//当线程返回Break,直接跳出循环
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
//注意插件结束函数不能使用取消传播作为条件
|
||||
foreach (var device in UploadDeviceCores)
|
||||
{
|
||||
//如果插件还没释放,执行一次结束函数
|
||||
if (!device.Driver.DisposedValue)
|
||||
await device.FinishActionAsync();
|
||||
}
|
||||
|
||||
}
|
||||
, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
@@ -1,435 +0,0 @@
|
||||
#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.FriendlyException;
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Application.Extensions;
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备上传后台服务
|
||||
/// </summary>
|
||||
public class UploadDeviceWorker : BackgroundService
|
||||
{
|
||||
private readonly ILogger<UploadDeviceWorker> _logger;
|
||||
|
||||
private readonly PluginSingletonService _pluginService;
|
||||
|
||||
private readonly IUploadDeviceService _uploadDeviceService;
|
||||
|
||||
/// <inheritdoc cref="UploadDeviceWorker"/>
|
||||
public UploadDeviceWorker(ILogger<UploadDeviceWorker> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
_pluginService = ServiceHelper.Services.GetService<PluginSingletonService>();
|
||||
_uploadDeviceService = ServiceHelper.Services.GetService<IUploadDeviceService>();
|
||||
}
|
||||
/// <summary>
|
||||
/// 上传设备List
|
||||
/// </summary>
|
||||
public List<UploadDeviceCore> UploadDeviceCores => UploadDeviceThreads
|
||||
.Where(a => !a.StoppingTokens.Any(b => b.IsCancellationRequested))
|
||||
.SelectMany(a => a.UploadDeviceCores).ToList();
|
||||
/// <summary>
|
||||
/// 全部设备子线程
|
||||
/// </summary>
|
||||
private ConcurrentList<UploadDeviceThread> UploadDeviceThreads { get; set; } = new();
|
||||
|
||||
#region 设备创建更新结束
|
||||
/// <summary>
|
||||
/// 全部重启锁
|
||||
/// </summary>
|
||||
private readonly EasyLock restartLock = new();
|
||||
/// <summary>
|
||||
/// 单个重启锁
|
||||
/// </summary>
|
||||
private readonly EasyLock singleRestartLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// 控制设备线程启停
|
||||
/// </summary>
|
||||
public void ConfigDeviceThread(long deviceId, bool isStart)
|
||||
{
|
||||
if (deviceId == 0)
|
||||
UploadDeviceCores.ForEach(it => it.PasueThread(isStart));
|
||||
else
|
||||
UploadDeviceCores.FirstOrDefault(it => it.DeviceId == deviceId)?.PasueThread(isStart);
|
||||
}
|
||||
/// <summary>
|
||||
/// 开始
|
||||
/// </summary>
|
||||
public async Task StartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
await singleRestartLock.WaitAsync();
|
||||
CreatAllDeviceThreads();
|
||||
await StartAllDeviceThreadsAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
singleRestartLock.Release();
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public async Task StopAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (restartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await restartLock.WaitAsync();
|
||||
await singleRestartLock.WaitAsync();
|
||||
|
||||
await RemoveAllDeviceThreadAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "重启错误");
|
||||
}
|
||||
finally
|
||||
{
|
||||
singleRestartLock.Release();
|
||||
restartLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新设备线程
|
||||
/// </summary>
|
||||
public async Task UpDeviceThreadAsync(long devId)
|
||||
{
|
||||
try
|
||||
{
|
||||
//重启操作在未完全之前直接取消
|
||||
if (singleRestartLock.IsWaitting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await singleRestartLock.WaitAsync();
|
||||
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var devThread = UploadDeviceThreads.FirstOrDefault(it => it.UploadDeviceCores.Any(a => a.DeviceId == devId));
|
||||
var devCore = devThread.UploadDeviceCores.FirstOrDefault(a => a.DeviceId == devId);
|
||||
if (devThread == null) { throw Oops.Bah($"更新设备线程失败,不存在{devId}为id的设备"); }
|
||||
//这里先停止上传,操作会使线程取消,需要重新恢复线程
|
||||
await devThread.StopThreadAsync();
|
||||
var dev = _uploadDeviceService.GetUploadDeviceRuntime(devId).FirstOrDefault();
|
||||
if (dev == null)
|
||||
{
|
||||
//线程管理器移除后,如果不存在其他设备,也删除线程管理器
|
||||
devThread.UploadDeviceCores.Remove(devCore);
|
||||
if (devThread.UploadDeviceCores.Count == 0)
|
||||
{
|
||||
UploadDeviceThreads.Remove(devThread);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//初始化
|
||||
devCore.Init(dev);
|
||||
|
||||
//线程管理器移除后,如果不存在其他设备,也删除线程管理器
|
||||
devThread.UploadDeviceCores.Remove(devCore);
|
||||
if (devThread.UploadDeviceCores.Count == 0)
|
||||
{
|
||||
UploadDeviceThreads.Remove(devThread);
|
||||
}
|
||||
|
||||
//需判断是否同一通道
|
||||
var newDevThread = DeviceThread(devCore);
|
||||
await newDevThread.StartThreadAsync();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
singleRestartLock.Release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 核心
|
||||
|
||||
/// <summary>
|
||||
/// 创建设备上传线程
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private void CreatAllDeviceThreads()
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
_logger.LogInformation("正在获取采集组态信息");
|
||||
var collectDeviceRunTimes = (_uploadDeviceService.GetUploadDeviceRuntime());
|
||||
_logger.LogInformation("获取采集组态信息完成");
|
||||
foreach (var collectDeviceRunTime in collectDeviceRunTimes)
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
UploadDeviceCore deviceCollectCore = new();
|
||||
deviceCollectCore.Init(collectDeviceRunTime);
|
||||
DeviceThread(deviceCollectCore);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, collectDeviceRunTime.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private UploadDeviceThread DeviceThread(UploadDeviceCore deviceUploadCore)
|
||||
{
|
||||
UploadDeviceThread deviceThread = new();
|
||||
deviceThread.UploadDeviceCores.Add(deviceUploadCore);
|
||||
UploadDeviceThreads.Add(deviceThread);
|
||||
return deviceThread;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除设备线程,并且释放资源
|
||||
/// </summary>
|
||||
private async Task RemoveAllDeviceThreadAsync()
|
||||
{
|
||||
await UploadDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await deviceThread.BeforeStopThreadAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, deviceThread.ToString());
|
||||
}
|
||||
}, 10);
|
||||
await UploadDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await deviceThread.DisposeAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, deviceThread.ToString());
|
||||
}
|
||||
}, 10);
|
||||
UploadDeviceThreads.Clear();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始设备上传线程
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task StartAllDeviceThreadsAsync()
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
foreach (var item in UploadDeviceThreads)
|
||||
{
|
||||
if (!_stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await item.StartThreadAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 设备信息获取
|
||||
|
||||
/// <summary>
|
||||
/// 获取设备属性
|
||||
/// </summary>
|
||||
/// <param name="driverId"></param>
|
||||
/// <param name="devId"></param>
|
||||
/// <returns></returns>
|
||||
public List<DependencyProperty> GetDevicePropertys(long driverId, long devId = 0)
|
||||
{
|
||||
var driverPluginService = ServiceHelper.Services.GetService<IDriverPluginService>();
|
||||
var driverPlugin = driverPluginService.GetDriverPluginById(driverId);
|
||||
|
||||
var driver = _pluginService.GetDriver(driverPlugin);
|
||||
var Propertys = _pluginService.GetDriverProperties(driver);
|
||||
if (devId != 0)
|
||||
{
|
||||
var devcore = UploadDeviceCores.FirstOrDefault(it => it.Device.Id == devId);
|
||||
devcore?.Device?.DevicePropertys?.ForEach(it =>
|
||||
{
|
||||
var dependencyProperty = Propertys.FirstOrDefault(a => a.PropertyName == it.PropertyName);
|
||||
if (dependencyProperty != null)
|
||||
{
|
||||
dependencyProperty.Value = it.Value;
|
||||
}
|
||||
});
|
||||
}
|
||||
driver?.SafeDispose();
|
||||
|
||||
return Propertys;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取变量上传属性
|
||||
/// </summary>
|
||||
/// <param name="driverId"></param>
|
||||
/// <param name="dependencyProperties"></param>
|
||||
/// <returns></returns>
|
||||
public List<DependencyProperty> GetVariablePropertys(long driverId, List<DependencyProperty> dependencyProperties = null)
|
||||
{
|
||||
var driverPluginService = ServiceHelper.Services.GetService<IDriverPluginService>();
|
||||
var driverPlugin = driverPluginService.GetDriverPluginById(driverId);
|
||||
var driver = (UpLoadBase)_pluginService.GetDriver(driverPlugin);
|
||||
var Propertys = _pluginService.GetDriverVariableProperties(driver);
|
||||
dependencyProperties?.ForEach(it =>
|
||||
{
|
||||
var dependencyProperty = Propertys.FirstOrDefault(a => a.PropertyName == it.PropertyName);
|
||||
if (dependencyProperty != null)
|
||||
{
|
||||
dependencyProperty.Value = it.Value;
|
||||
}
|
||||
});
|
||||
driver?.SafeDispose();
|
||||
|
||||
return Propertys;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region worker服务
|
||||
|
||||
/// <summary>
|
||||
/// 在软件关闭时取消
|
||||
/// </summary>
|
||||
private CancellationToken _stoppingToken;
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken token)
|
||||
{
|
||||
await base.StartAsync(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StopAsync(CancellationToken token)
|
||||
{
|
||||
using var stoppingToken = new CancellationTokenSource();
|
||||
_stoppingToken = stoppingToken.Token;
|
||||
stoppingToken.Cancel();
|
||||
await base.StopAsync(token);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
//这里不采用CancellationToken控制子线程,直接循环保持,结束时调用子设备线程Dispose
|
||||
//检测设备上传线程假死
|
||||
var num = UploadDeviceCores.Count;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
UploadDeviceCore devcore = UploadDeviceCores[i];
|
||||
if (devcore.Device != null)
|
||||
{
|
||||
//超过30分钟,或者(初始化失败并超过10分钟)会重启
|
||||
if (
|
||||
(devcore.Device.ActiveTime != DateTime.MinValue
|
||||
&& devcore.Device.ActiveTime.AddMinutes(30) <= SysDateTimeExtensions.CurrentDateTime)
|
||||
|| (devcore.IsInitSuccess == false && devcore.Device.ActiveTime.AddMinutes(10) <= SysDateTimeExtensions.CurrentDateTime)
|
||||
)
|
||||
{
|
||||
//如果线程处于暂停状态,跳过
|
||||
if (devcore.Device.DeviceStatus == DeviceStatusEnum.Pause)
|
||||
continue;
|
||||
//如果初始化失败
|
||||
if (!devcore.IsInitSuccess)
|
||||
_logger?.LogWarning(devcore.Device.Name + "初始化失败,重启线程中");
|
||||
else
|
||||
_logger?.LogWarning(devcore.Device.Name + "上传线程假死,重启线程中");
|
||||
//重启线程
|
||||
|
||||
await UpDeviceThreadAsync(devcore.Device.Id);
|
||||
break;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogTrace(devcore.Device.Name + "线程检测正常");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//每5分钟检测一次
|
||||
await Task.Delay(300000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@using ThingsGateway.Admin.Blazor.Core;
|
||||
@using ThingsGateway.Admin.Core;
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using Masa.Blazor;
|
||||
@using ThingsGateway.Application;
|
||||
@namespace ThingsGateway.Blazor
|
||||
@inject UserResoures UserResoures;
|
||||
@inherits DriverDebugUIBase
|
||||
|
||||
<MCard Flat Elevation="0">
|
||||
|
||||
@{
|
||||
switch (Channel)
|
||||
{
|
||||
case ChannelEnum.TcpClientEx:
|
||||
<TcpClientPage @ref=TcpClientPage></TcpClientPage>
|
||||
break;
|
||||
case ChannelEnum.SerialPort:
|
||||
<SerialClientPage @ref=SerialClientPage></SerialClientPage>
|
||||
break;
|
||||
case ChannelEnum.UdpSession:
|
||||
<UdpSessionPage @ref=UdpSessionPage></UdpSessionPage>
|
||||
break;
|
||||
case ChannelEnum.TcpServer:
|
||||
<TcpServerPage @ref=TcpServerPage></TcpServerPage>
|
||||
break;
|
||||
}
|
||||
}
|
||||
@if (Plc != null && ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
<MCard Class="pa-4" Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters>
|
||||
|
||||
<MCol Md="4">
|
||||
<MCard Flat Elevation="0">
|
||||
@if (OtherContent!=null)
|
||||
{
|
||||
@OtherContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<MCol Class="my-1 py-1">
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MTextField Class="mx-1 my-1" Label="变量地址" @attributes="@tip.Attrs" Dense Outlined HideDetails="@("auto")" @bind-Value=@Address />
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span style="white-space: pre-wrap;">@Plc?.GetAddressDescription()</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="DataTypeEnum" Outlined Label="数据类型"
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@WriteValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</MCol>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
<MCol Md="8">
|
||||
|
||||
|
||||
<MCard Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+300}px)") Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<MCardActions>
|
||||
输出日志
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
await DownDeviceMessageExportAsync(Messages.Select(a=>a.message));
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
@{
|
||||
var item = Messages;
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+400}px)") OverscanCount=2 ItemSize="100" Items="item">
|
||||
<ItemContent>
|
||||
<div title=@itemMessage.message class=@(itemMessage.level<Microsoft.Extensions.Logging.LogLevel.Information?UserResoures.IsDark? " while--text ":"black--text":itemMessage.level>=Microsoft.Extensions.Logging.LogLevel.Warning?" red--text ":"green--text ") style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;">
|
||||
@itemMessage.message
|
||||
</div>
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
|
||||
</MCard>
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using ThingsGateway.Application;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public enum ChannelEnum
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
None = 0,
|
||||
/// <inheritdoc/>
|
||||
TcpClientEx = 1,
|
||||
/// <inheritdoc/>
|
||||
SerialPort = 2,
|
||||
/// <inheritdoc/>
|
||||
UdpSession = 3,
|
||||
/// <inheritdoc/>
|
||||
TcpServer = 4,
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public partial class DefalutDebugDriverPage : DriverDebugUIBase
|
||||
{
|
||||
/// <summary>
|
||||
/// SerialClientPage
|
||||
/// </summary>
|
||||
public SerialClientPage SerialClientPage;
|
||||
/// <summary>
|
||||
/// TcpClientPage
|
||||
/// </summary>
|
||||
public TcpClientPage TcpClientPage;
|
||||
/// <summary>
|
||||
/// TcpServerPage
|
||||
/// </summary>
|
||||
public TcpServerPage TcpServerPage;
|
||||
/// <summary>
|
||||
/// UdpSessionPage
|
||||
/// </summary>
|
||||
public UdpSessionPage UdpSessionPage;
|
||||
/// <summary>
|
||||
/// ѡ<><D1A1><EFBFBD><EFBFBD>1-TCPCLIENT<4E><54>2-<2D><><EFBFBD>ڣ<EFBFBD>3-UDP<44><50>4-TCPServer
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public ChannelEnum Channel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public override ThingsGateway.Foundation.IReadWriteDevice Plc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ģ<><C4A3>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
/// <summary>
|
||||
/// <20>Զ<EFBFBD><D4B6><EFBFBD>ģ<EFBFBD><C4A3>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment OtherContent { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public override void Dispose()
|
||||
{
|
||||
Plc?.SafeDispose();
|
||||
TcpClientPage?.SafeDispose();
|
||||
SerialClientPage?.SafeDispose();
|
||||
TcpServerPage?.SafeDispose();
|
||||
UdpSessionPage?.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
if (TcpClientPage != null)
|
||||
TcpClientPage.LogAction = LogOut;
|
||||
if (SerialClientPage != null)
|
||||
SerialClientPage.LogAction = LogOut;
|
||||
if (TcpServerPage != null)
|
||||
TcpServerPage.LogAction = LogOut;
|
||||
if (UdpSessionPage != null)
|
||||
UdpSessionPage.LogAction = LogOut;
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
using ThingsGateway.Application.Extensions;
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
|
||||
namespace ThingsGateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 调试UI
|
||||
/// </summary>
|
||||
public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 导出提示
|
||||
/// </summary>
|
||||
public bool isDownExport;
|
||||
|
||||
/// <summary>
|
||||
/// 日志缓存
|
||||
/// </summary>
|
||||
public ConcurrentLinkedList<(LogLevel level, string message)> Messages = new();
|
||||
|
||||
IJSObjectReference _helper;
|
||||
readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1));
|
||||
|
||||
/// <summary>
|
||||
/// 默认读写设备
|
||||
/// </summary>
|
||||
public virtual IReadWriteDevice Plc { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 变量地址
|
||||
/// </summary>
|
||||
public virtual string Address { get; set; } = "40001";
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
protected virtual DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Int16;
|
||||
/// <inheritdoc/>
|
||||
[Inject]
|
||||
protected IJSRuntime JS { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 写入值
|
||||
/// </summary>
|
||||
public virtual string WriteValue { get; set; }
|
||||
[Inject]
|
||||
private ICollectDeviceService CollectDeviceService { get; set; }
|
||||
|
||||
[Inject]
|
||||
private IVariableService VariableService { get; set; }
|
||||
[Inject]
|
||||
private InitTimezone InitTimezone { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
_periodicTimer?.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task ReadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await Plc.ReadAsync(Address, DataTypeEnum.GetSystemType());
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
Messages.Add((LogLevel.Information, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - 对应类型值:" + data.Content));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Add((LogLevel.Error, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + data.Message));
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.Add((LogLevel.Warning, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + "错误:" + ex.Message));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task WriteAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await Plc.WriteAsync(Address, DataTypeEnum.GetSystemType(), WriteValue);
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
Messages.Add((LogLevel.Information, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + data.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Add((LogLevel.Warning, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + data.Message));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.Add((LogLevel.Error, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + "写入前失败:" + ex.Message));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 导入设备
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DeviceImportAsync(CollectDevice data)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
await CollectDeviceService.AddAsync(data);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导入变量
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DeviceVariableImportAsync(List<DeviceVariable> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
await VariableService.AddBatchAsync(data);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceMessageExportAsync(IEnumerable<string> values)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
using var memoryStream = new MemoryStream();
|
||||
StreamWriter writer = new(memoryStream);
|
||||
foreach (var item in values)
|
||||
{
|
||||
writer.WriteLine(item);
|
||||
}
|
||||
writer.Flush();
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
_helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
|
||||
await _helper.InvokeVoidAsync("downloadFileFromStream", $"报文导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.txt", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceExportAsync(CollectDevice data)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
using var memoryStream = await CollectDeviceService.ExportFileAsync(new List<CollectDevice>() { data });
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
_helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
|
||||
await _helper.InvokeVoidAsync("downloadFileFromStream", $"设备导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出到excel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task DownDeviceVariableExportAsync(List<DeviceVariable> data, string devName)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
using var memoryStream = await VariableService.ExportFileAsync(data, devName);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
_helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
|
||||
await _helper.InvokeVoidAsync("downloadFileFromStream", $"变量导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void LogOut(TouchSocket.Core.LogLevel logLevel, object source, string message, Exception exception)
|
||||
{
|
||||
Messages.Add(((LogLevel)logLevel, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + message + (exception != null ? exception.Message : "")));
|
||||
if (Messages.Count > 2500)
|
||||
{
|
||||
Messages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_ = RunTimerAsync();
|
||||
base.OnInitialized();
|
||||
}
|
||||
private async Task RunTimerAsync()
|
||||
{
|
||||
while (await _periodicTimer.WaitForNextTickAsync())
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using ThingsGateway.Foundation.Serial;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class SerialClientPage
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><>־<EFBFBD><D6BE><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private TouchSocketConfig config;
|
||||
|
||||
private readonly SerialProperty serialProperty = new();
|
||||
|
||||
private SerialClient SerialClient { get; set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
SerialClient.SafeDispose();
|
||||
}
|
||||
/// <summary>
|
||||
/// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public SerialClient GetSerialClient()
|
||||
{
|
||||
config?.Dispose();
|
||||
config = new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
config.SetSerialProperty(serialProperty);
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
SerialClient.Setup(config);
|
||||
return SerialClient;
|
||||
}
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
SerialClient.Close();
|
||||
await GetSerialClient().ConnectAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
SerialClient.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config ??= new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
SerialClient = new SerialClient();
|
||||
SerialClient.Setup(config);
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class TcpClientPage
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><>־<EFBFBD><D6BE><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private TouchSocketConfig config;
|
||||
/// <summary>
|
||||
/// IP
|
||||
/// </summary>
|
||||
private string IP = "127.0.0.1";
|
||||
/// <summary>
|
||||
/// <20>˿<EFBFBD>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int Port { get; set; } = 502;
|
||||
|
||||
private TcpClientEx TcpClientEx { get; set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
TcpClientEx.SafeDispose();
|
||||
}
|
||||
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
TcpClientEx.Close();
|
||||
await GetTcpClient().ConnectAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
TcpClientEx.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TcpClientEx GetTcpClient()
|
||||
{
|
||||
config ??= new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
config.SetRemoteIPHost(new IPHost(IP + ":" + Port)).SetBufferLength(300);
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
TcpClientEx.Setup(config);
|
||||
return TcpClientEx;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config?.Dispose();
|
||||
config = new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
config.SetRemoteIPHost(new IPHost(IP + ":" + Port)).SetBufferLength(300);
|
||||
TcpClientEx = new TcpClientEx();
|
||||
TcpClientEx.Setup(config);
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class TcpServerPage
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><>־<EFBFBD><D6BE><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private TouchSocketConfig config;
|
||||
|
||||
private string ip = "127.0.0.1";
|
||||
|
||||
private int port = 502;
|
||||
|
||||
private TcpService TcpServer { get; set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
TcpServer.SafeDispose();
|
||||
}
|
||||
private void Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
TcpServer.Stop();
|
||||
GetTcpServer().Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
TcpServer.Stop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TcpService GetTcpServer()
|
||||
{
|
||||
config ??= new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
|
||||
config.SetBufferLength(300);
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
TcpServer.Setup(config);
|
||||
return TcpServer;
|
||||
}
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config?.Dispose();
|
||||
config = new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
|
||||
config.SetBufferLength(300);
|
||||
TcpServer = new TcpService();
|
||||
TcpServer.Setup(config);
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class UdpSessionPage : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><>־<EFBFBD><D6BE><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
public Action<LogLevel, object, string, Exception> LogAction;
|
||||
|
||||
private TouchSocketConfig config;
|
||||
|
||||
private string ip = "127.0.0.1";
|
||||
|
||||
private int port = 502;
|
||||
|
||||
private UdpSession UdpSession { get; set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
UdpSession.SafeDispose();
|
||||
}
|
||||
private void Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
UdpSession.Stop();
|
||||
GetUdpSession().Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
|
||||
}
|
||||
private void DisConnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
UdpSession.Stop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public UdpSession GetUdpSession()
|
||||
{
|
||||
config ??= new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
config.SetRemoteIPHost(new IPHost(ip + ":" + port)).SetBufferLength(300);
|
||||
config.SetBindIPHost(new IPHost(0));
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
UdpSession.Setup(config);
|
||||
return UdpSession;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config?.Dispose();
|
||||
config = new TouchSocketConfig();
|
||||
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
|
||||
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||
config.SetRemoteIPHost(new IPHost(ip + ":" + port)).SetBufferLength(300);
|
||||
config.SetBindIPHost(new IPHost(0));
|
||||
UdpSession = new UdpSession();
|
||||
UdpSession.Setup(config);
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/gatewaylog/backendlog"
|
||||
@using System.Linq.Expressions;
|
||||
@inject IBackendLogService BackendLogService
|
||||
@inject UserResoures UserResoures
|
||||
@namespace ThingsGateway.Blazor
|
||||
@using BlazorComponent;
|
||||
@using Masa.Blazor
|
||||
@using Microsoft.AspNetCore.Authorization;
|
||||
@using ThingsGateway.Admin.Blazor.Core;
|
||||
@using ThingsGateway.Admin.Blazor;
|
||||
@using ThingsGateway.Admin.Core;
|
||||
@using ThingsGateway.Application;
|
||||
@attribute [Authorize]
|
||||
@inherits BaseComponentBase
|
||||
@layout MainLayout
|
||||
<AppDataTable @ref="_datatable" TItem="BackendLog" SearchItem="BackendLogPageInput"
|
||||
AddItem="object" EditItem="object" IsShowSelect=false
|
||||
SearchModel="@search" IsShowOperCol=true
|
||||
QueryCallAsync="QueryCallAsync"
|
||||
IsShowDetailButton
|
||||
IsShowQueryButton>
|
||||
<SearchTemplate>
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.Source"
|
||||
Outlined Label=@context.Description(x => x.Source) />
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.Level"
|
||||
Outlined Label=@context.Description(x => x.Level) />
|
||||
|
||||
</SearchTemplate>
|
||||
<OtherToolbarTemplate>
|
||||
@if (@UserResoures.IsHasButtonWithRole("gatewaybackendlogclear"))
|
||||
{
|
||||
<MButton Color="error" Class="mx-2 my-1" OnClick=@(ClearClickAsync)>清空</MButton>
|
||||
}
|
||||
<MMenu OffsetY Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary"
|
||||
Class="my-1 mx-2 ">
|
||||
导出
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem OnClick="()=>DownExportAsync()"> 导出全部</MListItem>
|
||||
<MListItem OnClick="()=>DownExportAsync(search)"> 导出搜索项 </MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
</OtherToolbarTemplate>
|
||||
</AppDataTable>
|
||||
|
||||
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/gatewayconfig/collectdevice"
|
||||
@namespace ThingsGateway.Blazor
|
||||
@using System.Linq.Expressions;
|
||||
@using BlazorComponent;
|
||||
@using Mapster;
|
||||
@using Masa.Blazor
|
||||
@using Masa.Blazor.Presets;
|
||||
@using System.IO;
|
||||
@using Microsoft.AspNetCore.Authorization;
|
||||
@using ThingsGateway.Admin.Blazor.Core;
|
||||
@using ThingsGateway.Admin.Blazor;
|
||||
@using ThingsGateway.Application;
|
||||
@inject ICollectDeviceService CollectDeviceService
|
||||
@attribute [Authorize]
|
||||
@inherits BaseComponentBase
|
||||
@inject UserResoures UserResoures
|
||||
@inject IDriverPluginService DriverPluginService
|
||||
@layout MainLayout
|
||||
@using ThingsGateway.Admin.Core;
|
||||
@if (IsMobile)
|
||||
{
|
||||
@GetAppDataTable()
|
||||
}
|
||||
else
|
||||
{
|
||||
<MRow>
|
||||
<MCol Md=2 Cols="12">
|
||||
<MCard Class="ma-2" Height=@("100%")>
|
||||
<MCardTitle>
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_searchName"
|
||||
Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.DeviceGroup)) />
|
||||
</MCardTitle>
|
||||
<MTreeview
|
||||
Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+100}px);overflow-y:auto")
|
||||
Dense TItem="string" TKey="string" ActiveChanged=@(async a=>
|
||||
{
|
||||
if(search.DeviceGroup!=a.FirstOrDefault())
|
||||
{
|
||||
search.DeviceGroup=a.FirstOrDefault();
|
||||
await DatatableQueryAsync();
|
||||
}
|
||||
} )
|
||||
Items="_deviceGroups" ItemText="r=>r" ItemChildren="r=>null"
|
||||
Search="@_searchName"
|
||||
Activatable ItemKey=@(r=>r)>
|
||||
<LabelContent>
|
||||
<span title=@context.Item>
|
||||
@context.Item
|
||||
</span>
|
||||
</LabelContent>
|
||||
</MTreeview>
|
||||
</MCard>
|
||||
</MCol>
|
||||
<MCol Md=10 Cols="12">
|
||||
|
||||
@GetAppDataTable()
|
||||
</MCol>
|
||||
</MRow>
|
||||
}
|
||||
|
||||
|
||||
<ImportExcel @ref=ImportExcel Import="SaveDeviceImportAsync" Preview="DeviceImportAsync" />
|
||||
|
||||
|
||||
@code {
|
||||
RenderFragment GetAppDataTable()
|
||||
{
|
||||
RenderFragment renderFragment =
|
||||
@<AppDataTable @ref="_datatable"
|
||||
StyleString=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+10}px)")
|
||||
TItem="CollectDevice" SearchItem="CollectDevicePageInput"
|
||||
AddItem="CollectDeviceAddInput" EditItem="CollectDeviceEditInput"
|
||||
IsMenuOperTemplate=false SearchModel="@search"
|
||||
QueryCallAsync="QueryCallAsync" AddCallAsync="AddCallAsync"
|
||||
EditCallAsync="EditCallAsync" DeleteCallAsync="DeleteCallAsync"
|
||||
IsShowDetailButton
|
||||
IsShowQueryButton
|
||||
IsShowAddButton=@UserResoures.IsHasButtonWithRole("gatewaycollectdeviceadd")
|
||||
IsShowDeleteButton=@UserResoures.IsHasButtonWithRole("gatewaycollectdevicedelete")
|
||||
IsShowEditButton=@UserResoures.IsHasButtonWithRole("gatewaycollectdeviceedit")>
|
||||
<AddTemplate>
|
||||
@GetRenderFragment(context)
|
||||
</AddTemplate>
|
||||
|
||||
<EditTemplate>
|
||||
@GetRenderFragment(context)
|
||||
|
||||
</EditTemplate>
|
||||
|
||||
<SearchTemplate>
|
||||
<MTextField Dense
|
||||
Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.Name"
|
||||
Clearable
|
||||
Outlined
|
||||
Label=@context.Description(x => x.Name) />
|
||||
<MTextField Dense
|
||||
Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.PluginName"
|
||||
Clearable
|
||||
Outlined
|
||||
Label=@context.Description(x => x.PluginName) />
|
||||
<MTextField Dense
|
||||
Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.DeviceGroup"
|
||||
Clearable
|
||||
Outlined
|
||||
Label=@context.Description(x => x.DeviceGroup) />
|
||||
</SearchTemplate>
|
||||
<OtherToolbarTemplate>
|
||||
|
||||
<MMenu OffsetY
|
||||
Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary"
|
||||
Class="my-1 mx-2 ">
|
||||
复制
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem OnClick="()=>CopyDeviceAsync(context)">复制设备</MListItem>
|
||||
<MListItem OnClick="()=>CopyDevAndVarAsync(context)">复制设备及设备下变量</MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
<MMenu OffsetY
|
||||
Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary"
|
||||
Class="my-1 mx-2 ">
|
||||
导出
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem OnClick="()=>DownExportAsync()">导出全部</MListItem>
|
||||
<MListItem OnClick="()=>DownExportAsync(search)">导出搜索项</MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaycollectdeviceedit")) Class="my-1 mx-2" OnClick="()=>{ ImportExcel.Step=1; ImportExcel.IsShowImport=true;}" Color="primary">
|
||||
导入
|
||||
</MButton>
|
||||
</OtherToolbarTemplate>
|
||||
|
||||
|
||||
|
||||
<ItemColTemplate>
|
||||
@switch (context.Header.Value)
|
||||
{
|
||||
|
||||
case nameof(context.Item.Enable):
|
||||
<EnableChip Value="context.Item.Enable">
|
||||
</EnableChip>
|
||||
break;
|
||||
case nameof(context.Item.PluginId):
|
||||
<span title=@context.Value>
|
||||
@(
|
||||
DriverPluginService.GetNameById(context.Item.PluginId)
|
||||
)
|
||||
</span>
|
||||
break;
|
||||
default:
|
||||
@if (context.Header.CellClass?.Contains("text-truncate") == true)
|
||||
{
|
||||
<span title=@context.Value>
|
||||
@context.Value
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@context.Value
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
</ItemColTemplate>
|
||||
|
||||
|
||||
</AppDataTable>
|
||||
;
|
||||
return renderFragment;
|
||||
}
|
||||
}
|
||||
@code {
|
||||
|
||||
RenderFragment GetRenderFragment(CollectDeviceAddInput context)
|
||||
{
|
||||
RenderFragment renderFragment =
|
||||
@<div>
|
||||
<MTabs @bind-Value="tab">
|
||||
<MTab Value="1" Style="height:50px;"> 基本信息 </MTab>
|
||||
<MTab Value="2"> 扩展属性</MTab>
|
||||
</MTabs>
|
||||
|
||||
<MTabsItems Value="tab">
|
||||
<MTabItem Value="1">
|
||||
@if (tab == 1)
|
||||
{
|
||||
<MCard Flat Class="ma-2">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(context.Description(x => x.Name)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Name />
|
||||
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(context.Description(x => x.Description)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Description />
|
||||
|
||||
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(context.Description(x => x.DeviceGroup)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.DeviceGroup />
|
||||
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(typeof(CollectDeviceRunTime).GetDescription(nameof(CollectDeviceRunTime.PluginName))) </MSubheader>
|
||||
<MCascader Value="context.PluginId" Class="mt-3 mr-3" Clearable TValue=long TItem="DriverPluginCategory"
|
||||
ValueChanged=@(async a=>await DriverValueChangedAsync(context,a))
|
||||
Items="DriverPlugins" ItemText="u => u.Name" ItemValue="u => u.Id" ItemChildren="u => u.Children"
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ShowAllLevels="false" HideDetails="@("auto")" Height="30" Dense>
|
||||
</MCascader>
|
||||
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(context.Description(x => x.Enable)) </MSubheader>
|
||||
<MSwitch @bind-Value=@context.Enable />
|
||||
|
||||
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(context.Description(x => x.IsLogOut)) </MSubheader>
|
||||
<MSwitch @bind-Value=@context.IsLogOut />
|
||||
|
||||
|
||||
<MSubheader Class="mt-4 font-weight-black"> @(context.Description(x => x.IsRedundant)) </MSubheader>
|
||||
<MSwitch @bind-Value=@context.IsRedundant />
|
||||
|
||||
<MSubheader Class="font-weight-black"> @context.Description(x=>x.RedundantDeviceId) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.RedundantDeviceId Outlined
|
||||
Items=@(CollectDevices)
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Name) ItemValue=@(u =>u.Id)
|
||||
ItemDisabled="u => (u.PluginId!=context.PluginId||u.Id==context.Id)"
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
|
||||
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
<MTabItem Value="2">
|
||||
@if (tab == 2)
|
||||
{
|
||||
<MCard Flat Class="ma-2">
|
||||
<MButton Class="my-3" OnClick=@(async() =>
|
||||
{
|
||||
if(context.PluginId>0)
|
||||
{
|
||||
context.DevicePropertys= GetDriverProperties(context.PluginId,context.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("需选择驱动",AlertTypes.Error);
|
||||
}
|
||||
}
|
||||
) Color="primary">
|
||||
刷新设备属性
|
||||
</MButton>
|
||||
@if (context.DevicePropertys != null)
|
||||
{
|
||||
@foreach (var item in context.DevicePropertys)
|
||||
{
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Description </MSubheader>
|
||||
<MTooltip Disabled=@(item.Remark==null||item.Remark?.IsNullOrEmpty()==true) Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MTextField Type="@(item.PropertyName.Contains("Password") ? "password" : "text")" @attributes="@tip.Attrs" Dense Outlined HideDetails="@("auto")" @bind-Value=@item.Value />
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>@item.Remark</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
}
|
||||
|
||||
}
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
</MTabsItems>
|
||||
</div>
|
||||
;
|
||||
return renderFragment;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
#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 BlazorComponent;
|
||||
|
||||
using Furion;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Blazor;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
using ThingsGateway.Application;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
/// <summary>
|
||||
/// 采集设备页面
|
||||
/// </summary>
|
||||
public partial class CollectDevicePage
|
||||
{
|
||||
readonly CollectDevicePageInput search = new();
|
||||
IAppDataTable _datatable;
|
||||
List<string> _deviceGroups = new();
|
||||
string _searchName;
|
||||
List<CollectDevice> CollectDevices = new();
|
||||
List<DriverPluginCategory> DriverPlugins;
|
||||
ImportExcel ImportExcel;
|
||||
StringNumber tab;
|
||||
[Inject]
|
||||
AjaxService AjaxService { get; set; }
|
||||
[CascadingParameter]
|
||||
MainLayout MainLayout { get; set; }
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
CollectDevices = App.GetService<ICollectDeviceService>().GetCacheList();
|
||||
DriverPlugins = App.GetService<IDriverPluginService>().GetDriverPluginChildrenList(DriverEnum.Collect);
|
||||
_deviceGroups = CollectDevices?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
private async Task AddCallAsync(CollectDeviceAddInput input)
|
||||
{
|
||||
await CollectDeviceService.AddAsync(input);
|
||||
CollectDevices = CollectDeviceService.GetCacheList();
|
||||
_deviceGroups = CollectDevices?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
async Task CopyDevAndVarAsync(IEnumerable<CollectDevice> data)
|
||||
{
|
||||
if (!data.Any())
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("需选择一项或多项", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
await CollectDeviceService.CopyDevAndVarAsync(data);
|
||||
await DatatableQueryAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("复制成功", AlertTypes.Success);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
async Task CopyDeviceAsync(IEnumerable<CollectDevice> data)
|
||||
{
|
||||
if (!data.Any())
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("需选择一项或多项", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
await CollectDeviceService.CopyDevAsync(data);
|
||||
await DatatableQueryAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("复制成功", AlertTypes.Success);
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
private async Task DatatableQueryAsync()
|
||||
{
|
||||
await _datatable?.QueryClickAsync();
|
||||
}
|
||||
|
||||
private async Task DeleteCallAsync(IEnumerable<CollectDevice> input)
|
||||
{
|
||||
await CollectDeviceService.DeleteAsync(input.Select(a => a.Id).ToArray());
|
||||
CollectDevices = CollectDeviceService.GetCacheList();
|
||||
_deviceGroups = CollectDevices?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> DeviceImportAsync(IBrowserFile file)
|
||||
{
|
||||
return CollectDeviceService.PreviewAsync(file);
|
||||
}
|
||||
async Task DownExportAsync(CollectDevicePageInput input = null)
|
||||
{
|
||||
await AjaxService.DownFileAsync("gatewayFile/collectDevice", SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat(), input.Adapt<CollectDeviceInput>());
|
||||
}
|
||||
private async Task DriverValueChangedAsync(CollectDeviceAddInput context, long pluginId)
|
||||
{
|
||||
if (pluginId <= 0) return;
|
||||
|
||||
if (context.DevicePropertys == null || context.DevicePropertys?.Count == 0 || context.PluginId != pluginId)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.DevicePropertys = GetDriverProperties(pluginId, context.Id);
|
||||
await PopupService.EnqueueSnackbarAsync("插件附加属性已更新", AlertTypes.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync(ex.Message, AlertTypes.Error);
|
||||
}
|
||||
}
|
||||
context.PluginId = pluginId;
|
||||
}
|
||||
|
||||
private async Task EditCallAsync(CollectDeviceEditInput input)
|
||||
{
|
||||
await CollectDeviceService.EditAsync(input);
|
||||
CollectDevices = CollectDeviceService.GetCacheList();
|
||||
_deviceGroups = CollectDevices?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
|
||||
|
||||
List<DependencyProperty> GetDriverProperties(long driverId, long devId)
|
||||
{
|
||||
return ServiceHelper.GetBackgroundService<CollectDeviceWorker>().GetDevicePropertys(driverId, devId);
|
||||
}
|
||||
|
||||
private async Task<SqlSugarPagedList<CollectDevice>> QueryCallAsync(CollectDevicePageInput input)
|
||||
{
|
||||
var data = await CollectDeviceService.PageAsync(input);
|
||||
return data;
|
||||
}
|
||||
|
||||
async Task SaveDeviceImportAsync(Dictionary<string, ImportPreviewOutputBase> data)
|
||||
{
|
||||
await CollectDeviceService.ImportAsync(data);
|
||||
await DatatableQueryAsync();
|
||||
ImportExcel.IsShowImport = false;
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/gatewayconfig/config"
|
||||
@using System.Linq.Expressions;
|
||||
@using Masa.Blazor
|
||||
@using Microsoft.AspNetCore.Authorization;
|
||||
@using ThingsGateway.Admin.Application;
|
||||
@using ThingsGateway.Admin.Blazor.Core;
|
||||
@using ThingsGateway.Admin.Blazor;
|
||||
@using ThingsGateway.Admin.Core;
|
||||
@using ThingsGateway.Application;
|
||||
|
||||
@inject IConfigService ConfigService
|
||||
@namespace ThingsGateway.Blazor
|
||||
@attribute [Authorize]
|
||||
@inject UserResoures UserResoures
|
||||
@inherits BaseComponentBase
|
||||
@layout MainLayout
|
||||
<MCard Height=@("100%") Elevation="1" Class="pa-2">
|
||||
|
||||
<MTabs @bind-Value="tab">
|
||||
<MTab Style="height:50px;" Value="1">
|
||||
报警转储配置
|
||||
</MTab>
|
||||
<MTab Style="height:50px;" Value="2">
|
||||
历史数据配置
|
||||
</MTab>
|
||||
</MTabs>
|
||||
|
||||
<MTabsItems Value="tab">
|
||||
<MTabItem Value="1">
|
||||
@if (tab == 1)
|
||||
{
|
||||
|
||||
<MCard Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+100}px);overflow-y:auto")
|
||||
Flat Class="ml-2 my-4">
|
||||
<MRow NoGutters>
|
||||
@foreach (var item in _alarmConfig)
|
||||
{
|
||||
switch (item.ConfigKey)
|
||||
{
|
||||
case ThingsGatewayConfigConst.Config_Alarm_ConnStr:
|
||||
<MCol Class="pa-2 px-4" Md=12 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@item.ConfigValue />
|
||||
</MCol>
|
||||
break;
|
||||
case ThingsGatewayConfigConst.Config_Alarm_DbType:
|
||||
<MCol Class="pa-2 px-4" Md=12 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MSelect @bind-Value="item.ConfigValue" Outlined
|
||||
Items=@(typeof(SqlDbType).GetEnumList()) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des) ItemValue=@(u =>u.name)
|
||||
HideDetails="true" Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
break;
|
||||
case ThingsGatewayConfigConst.Config_Alarm_Enable:
|
||||
<MCol Class="pa-2 px-4" Md=12 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MSelect @bind-Value=@item.ConfigValue Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u.ToString())
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
|
||||
</MCol>
|
||||
break;
|
||||
default:
|
||||
<MCol Class="pa-2 px-4" Md=3 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@item.ConfigValue />
|
||||
</MCol>
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
</MRow>
|
||||
<MCardActions>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewayalarmconfig")) Class="mt-8" OnClick=OnAlarmSaveAsync Color="primary">
|
||||
保存
|
||||
</MButton>
|
||||
</MCardActions>
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
<MTabItem Value="2">
|
||||
@if (tab == 2)
|
||||
{
|
||||
<MCard Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+100}px);overflow-y:auto")
|
||||
Flat Class="ml-2 my-4">
|
||||
<MRow NoGutters>
|
||||
@foreach (var item in _hisConfig)
|
||||
{
|
||||
switch (item.ConfigKey)
|
||||
{
|
||||
case ThingsGatewayConfigConst.Config_His_ConnStr:
|
||||
<MCol Class="pa-2 px-4" Md=12 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@item.ConfigValue />
|
||||
</MCol>
|
||||
break;
|
||||
case ThingsGatewayConfigConst.Config_His_DbType:
|
||||
<MCol Class="pa-2 px-4" Md=12 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MSelect @bind-Value="item.ConfigValue" Outlined
|
||||
Items=@(typeof(HisDbType).GetEnumList()) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des) ItemValue=@(u =>u.name)
|
||||
HideDetails="true" Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
break;
|
||||
case ThingsGatewayConfigConst.Config_His_Enable:
|
||||
<MCol Class="pa-2 px-4" Md=12 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MSelect @bind-Value=@item.ConfigValue Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u.ToString())
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
|
||||
</MCol>
|
||||
break;
|
||||
default:
|
||||
<MCol Class="pa-2 px-4" Md=3 Cols="12">
|
||||
<MSubheader Class="mt-4 font-weight-black"> @item.Remark</MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@item.ConfigValue />
|
||||
</MCol>
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
</MRow>
|
||||
<MCardActions>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewayhisconfig")) Class="mt-8" OnClick=OnHisSaveAsync Color="primary">
|
||||
保存
|
||||
</MButton>
|
||||
</MCardActions>
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
|
||||
</MTabsItems>
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#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 BlazorComponent;
|
||||
|
||||
using Furion;
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
using ThingsGateway.Application;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ConfigPage
|
||||
{
|
||||
private List<SysConfig> _alarmConfig = new();
|
||||
private List<SysConfig> _hisConfig = new();
|
||||
StringNumber tab;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConfigPage()
|
||||
{
|
||||
AlarmHostService = ServiceHelper.GetBackgroundService<AlarmWorker>();
|
||||
HistoryValueHostService = ServiceHelper.GetBackgroundService<HistoryValueWorker>();
|
||||
}
|
||||
|
||||
AlarmWorker AlarmHostService { get; set; }
|
||||
|
||||
HistoryValueWorker HistoryValueHostService { get; set; }
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_alarmConfig = await App.GetService<IConfigService>().GetListByCategoryAsync(ThingsGatewayConfigConst.ThingGateway_AlarmConfig_Base);
|
||||
_hisConfig = await App.GetService<IConfigService>().GetListByCategoryAsync(ThingsGatewayConfigConst.ThingGateway_HisConfig_Base);
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
private async Task OnAlarmSaveAsync()
|
||||
{
|
||||
var confirm = await PopupService.OpenConfirmDialogAsync("确认", "保存配置后将重启报警服务,是否确定?");
|
||||
if (confirm)
|
||||
{
|
||||
await ConfigService.EditBatchAsync(_alarmConfig);
|
||||
await AlarmHostService.RestartAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnHisSaveAsync()
|
||||
{
|
||||
var confirm = await PopupService.OpenConfirmDialogAsync("确认", "保存配置后将重启历史服务,是否确定?");
|
||||
if (confirm)
|
||||
{
|
||||
await ConfigService.EditBatchAsync(_hisConfig);
|
||||
await HistoryValueHostService.RestartAsync();
|
||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,613 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/gatewayruntime/devicestatus"
|
||||
@namespace ThingsGateway.Blazor
|
||||
@using System.Linq.Expressions;
|
||||
@using BlazorComponent;
|
||||
@using Mapster;
|
||||
@using Masa.Blazor
|
||||
@using Masa.Blazor.Presets;
|
||||
@using System.IO;
|
||||
@using Microsoft.AspNetCore.Authorization;
|
||||
@using ThingsGateway.Admin.Blazor.Core;
|
||||
@using ThingsGateway.Admin.Blazor;
|
||||
@using ThingsGateway.Admin.Core;
|
||||
@using ThingsGateway.Application;
|
||||
@using TouchSocket.Core;
|
||||
@inject ICollectDeviceService CollectDeviceService
|
||||
@inject IUploadDeviceService UploadDeviceService
|
||||
|
||||
@attribute [Authorize]
|
||||
@inherits BaseComponentBase
|
||||
@inject UserResoures UserResoures
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IDriverPluginService DriverPluginService
|
||||
@layout MainLayout
|
||||
<MSheet Style="overflow:auto">
|
||||
|
||||
|
||||
<MTabs @bind-Value="tab">
|
||||
<MTab Style="height:50px;" Value="1">
|
||||
采集设备状态
|
||||
</MTab>
|
||||
<MTab Style="height:50px;" Value="2">
|
||||
上传设备状态
|
||||
</MTab>
|
||||
<MTab Style="height:50px;" Value="3">
|
||||
其他服务状态
|
||||
</MTab>
|
||||
<MButton Class="position-button" Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicerestart")) Color="red" Dark Fab Small Loading=isAllRestart OnClick="AllRestartAsync">
|
||||
<MIcon> mdi-restart </MIcon>
|
||||
</MButton>
|
||||
</MTabs>
|
||||
<MTabsItems Value="tab">
|
||||
<MTabItem Value="1">
|
||||
@if (tab == 1)
|
||||
{
|
||||
<MRow NoGutters>
|
||||
<MCol Md=2 Cols="12">
|
||||
|
||||
<MCard Class="ma-2" Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight + 80}px );")>
|
||||
<MCardTitle>
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_collectDeviceGroupSearchName"
|
||||
Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.DeviceGroup)) />
|
||||
</MCardTitle>
|
||||
|
||||
<MTreeview Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight + 240}px; overflow-y:auto)") Dense TItem="string" TKey="string" ActiveChanged=@(async a=>
|
||||
{
|
||||
if(_collectDeviceGroup!=a.FirstOrDefault())
|
||||
{
|
||||
_collectDeviceGroup=a.FirstOrDefault(); CollectDeviceQuery();
|
||||
}
|
||||
} )
|
||||
Items="_collectDeviceGroups" ItemText="r=>r" ItemChildren="r=>null"
|
||||
Search="@_collectDeviceGroupSearchName"
|
||||
Activatable ItemKey=@(r=>r)>
|
||||
<LabelContent>
|
||||
<span title=@context.Item>
|
||||
@context.Item
|
||||
</span>
|
||||
</LabelContent>
|
||||
</MTreeview>
|
||||
</MCard>
|
||||
</MCol>
|
||||
<MCol Md=3 Cols="12">
|
||||
<MCard Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight + 80}px; )") Style="overflow-y:auto;" Flat Class="ml-2 my-4">
|
||||
|
||||
<MVirtualScroll Context="item" Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+100}px)") OverscanCount=2 ItemSize="60" Items="_collectDeviceCores">
|
||||
|
||||
<ItemContent>
|
||||
@if (item.Device != null)
|
||||
{
|
||||
|
||||
<MListItem OnClick=@(()=>CollectDeviceInfo(item))>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>
|
||||
<MLabel Class=@((item.Device?.DeviceStatus==DeviceStatusEnum.OnLine?"green--text":"red--text")+$" text-h6")>
|
||||
<div class="mt-1 d-flex align-center justify-space-between" title=@item.Device?.Name>
|
||||
<span>@item.Device?.Name</span>
|
||||
<span style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;" class="text-caption">@(item.Device?.ActiveTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " " + typeof(DeviceStatusEnum).GetDescription(item.Device?.DeviceStatus.ToString()))</span>
|
||||
</div>
|
||||
</MLabel>
|
||||
</MListItemTitle>
|
||||
</MListItemContent>
|
||||
|
||||
</MListItem>
|
||||
|
||||
<MDivider></MDivider>
|
||||
|
||||
}
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
|
||||
|
||||
</MCard>
|
||||
|
||||
</MCol>
|
||||
<MCol Md=7 Cols="12">
|
||||
<MCard Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight + 80}px); overflow:auto)") Flat Elevation="0">
|
||||
@if (collectDeviceInfoItem != null && collectDeviceInfoItem?.Device != null)
|
||||
{
|
||||
var item = collectDeviceInfoItem;
|
||||
<MCard Style="height:100px;overflow:auto;" Flat Class="ml-4 my-4 ma-2" Elevation="0">
|
||||
|
||||
<MCardActions>
|
||||
<div class="mr-12"></div>
|
||||
|
||||
<MLabel Class=@((item.Device?.DeviceStatus==DeviceStatusEnum.OnLine?"green--text":"red--text")+$" text-h6")>
|
||||
<div class="mt-1 d-flex align-center justify-space-between">
|
||||
<span class="mx-3">@item.Device?.Name</span>
|
||||
<span style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;" class="text-caption mx-3">@(item.Device?.ActiveTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " " + typeof(DeviceStatusEnum).GetDescription(item.Device?.DeviceStatus.ToString()))</span>
|
||||
</div>
|
||||
</MLabel>
|
||||
|
||||
<MSpacer></MSpacer>
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasPageWithRole("/gatewayruntime/devicevariable")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small Loading=isRestart
|
||||
OnClick=@(()=>NavigationManager.NavigateTo("/gatewayruntime/devicevariable?devicename="+item.Device?.Name))>
|
||||
<MIcon>mdi-information-outline</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>相关变量</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small OnClick=@(()=>ConfigAsync(item.DeviceId,!item.Device?.KeepRun))>
|
||||
<MIcon>@(item.Device?.KeepRun == true ? "mdi-pause" : "mdi-play")</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>@(item.Device?.KeepRun == true ? "暂停" : "运行")</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
@* <MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicerestart")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small Loading=isRestart OnClick=@(()=>RestartAsync(item.DeviceId))>
|
||||
<MIcon>mdi-restart</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>重启</span>
|
||||
</ChildContent>
|
||||
</MTooltip>*@
|
||||
|
||||
</MCardActions>
|
||||
|
||||
|
||||
</MCard>
|
||||
|
||||
<MCard Style="height:200px;overflow:auto;" Flat Class="ml-4 my-4 ma-2" Elevation="0">
|
||||
|
||||
<MSubheader>
|
||||
运行状态
|
||||
</MSubheader>
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.ActiveTime)</span>
|
||||
<span class="text-caption">@item.Device?.ActiveTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)</span>
|
||||
</MCol>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.SourceVariableCount)</span>
|
||||
<span class="text-caption">@item.Device?.SourceVariableCount</span>
|
||||
</MCol>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.DeviceVariableCount)</span>
|
||||
<span class="text-caption">@item.Device?.DeviceVariableCount</span>
|
||||
</MCol>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.MethodVariableCount)</span>
|
||||
<span class="text-caption">@item.Device?.MethodVariableCount</span>
|
||||
</MCol>
|
||||
<MCol Md=12 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.LastErrorMessage)</span>
|
||||
<span class="text-caption red--text">@item.Device?.LastErrorMessage</span>
|
||||
</MCol>
|
||||
</MRow>
|
||||
|
||||
<MSubheader>
|
||||
配置信息
|
||||
</MSubheader>
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.PluginName)</span>
|
||||
<span class="text-caption">@item.Device?.PluginName</span>
|
||||
</MCol>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.IsLogOut)</span>
|
||||
<span class="text-caption">@item.Device?.IsLogOut</span>
|
||||
</MCol>
|
||||
@foreach (var property in item.Device?.DevicePropertys ?? new())
|
||||
{
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@property.Description</span>
|
||||
<span class="text-caption ">@(property.PropertyName.Contains("Password") ? "******" : @property.Value)</span>
|
||||
</MCol>
|
||||
}
|
||||
|
||||
</MRow>
|
||||
|
||||
|
||||
</MCard>
|
||||
|
||||
}
|
||||
|
||||
<MCard Flat Class="ml-4">
|
||||
@if (collectDeviceInfoItem != null && collectDeviceInfoItem?.Device != null)
|
||||
{
|
||||
<MCardActions>
|
||||
报文日志(共享链路的日志也会相同)
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(()=>
|
||||
{
|
||||
pauseMessage=!pauseMessage;
|
||||
if(pauseMessage)
|
||||
CurMessages= collectDeviceInfoItem.Driver?.Messages.ToList();
|
||||
}
|
||||
)>
|
||||
<MIcon>@((!pauseMessage) ? "mdi-pause" : "mdi-play")</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>@((!pauseMessage) ? "暂停日志" : "运行日志")</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
var curMessages= collectDeviceInfoItem.Driver?.Messages.ToList();
|
||||
await DownDeviceMessageExportAsync(curMessages);
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
{
|
||||
ICollection<string> item = null;
|
||||
if (pauseMessage)
|
||||
{
|
||||
item = CurMessages;
|
||||
}
|
||||
else if (collectDeviceInfoItem.Driver != null)
|
||||
{
|
||||
item = collectDeviceInfoItem.Driver?.Messages;
|
||||
}
|
||||
if (item == null)
|
||||
{
|
||||
item = new List<string>();
|
||||
}
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" TItem="string" Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+480}px)") OverscanCount=2 ItemSize="100" Items="item">
|
||||
<ItemContent>
|
||||
<div title=@itemMessage style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;">
|
||||
@itemMessage
|
||||
</div>
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
|
||||
}
|
||||
</MCard>
|
||||
|
||||
</MCard>
|
||||
</MCol>
|
||||
</MRow>
|
||||
}
|
||||
</MTabItem>
|
||||
<MTabItem Value="2">
|
||||
@if (tab == 2)
|
||||
{
|
||||
<MRow>
|
||||
<MCol Md=2 Cols="12">
|
||||
<MCard Class="ma-2" Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight + 80}px); )")>
|
||||
<MCardTitle>
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_uploadDeviceGroupSearchName"
|
||||
Outlined Label=@typeof(UploadDevice).GetDescription(nameof(UploadDevice.DeviceGroup)) />
|
||||
</MCardTitle>
|
||||
<MTreeview Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+240}px);overflow-y:auto") Dense TItem="string" TKey="string" ActiveChanged=@(async a=>
|
||||
{
|
||||
if(_uploadDeviceGroup!=a.FirstOrDefault())
|
||||
{
|
||||
_uploadDeviceGroup=a.FirstOrDefault(); UploadDeviceQuery();
|
||||
}
|
||||
} )
|
||||
Items="_uploadDeviceGroups" ItemText="r=>r" ItemChildren="r=>null"
|
||||
Search="@_uploadDeviceGroupSearchName"
|
||||
Activatable ItemKey=@(r=>r)>
|
||||
<LabelContent>
|
||||
<span title=@context.Item>
|
||||
@context.Item
|
||||
</span>
|
||||
</LabelContent>
|
||||
</MTreeview>
|
||||
</MCard>
|
||||
</MCol>
|
||||
<MCol Md=3 Cols="12">
|
||||
<MCard Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+80}px)") Style="overflow-y:auto;" Flat Class="ml-2 my-4">
|
||||
|
||||
<MVirtualScroll Context="item" Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+100}px)") OverscanCount=2 ItemSize="60" Items="_uploadDeviceCores">
|
||||
|
||||
<ItemContent>
|
||||
@if (item.Device != null)
|
||||
{
|
||||
|
||||
<MListItem OnClick=@(()=>UploadDeviceInfo(item))>
|
||||
<MListItemContent>
|
||||
<MListItemTitle>
|
||||
<MLabel Class=@((item.Device?.DeviceStatus==DeviceStatusEnum.OnLine?"green--text":"red--text")+$" text-h6")>
|
||||
<div class="mt-1 d-flex align-center justify-space-between" title=@item.Device?.Name>
|
||||
<span>@item.Device?.Name</span>
|
||||
<span style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;" class="text-caption">@(item.Device?.ActiveTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " " + typeof(DeviceStatusEnum).GetDescription(item.Device?.DeviceStatus.ToString()))</span>
|
||||
</div>
|
||||
</MLabel>
|
||||
</MListItemTitle>
|
||||
</MListItemContent>
|
||||
|
||||
</MListItem>
|
||||
|
||||
<MDivider></MDivider>
|
||||
|
||||
}
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
|
||||
|
||||
</MCard>
|
||||
|
||||
</MCol>
|
||||
<MCol Md=7 Cols="12">
|
||||
<MCard Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight + 80}px); overflow:auto)") Flat Elevation="0">
|
||||
@if (uploadDeviceInfoItem != null && uploadDeviceInfoItem?.Device != null)
|
||||
{
|
||||
var item = uploadDeviceInfoItem;
|
||||
<MCard Style="height:100px;overflow:auto;" Flat Class="ml-4 my-4 ma-2" Elevation="0">
|
||||
|
||||
<MCardActions>
|
||||
<div class="mr-12"></div>
|
||||
|
||||
<MLabel Class=@((item.Device?.DeviceStatus==DeviceStatusEnum.OnLine?"green--text":"red--text")+$" text-h6")>
|
||||
<div class="mt-1 d-flex align-center justify-space-between">
|
||||
<span class="mx-3">@item.Device?.Name</span>
|
||||
<span style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;" class="text-caption mx-3">@(item.Device?.ActiveTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " " + typeof(DeviceStatusEnum).GetDescription(item.Device?.DeviceStatus.ToString()))</span>
|
||||
</div>
|
||||
</MLabel>
|
||||
|
||||
<MSpacer></MSpacer>
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasPageWithRole("/gatewayruntime/devicevariable")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small Loading=isRestart
|
||||
OnClick=@(()=>NavigationManager.NavigateTo("/gatewayruntime/devicevariable?uploaddevicename="+item.Device?.Name))>
|
||||
<MIcon>mdi-information-outline</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>相关变量</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small OnClick=@(()=>UpConfigAsync(item.DeviceId,!item.Device?.KeepRun))>
|
||||
<MIcon>@(item.Device?.KeepRun == true ? "mdi-pause" : "mdi-play")</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>@(item.Device?.KeepRun == true ? "暂停" : "运行")</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicerestart")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small Loading=isRestart OnClick=@(()=> UpRestartAsync(item.DeviceId))>
|
||||
<MIcon>mdi-restart</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>重启</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
|
||||
|
||||
|
||||
|
||||
</MCard>
|
||||
<MCard Style="height:200px;overflow:auto;" Flat Class="ml-4 my-4 ma-2" Elevation="0">
|
||||
|
||||
|
||||
<MSubheader>
|
||||
运行状态
|
||||
</MSubheader>
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.ActiveTime)</span>
|
||||
<span class="text-caption">@item.Device?.ActiveTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)</span>
|
||||
</MCol>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.UploadVariableCount)</span>
|
||||
<span class="text-caption">@item.Device?.UploadVariableCount</span>
|
||||
</MCol>
|
||||
<MCol Md=12 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.LastErrorMessage)</span>
|
||||
<span class="text-caption red--text">@item.Device?.LastErrorMessage</span>
|
||||
</MCol>
|
||||
</MRow>
|
||||
|
||||
<MSubheader>
|
||||
配置信息
|
||||
</MSubheader>
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.PluginName)</span>
|
||||
<span class="text-caption">@item.Device?.PluginName</span>
|
||||
</MCol>
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@item.Device?.Description(a=>a.IsLogOut)</span>
|
||||
<span class="text-caption">@item.Device?.IsLogOut</span>
|
||||
</MCol>
|
||||
@foreach (var property in item.Device?.DevicePropertys ?? new())
|
||||
{
|
||||
<MCol Md=6 Cols="12" Class="px-4 mt-1 d-flex align-center justify-space-between">
|
||||
<span class="text-subtitle-2 grey--text">@property.Description</span>
|
||||
<span class="text-caption ">@(property.PropertyName.Contains("Password") ? "******" : @property.Value)</span>
|
||||
</MCol>
|
||||
}
|
||||
|
||||
</MRow>
|
||||
|
||||
|
||||
</MCard>
|
||||
}
|
||||
|
||||
<MCard Flat Class="ml-4">
|
||||
@if (uploadDeviceInfoItem != null && uploadDeviceInfoItem?.Device != null)
|
||||
{
|
||||
<MCardActions>
|
||||
报文日志(共享链路的日志也会相同)
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(()=>
|
||||
{
|
||||
pauseMessage=!pauseMessage;
|
||||
if(pauseMessage)
|
||||
CurMessages= uploadDeviceInfoItem.Driver?.Messages.ToList();
|
||||
}
|
||||
)>
|
||||
<MIcon>@((!pauseMessage) ? "mdi-pause" : "mdi-play")</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>@((!pauseMessage) ? "暂停日志" : "运行日志")</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
var curMessages= uploadDeviceInfoItem.Driver?.Messages.ToList();
|
||||
await DownDeviceMessageExportAsync(curMessages);
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
{
|
||||
ICollection<string> item = null;
|
||||
if (pauseMessage)
|
||||
{
|
||||
item = CurMessages;
|
||||
}
|
||||
else if (uploadDeviceInfoItem.Driver != null)
|
||||
{
|
||||
item = uploadDeviceInfoItem.Driver?.Messages ?? new();
|
||||
}
|
||||
if (item == null)
|
||||
{
|
||||
item = new List<string>();
|
||||
}
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" TItem="string" Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+480}px)") OverscanCount=2 ItemSize="100" Items="item">
|
||||
<ItemContent>
|
||||
<div title=@itemMessage style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;">
|
||||
@itemMessage
|
||||
</div>
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
|
||||
}
|
||||
</MCard>
|
||||
|
||||
</MCard>
|
||||
</MCol>
|
||||
</MRow>
|
||||
}
|
||||
</MTabItem>
|
||||
|
||||
<MTabItem Value="3">
|
||||
@if (tab == 3)
|
||||
{
|
||||
<MRow NoGutters>
|
||||
<MCard Class="ml-2 my-3" Style="width:100%" Elevation="1">
|
||||
<MCardSubtitle Class=@((AlarmHostService.StatuString.IsSuccess?"green--text":"red--text")+$" text-subtitle-2")>
|
||||
<div class="mt-1 d-flex align-center justify-space-between">
|
||||
<span>历史报警服务状态</span>
|
||||
<span class="text-caption">@AlarmHostService.StatuString.Message</span>
|
||||
</div>
|
||||
</MCardSubtitle>
|
||||
</MCard>
|
||||
</MRow>
|
||||
<MRow NoGutters>
|
||||
<MCard Class="ml-2 my-3" Style="width:100%" Elevation="1">
|
||||
<MCardSubtitle Class=@((HistoryValueHostService.StatuString.IsSuccess?"green--text":"red--text")+$" text-subtitle-2")>
|
||||
<div class="mt-1 d-flex align-center justify-space-between">
|
||||
<span>历史数据服务状态</span>
|
||||
<span class="text-caption">@HistoryValueHostService.StatuString.Message</span>
|
||||
</div>
|
||||
</MCardSubtitle>
|
||||
</MCard>
|
||||
</MRow>
|
||||
<MRow NoGutters>
|
||||
<MCard Class="ml-2 my-3" Style="width:100%" Elevation="1">
|
||||
<MCardSubtitle Class=@((MemoryVariableWorker.StatuString.IsSuccess?"green--text":"red--text")+$" text-subtitle-2")>
|
||||
<div class="mt-1 d-flex align-center justify-space-between">
|
||||
<span>中间变量计算服务状态</span>
|
||||
<span class="text-caption">@MemoryVariableWorker.StatuString.Message</span>
|
||||
</div>
|
||||
</MCardSubtitle>
|
||||
</MCard>
|
||||
</MRow>
|
||||
|
||||
}
|
||||
|
||||
</MTabItem>
|
||||
</MTabsItems>
|
||||
|
||||
|
||||
|
||||
|
||||
</MSheet>
|
||||
<style>
|
||||
.position-button {
|
||||
position: fixed !important;
|
||||
top: 10%;
|
||||
right: 0;
|
||||
box-shadow: 1px 1px 8px var(--mud-palette-primary);
|
||||
background-color: var(--mud-palette-primary);
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using BlazorComponent;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
using ThingsGateway.Admin.Blazor;
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Admin.Core;
|
||||
using ThingsGateway.Application;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
|
||||
/// <summary>
|
||||
/// <20>豸״̬ҳ<CCAC><D2B3>
|
||||
/// </summary>
|
||||
public partial class DeviceStatusPage : IDisposable
|
||||
{
|
||||
readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1));
|
||||
List<CollectDeviceCore> _collectDeviceCores = new();
|
||||
private string _collectDeviceGroup;
|
||||
List<string> _collectDeviceGroups = new();
|
||||
string _collectDeviceGroupSearchName;
|
||||
List<UploadDeviceCore> _uploadDeviceCores = new();
|
||||
private string _uploadDeviceGroup;
|
||||
List<string> _uploadDeviceGroups = new();
|
||||
string _uploadDeviceGroupSearchName;
|
||||
CollectDeviceCore collectDeviceInfoItem;
|
||||
List<string> CurMessages = new();
|
||||
bool isAllRestart;
|
||||
private bool isDownExport;
|
||||
bool isRestart;
|
||||
bool pauseMessage;
|
||||
StringNumber tab;
|
||||
UploadDeviceCore uploadDeviceInfoItem;
|
||||
AlarmWorker AlarmHostService { get; set; }
|
||||
CollectDeviceWorker CollectDeviceHostService { get; set; }
|
||||
[Inject]
|
||||
GlobalDeviceData GlobalDeviceData { get; set; }
|
||||
|
||||
IJSObjectReference Helper { get; set; }
|
||||
HistoryValueWorker HistoryValueHostService { get; set; }
|
||||
|
||||
[Inject]
|
||||
InitTimezone InitTimezone { get; set; }
|
||||
[Inject]
|
||||
IJSRuntime JS { get; set; }
|
||||
[CascadingParameter]
|
||||
MainLayout MainLayout { get; set; }
|
||||
|
||||
MemoryVariableWorker MemoryVariableWorker { get; set; }
|
||||
StringNumber Panel { get; set; }
|
||||
|
||||
UploadDeviceWorker UploadDeviceHostService { get; set; }
|
||||
StringNumber Uppanel { get; set; }
|
||||
[Inject]
|
||||
IVariableService VariableService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_periodicTimer?.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
CollectDeviceHostService = ServiceHelper.GetBackgroundService<CollectDeviceWorker>();
|
||||
UploadDeviceHostService = ServiceHelper.GetBackgroundService<UploadDeviceWorker>();
|
||||
AlarmHostService = ServiceHelper.GetBackgroundService<AlarmWorker>();
|
||||
HistoryValueHostService = ServiceHelper.GetBackgroundService<HistoryValueWorker>();
|
||||
MemoryVariableWorker = ServiceHelper.GetBackgroundService<MemoryVariableWorker>();
|
||||
|
||||
_ = RunTimerAsync();
|
||||
base.OnInitialized();
|
||||
}
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
CollectDeviceQuery();
|
||||
UploadDeviceQuery();
|
||||
base.OnParametersSet();
|
||||
}
|
||||
async Task AllRestartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var confirm = await PopupService.OpenConfirmDialogAsync("<22><><EFBFBD><EFBFBD>", "ȷ<><C8B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?");
|
||||
if (confirm)
|
||||
{
|
||||
isAllRestart = true;
|
||||
StateHasChanged();
|
||||
PopupService.ShowProgressLinear();
|
||||
await Task.Run(async () => await CollectDeviceHostService.RestartDeviceThreadAsync());
|
||||
CollectDeviceQuery();
|
||||
UploadDeviceQuery();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
collectDeviceInfoItem = null;
|
||||
uploadDeviceInfoItem = null;
|
||||
isAllRestart = false;
|
||||
PopupService.HideProgressLinear();
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
}
|
||||
void CollectDeviceInfo(CollectDeviceCore item)
|
||||
{
|
||||
collectDeviceInfoItem = item;
|
||||
CurMessages = new();
|
||||
}
|
||||
void CollectDeviceQuery()
|
||||
{
|
||||
_collectDeviceGroups = GlobalDeviceData.CollectDevices.Adapt<List<CollectDevice>>()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList() ?? new();
|
||||
_collectDeviceCores = CollectDeviceHostService?.CollectDeviceCores?.WhereIF(!_collectDeviceGroup.IsNullOrEmpty(), a => a.Device?.DeviceGroup == _collectDeviceGroup).ToList() ?? new();
|
||||
}
|
||||
async Task ConfigAsync(long devId, bool? isStart)
|
||||
{
|
||||
var str = isStart == true ? "<22><><EFBFBD><EFBFBD>" : "<22><>ͣ";
|
||||
var confirm = await PopupService.OpenConfirmDialogAsync(str, $"ȷ<><C8B7>{str}?");
|
||||
if (confirm)
|
||||
{
|
||||
CollectDeviceHostService.ConfigDeviceThread(devId, isStart == true);
|
||||
}
|
||||
}
|
||||
|
||||
async Task DownDeviceMessageExportAsync(List<string> values)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
using var memoryStream = new MemoryStream();
|
||||
StreamWriter writer = new(memoryStream);
|
||||
foreach (var item in values)
|
||||
{
|
||||
writer.WriteLine(item);
|
||||
}
|
||||
|
||||
writer.Flush();
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
Helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
|
||||
await Helper.InvokeVoidAsync("downloadFileFromStream", $"<22><><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.txt", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
//ȥ<><C8A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD><C9BC><EFBFBD><EFBFBD><EFBFBD>
|
||||
//async Task RestartAsync(long devId)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// var confirm = await PopupService.OpenConfirmDialogAsync("<22><><EFBFBD><EFBFBD>", "ȷ<><C8B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?");
|
||||
// if (confirm)
|
||||
// {
|
||||
// isRestart = true;
|
||||
// StateHasChanged();
|
||||
// await Task.Run(async () => await CollectDeviceHostService.UpDeviceThreadAsync(devId));
|
||||
// collectDeviceInfoItem = null;
|
||||
// CollectDeviceQuery();
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// await PopupService.EnqueueSnackbarAsync(ex.Message, AlertTypes.Warning);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// isRestart = false;
|
||||
// await MainLayout.StateHasChangedAsync();
|
||||
// }
|
||||
//}
|
||||
private async Task RunTimerAsync()
|
||||
{
|
||||
while (await _periodicTimer.WaitForNextTickAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
_collectDeviceGroups = GlobalDeviceData.CollectDevices.Adapt<List<CollectDevice>>()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList() ?? new();
|
||||
_collectDeviceCores = CollectDeviceHostService?.CollectDeviceCores?.WhereIF(!_collectDeviceGroup.IsNullOrEmpty(), a => a.Device?.DeviceGroup == _collectDeviceGroup).ToList() ?? new();
|
||||
}
|
||||
if (_collectDeviceCores?.FirstOrDefault()?.Device == null || CollectDeviceHostService?.CollectDeviceCores.Count != _collectDeviceCores.Count)
|
||||
{
|
||||
CollectDeviceQuery();
|
||||
}
|
||||
|
||||
if (_uploadDeviceCores?.FirstOrDefault()?.Device == null || UploadDeviceHostService?.UploadDeviceCores.Count != _uploadDeviceCores.Count)
|
||||
{
|
||||
UploadDeviceQuery();
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async Task UpConfigAsync(long devId, bool? isStart)
|
||||
{
|
||||
var str = isStart == true ? "<22><><EFBFBD><EFBFBD>" : "<22><>ͣ";
|
||||
var confirm = await PopupService.OpenConfirmDialogAsync(str, $"ȷ<><C8B7>{str}?");
|
||||
if (confirm)
|
||||
{
|
||||
UploadDeviceHostService.ConfigDeviceThread(devId, isStart == true);
|
||||
}
|
||||
}
|
||||
|
||||
void UploadDeviceInfo(UploadDeviceCore item)
|
||||
{
|
||||
uploadDeviceInfoItem = item;
|
||||
}
|
||||
|
||||
void UploadDeviceQuery()
|
||||
{
|
||||
_uploadDeviceGroups = UploadDeviceHostService.UploadDeviceCores.Select(a => a.Device)?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList() ?? new();
|
||||
_uploadDeviceCores = UploadDeviceHostService?.UploadDeviceCores?.WhereIF(!_uploadDeviceGroup.IsNullOrEmpty(), a => a.Device?.DeviceGroup == _uploadDeviceGroup).ToList() ?? new();
|
||||
}
|
||||
async Task UpRestartAsync(long devId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var confirm = await PopupService.OpenConfirmDialogAsync("<22><><EFBFBD><EFBFBD>", "ȷ<><C8B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?");
|
||||
if (confirm)
|
||||
{
|
||||
isRestart = true;
|
||||
StateHasChanged();
|
||||
await Task.Run(async () => await UploadDeviceHostService.UpDeviceThreadAsync(devId));
|
||||
uploadDeviceInfoItem = null;
|
||||
UploadDeviceQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync(ex.Message, AlertTypes.Warning);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isRestart = false;
|
||||
await MainLayout.StateHasChangedAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,663 +0,0 @@
|
||||
@*
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
*@
|
||||
|
||||
@page "/gatewayconfig/devicevariable"
|
||||
@namespace ThingsGateway.Blazor
|
||||
@using System.Linq.Expressions;
|
||||
@using BlazorComponent;
|
||||
@using Furion.DataValidation;
|
||||
@using Furion;
|
||||
@using Mapster;
|
||||
@using Masa.Blazor
|
||||
@using Masa.Blazor.Presets;
|
||||
@using System.IO;
|
||||
@using Microsoft.AspNetCore.Authorization;
|
||||
@using ThingsGateway.Admin.Blazor.Core;
|
||||
@using ThingsGateway.Admin.Blazor;
|
||||
@using ThingsGateway.Admin.Core;
|
||||
@using ThingsGateway.Application;
|
||||
@attribute [Authorize]
|
||||
@inherits BaseComponentBase
|
||||
@inject UserResoures UserResoures
|
||||
@layout MainLayout
|
||||
|
||||
@if (IsMobile)
|
||||
{
|
||||
@GetAppDataTable()
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
<MRow>
|
||||
<MCol Md=2 Cols="12">
|
||||
<MCard Class="ma-2" Height=@("100%")>
|
||||
<MCardTitle>
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_searchName"
|
||||
Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.DeviceGroup)) />
|
||||
</MCardTitle>
|
||||
<MTreeview Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+100}px);overflow-y:auto") Dense TItem="DeviceTree" TKey="string" OpenOnClick ActiveChanged=@(async a=>
|
||||
{
|
||||
if(search.DeviceName!=a.FirstOrDefault())
|
||||
{
|
||||
search.DeviceName=a.FirstOrDefault();
|
||||
await DatatableQueryAsync();
|
||||
}
|
||||
} )
|
||||
Items="_deviceGroups" ItemText="r=>r.Name" ItemChildren="r=>r.Childrens"
|
||||
Search="@_searchName"
|
||||
Activatable ItemKey=@(r=>r.Name)>
|
||||
<LabelContent>
|
||||
<span title=@context.Item.Name>
|
||||
@context.Item.Name
|
||||
</span>
|
||||
</LabelContent>
|
||||
</MTreeview>
|
||||
</MCard>
|
||||
</MCol>
|
||||
<MCol Md=10 Cols="12">
|
||||
|
||||
@GetAppDataTable()
|
||||
|
||||
</MCol>
|
||||
</MRow>
|
||||
}
|
||||
|
||||
<ImportExcel @ref=ImportExcel Import="SaveDeviceImportAsync" Preview="DeviceImportAsync" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@code {
|
||||
RenderFragment GetAppDataTable()
|
||||
{
|
||||
RenderFragment renderFragment =
|
||||
@<AppDataTable @ref="_datatable"
|
||||
StyleString=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+10}px)")
|
||||
TItem="DeviceVariable" SearchItem="DeviceVariablePageInput"
|
||||
AddItem="DeviceVariableAddInput" EditItem="VariableEditInput"
|
||||
IsMenuOperTemplate=false SearchModel="search"
|
||||
QueryCallAsync="QueryCallAsync" AddCallAsync="AddCallAsync"
|
||||
EditCallAsync="EditCallAsync" DeleteCallAsync="DeleteCallAsync"
|
||||
IsShowDetailButton
|
||||
IsShowQueryButton
|
||||
IsShowAddButton=@UserResoures.IsHasButtonWithRole("gatewayvariableadd")
|
||||
IsShowDeleteButton=@UserResoures.IsHasButtonWithRole("gatewayvariabledelete")
|
||||
IsShowEditButton=@UserResoures.IsHasButtonWithRole("gatewayvariableedit")>
|
||||
<SearchTemplate>
|
||||
<MTextField Dense
|
||||
Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.Name"
|
||||
Clearable
|
||||
Outlined
|
||||
Label=@context.Description(x => x.Name) />
|
||||
<MTextField Dense
|
||||
Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.VariableAddress"
|
||||
Clearable
|
||||
Outlined
|
||||
Label=@context.Description(x => x.VariableAddress) />
|
||||
<MTextField Dense
|
||||
Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.DeviceName"
|
||||
Clearable
|
||||
Outlined
|
||||
Label=@context.Description(x => x.DeviceName) />
|
||||
<MTextField Dense
|
||||
Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 " @bind-Value="context.UploadDeviceName"
|
||||
Clearable
|
||||
Outlined
|
||||
Label=@context.Description(x => x.UploadDeviceName) />
|
||||
</SearchTemplate>
|
||||
<OtherToolbarTemplate>
|
||||
|
||||
<MMenu OffsetY
|
||||
Context="menu">
|
||||
<ActivatorContent>
|
||||
<MButton @attributes="@menu.Attrs" Color="primary"
|
||||
Class="my-1 mx-2 ">
|
||||
导出
|
||||
<AppChevronDown></AppChevronDown>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MList>
|
||||
<MListItem OnClick="()=>DownExportAsync()"> 导出全部 </MListItem>
|
||||
<MListItem OnClick="()=>DownExportAsync(search)"> 导出搜索项 </MListItem>
|
||||
</MList>
|
||||
|
||||
</ChildContent>
|
||||
</MMenu>
|
||||
|
||||
|
||||
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewayvariableedit")) Class="my-1 mx-2" OnClick="()=>{ ImportExcel.Step=1; ImportExcel.IsShowImport=true;}" Color="primary">
|
||||
导入
|
||||
</MButton>
|
||||
<MButton Disabled=@(!UserResoures.IsHasButtonWithRole("gatewayvariableedit")) Class="my-1 mx-2" OnClick=ClearAsync Color="primary">
|
||||
清空
|
||||
</MButton>
|
||||
</OtherToolbarTemplate>
|
||||
<AddTemplate>
|
||||
@{
|
||||
var data = CollectDevices.FirstOrDefault();
|
||||
context.DeviceId = context.DeviceId == 0 ? data == null ? 0 : data.Id : context.DeviceId;
|
||||
}
|
||||
@GetRenderFragment(context)
|
||||
</AddTemplate>
|
||||
<EditTemplate>
|
||||
@GetRenderFragment(context)
|
||||
</EditTemplate>
|
||||
<ItemColTemplate>
|
||||
@switch (context.Header.Value)
|
||||
{
|
||||
case nameof(context.Item.DeviceId):
|
||||
<span title=@context.Value>
|
||||
@(
|
||||
App.GetService<ICollectDeviceService>().GetNameById(context.Item.DeviceId)
|
||||
)
|
||||
</span>
|
||||
break;
|
||||
default:
|
||||
@if (context.Header.CellClass?.Contains("text-truncate") == true)
|
||||
{
|
||||
<span title=@context.Value>
|
||||
@context.Value
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@context.Value
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
</ItemColTemplate>
|
||||
|
||||
<Detailemplate>
|
||||
@{
|
||||
switch (context.Item1.Value)
|
||||
{
|
||||
case nameof(DeviceVariable.DeviceId):
|
||||
<tr @key="context.Item1.Text">
|
||||
<td class="text-start table-minwidth">
|
||||
@context.Item1.Text
|
||||
</td>
|
||||
<td class="text-start ">
|
||||
<div style="word-break:break-all; white-space:pre-wrap;">
|
||||
@(App.GetService<ICollectDeviceService>().GetNameById(context.Item2.ToLong()))
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
break;
|
||||
default:
|
||||
<tr @key="context.Item1.Text">
|
||||
<td class="text-start table-minwidth">
|
||||
@context.Item1.Text
|
||||
</td>
|
||||
<td class="text-start ">
|
||||
<div style="word-break:break-all; white-space:pre-wrap;">
|
||||
@context.Item2
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
</Detailemplate>
|
||||
</AppDataTable>
|
||||
;
|
||||
return renderFragment;
|
||||
}
|
||||
|
||||
RenderFragment GetRenderFragment(VariableEditInput context)
|
||||
{
|
||||
if (!OtherMethods.ContainsKey(context.DeviceId))
|
||||
{
|
||||
DeviceChanged(context.DeviceId);
|
||||
}
|
||||
|
||||
RenderFragment renderFragment =
|
||||
@<div>
|
||||
<MTabs @bind-Value="tab">
|
||||
<MTab Value=1 Style="height:50px;"> 基本属性 </MTab>
|
||||
<MTab Value=2 Style="height:50px;"> 报警属性 </MTab>
|
||||
<MTab Value=3 Style="height:50px;"> 历史属性 </MTab>
|
||||
<MTab Value=4 Style="height:50px;"> 上传属性 </MTab>
|
||||
</MTabs>
|
||||
|
||||
<MTabsItems Value="tab">
|
||||
<MTabItem Value="1">
|
||||
@if (tab == 1)
|
||||
{
|
||||
|
||||
<MCard Flat Class="ma-2">
|
||||
<MRow NoGutters Class="my-2" Justify="JustifyTypes.Start">
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.Name)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Name />
|
||||
</MCol>
|
||||
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.Description)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Description />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @context.Description(x=>x.DeviceId) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.DeviceId Outlined
|
||||
Items=@(CollectDevices) OnClick=@(()=>DeviceChanged(context.DeviceId))
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Name) ItemValue=@(u =>u.Id)
|
||||
ItemDisabled="u => !u.Enable"
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.IntervalTime)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.IntervalTime />
|
||||
</MCol>
|
||||
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.RpcWriteEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.RpcWriteEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.Unit)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Unit />
|
||||
</MCol>
|
||||
|
||||
|
||||
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.VariableAddress)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.VariableAddress />
|
||||
</MCol>
|
||||
|
||||
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.ProtectTypeEnum)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value="context.ProtectTypeEnum" Outlined
|
||||
Items=@(typeof(ProtectTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(ProtectTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.DataTypeEnum)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value="context.DataTypeEnum" Outlined
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList()) Clearable
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.ReadExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.ReadExpressions />
|
||||
</MCol>
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.WriteExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.WriteExpressions />
|
||||
</MCol>
|
||||
|
||||
|
||||
|
||||
|
||||
@if (OtherMethods.ContainsKey(context.DeviceId))
|
||||
{
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.OtherMethod)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value="context.OtherMethod" Outlined
|
||||
Items=@(OtherMethods[context.DeviceId]) Clearable
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u)
|
||||
ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
}
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
|
||||
<MTabItem Value="2">
|
||||
@if (tab == 2)
|
||||
{
|
||||
<MCard Flat Class="ma-2">
|
||||
<MRow NoGutters Class="my-2" Justify="JustifyTypes.Center">
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1 py-2">
|
||||
<MDivider Center Height="2"></MDivider>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.BoolCloseAlarmEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.BoolCloseAlarmEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.BoolCloseAlarmText)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.BoolCloseAlarmText />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.BoolCloseRestrainExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.BoolCloseRestrainExpressions />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1 py-2">
|
||||
<MDivider Center Height="2"></MDivider>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.BoolOpenAlarmEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.BoolOpenAlarmEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.BoolOpenAlarmText)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.BoolOpenAlarmText />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.BoolOpenRestrainExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.BoolOpenRestrainExpressions />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1 py-2">
|
||||
<MDivider Center Height="2"></MDivider>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HHAlarmEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.HHAlarmEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HHAlarmText)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.HHAlarmText />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HHAlarmCode)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.HHAlarmCode />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HHRestrainExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.HHRestrainExpressions />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1 py-2">
|
||||
<MDivider Center Height="2"></MDivider>
|
||||
</MCol>
|
||||
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HAlarmEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.HAlarmEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HAlarmText)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.HAlarmText />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HAlarmCode)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.HAlarmCode />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1 py-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HRestrainExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.HRestrainExpressions />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1 py-2">
|
||||
<MDivider Center Height="2"></MDivider>
|
||||
</MCol>
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LAlarmEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.LAlarmEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LAlarmText)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.LAlarmText />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LAlarmCode)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.LAlarmCode />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LRestrainExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.LRestrainExpressions />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1 py-2">
|
||||
<MDivider Center Height="2"></MDivider>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LLAlarmEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.LLAlarmEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LLAlarmText)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.LLAlarmText />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LLAlarmCode)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.LLAlarmCode />
|
||||
</MCol>
|
||||
|
||||
<MCol Md=6 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.LLRestrainExpressions)) </MSubheader>
|
||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.LLRestrainExpressions />
|
||||
</MCol>
|
||||
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
|
||||
|
||||
<MTabItem Value="3">
|
||||
@if (tab == 3)
|
||||
{
|
||||
<MCard Flat Class="ma-2">
|
||||
|
||||
<MRow NoGutters Class="my-2" Justify="JustifyTypes.Center">
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HisEnable)) </MSubheader>
|
||||
<MSelect Class="mr-3" @bind-Value=@context.HisEnable Outlined
|
||||
Items=@(new List<bool>(){true,false}) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.ToString()) ItemValue=@(u =>u)
|
||||
HideDetails=@("auto") Height="30" Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
<MCol Md=12 Cols=12 class="px-1">
|
||||
<MSubheader Class="font-weight-black"> @(context.Description(x => x.HisType)) </MSubheader>
|
||||
|
||||
<MSelect Class="mr-3" @bind-Value=@context.HisType Outlined
|
||||
Items=@(typeof(HisType).GetEnumList()) Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(HisType)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
<MTabItem Value="4">
|
||||
@if (tab == 4)
|
||||
{
|
||||
<MCard Flat Class="pa-2">
|
||||
<MRow Class="px-1 py-6" Align="AlignTypes.Center">
|
||||
|
||||
<MSelect Class="mr-3" @bind-Value=@choiceUploadDeviceId
|
||||
Outlined
|
||||
Items=@(UploadDevices)
|
||||
Clearable
|
||||
MenuProps="@(props => { props.Bottom = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.Name) ItemValue=@(u =>u.Id)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
<MButton Class="my-3" OnClick=@(async() =>
|
||||
{
|
||||
if(choiceUploadDeviceId>0)
|
||||
{
|
||||
var data=GetDriverProperties(UploadDevices.FirstOrDefault(a=>a.Id==choiceUploadDeviceId).PluginId,context.VariablePropertys.ContainsKey(choiceUploadDeviceId)?context.VariablePropertys[choiceUploadDeviceId]:null);
|
||||
if(data?.Count>0)
|
||||
{
|
||||
context.VariablePropertys.AddOrUpdate(choiceUploadDeviceId,a=> data,(a,b)=>data);
|
||||
}
|
||||
else
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("此上传设备没有变量上传属性",AlertTypes.Warning);
|
||||
context.VariablePropertys.Remove(choiceUploadDeviceId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("需选择上传设备",AlertTypes.Warning);
|
||||
}
|
||||
}
|
||||
) Color="primary">
|
||||
添加/刷新属性
|
||||
</MButton>
|
||||
|
||||
</MRow>
|
||||
@if (context.VariablePropertys != null)
|
||||
{
|
||||
@foreach (var item in context.VariablePropertys)
|
||||
{
|
||||
<MCard Class="pa-2 my-3">
|
||||
|
||||
<MSubheader Class="mt-4 font-weight-black">
|
||||
@(
|
||||
UploadDevices.FirstOrDefault(a => a.Id == item.Key)?.Name ?? "未知"
|
||||
)
|
||||
</MSubheader>
|
||||
@foreach (var property in item.Value ?? new())
|
||||
{
|
||||
<MSubheader Class="mt-4 font-weight-black"> @property.Description </MSubheader>
|
||||
<MTooltip Disabled=string.IsNullOrEmpty(property.Remark)
|
||||
Bottom
|
||||
Context="tip">
|
||||
<ActivatorContent>
|
||||
<MTextField @attributes="@tip.Attrs"
|
||||
Dense
|
||||
Outlined
|
||||
HideDetails="@("auto")" @bind-Value=@property.Value />
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>@property.Remark</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
}
|
||||
<MCardActions>
|
||||
<MButton Class="my-3" OnClick=@(() =>
|
||||
{
|
||||
context.VariablePropertys.Remove(item.Key);
|
||||
}
|
||||
) Color="primary">
|
||||
删除
|
||||
</MButton>
|
||||
</MCardActions>
|
||||
</MCard>
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</MCard>
|
||||
}
|
||||
</MTabItem>
|
||||
</MTabsItems>
|
||||
|
||||
</div>
|
||||
;
|
||||
return renderFragment;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#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 BlazorComponent;
|
||||
|
||||
using Masa.Blazor;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using ThingsGateway.Admin.Blazor.Core;
|
||||
using ThingsGateway.Application;
|
||||
|
||||
namespace ThingsGateway.Blazor;
|
||||
/// <summary>
|
||||
/// 调试页面
|
||||
/// </summary>
|
||||
public partial class DriverDebugPage
|
||||
{
|
||||
readonly List<DeviceTree> _deviceGroups = new();
|
||||
private BootstrapDynamicComponent _importComponent;
|
||||
private object _importRef;
|
||||
private RenderFragment _importRender;
|
||||
string _searchName;
|
||||
List<DriverPluginCategory> DriverPlugins;
|
||||
bool IsShowTreeView = true;
|
||||
PluginDebugUIInput SearchModel { get; set; } = new();
|
||||
[Inject]
|
||||
IDriverPluginService DriverPluginService { get; set; }
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
DriverPlugins = DriverPluginService.GetDriverPluginChildrenList();
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
async Task ImportVaiableAsync(long driverId)
|
||||
{
|
||||
var driver = ServiceHelper.GetBackgroundService<CollectDeviceWorker>().GetDebugUI(driverId);
|
||||
if (driver == null)
|
||||
{
|
||||
await PopupService.EnqueueSnackbarAsync("插件未实现调试页面", AlertTypes.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
_importComponent = new BootstrapDynamicComponent(driver);
|
||||
_importRender = _importComponent.Render(a => _importRef = a);
|
||||
}
|
||||
|
||||
class PluginDebugUIInput
|
||||
{
|
||||
public long PluginId { get; set; }
|
||||
public string PluginName { get; set; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user